@@ -254,25 +254,65 @@ protected override CompletionItem CreateItem(
254254 item = SymbolCompletionItem . AddShouldProvideParenthesisCompletion ( item ) ;
255255 }
256256 }
257- else if ( symbol . IsKind ( SymbolKind . NamedType ) || symbol is IAliasSymbol aliasSymbol && aliasSymbol . Target . IsType )
257+ else if ( context . IsObjectCreationTypeContext )
258258 {
259259 // If this is a type symbol/alias symbol, also consider appending parenthesis when later, it is committed by using special characters,
260260 // and the type is used as constructor
261- if ( context . IsObjectCreationTypeContext )
261+ var typeSymbol = symbol as INamedTypeSymbol ?? ( symbol as IAliasSymbol ) ? . Target as INamedTypeSymbol ;
262+ if ( typeSymbol is not null )
263+ {
262264 item = SymbolCompletionItem . AddShouldProvideParenthesisCompletion ( item ) ;
265+
266+ // When committing with `.`, we don't want to automatically appending parenthesis for types
267+ // that has accessible nested types (user might want to access nested types like Bar.Foo)
268+ if ( HasAccessibleNestedTypes ( typeSymbol ) )
269+ {
270+ item = SymbolCompletionItem . AddHasAccessibleNestedTypes ( item ) ;
271+ }
272+ }
263273 }
264274
265275 return item ;
276+
277+ bool HasAccessibleNestedTypes ( INamedTypeSymbol typeSymbol )
278+ {
279+ // This is intended to be a heuristic to cover common cases.
280+ // Because we might be passed a speculative member semantic model, we won't be able to get
281+ // symbols for containing type/member symbol to check accessibility using `IsAccessibleWithin`.
282+ // While this check is inaccurate in some cases (e.g. from within nested/derived types),
283+ // it's likely fine because people typically don't access the nested types via the outer one
284+ // when they are within the inner or derived type.
285+ foreach ( var typeMember in typeSymbol . GetTypeMembers ( ) )
286+ {
287+ if ( typeMember . DeclaredAccessibility <= Accessibility . Protected )
288+ continue ;
289+
290+ if ( typeMember . DeclaredAccessibility is Accessibility . Public )
291+ return true ;
292+
293+ if ( typeMember . IsAccessibleWithin ( context . SemanticModel . Compilation . Assembly ) )
294+ return true ;
295+ }
296+
297+ return false ;
298+ }
266299 }
267300
268301 protected override string GetInsertionText ( CompletionItem item , char ch )
269302 {
270- if ( ch is ';' or '.' && SymbolCompletionItem . GetShouldProvideParenthesisCompletion ( item ) )
303+ if ( SymbolCompletionItem . GetShouldProvideParenthesisCompletion ( item ) )
271304 {
272- CompletionProvidersLogger . LogCustomizedCommitToAddParenthesis ( ch ) ;
273- return SymbolCompletionItem . GetInsertionText ( item ) + "()" ;
305+ // For '.', don't add parentheses if the type has accessible nested types
306+ // (user might want to access nested types like Bar.Foo)
307+ if ( ch is ';' ||
308+ ch is '.' && ! SymbolCompletionItem . GetHasAccessibleNestedTypes ( item ) )
309+ {
310+ CompletionProvidersLogger . LogCustomizedCommitToAddParenthesis ( ch ) ;
311+ return SymbolCompletionItem . GetInsertionText ( item ) + "()" ;
312+ }
274313 }
275314
276315 return base . GetInsertionText ( item , ch ) ;
277316 }
278317}
318+
0 commit comments