private static async Task HandleSingleTypeAsync(CompletionContext context, SemanticModel semanticModel, SyntaxToken token, ITypeSymbol type, CancellationToken cancellationToken) { // If we have a Nullable<T>, unwrap it. if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { var typeArg = type.GetTypeArguments().FirstOrDefault(); if (typeArg == null) { return; } type = typeArg; } // When true, this completion provider shows both the type (e.g. DayOfWeek) and its qualified members (e.g. // DayOfWeek.Friday). We set this to false for enum-like cases (static members of structs and classes) so we // only show the qualified members in these cases. var showType = true; var position = context.Position; var enclosingNamedType = semanticModel.GetEnclosingNamedType(position, cancellationToken); if (type.TypeKind != TypeKind.Enum) { var enumType = TryGetEnumTypeInEnumInitializer(semanticModel, token, type, cancellationToken) ?? TryGetCompletionListType(type, enclosingNamedType, semanticModel.Compilation); if (enumType == null) { if (context.Trigger.Kind == CompletionTriggerKind.Insertion && s_triggerCharacters.Contains(context.Trigger.Character)) { // This completion provider understands static members of matching types, but doesn't // proactively trigger completion for them to avoid interfering with common typing patterns. return; } // If this isn't an enum or marked with completionlist, also check if it contains static members of // a matching type. These 'enum-like' types have similar characteristics to enum completion, but do // not show the containing type as a separate item in completion. showType = false; enumType = TryGetTypeWithStaticMembers(type); if (enumType == null) { return; } } type = enumType; } var hideAdvancedMembers = context.CompletionOptions.HideAdvancedMembers; if (!type.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) { return; } // Does type have any aliases? var alias = await type.FindApplicableAliasAsync(position, semanticModel, cancellationToken).ConfigureAwait(false); var displayText = alias != null ? alias.Name : type.ToMinimalDisplayString(semanticModel, position); // Add the enum itself. var symbol = alias ?? type; var sortText = symbol.Name; if (showType) { context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText, displayTextSuffix: "", symbols: ImmutableArray.Create(symbol), rules: s_enumTypeRules, contextPosition: position, sortText: sortText)); } // And now all the accessible members of the enum. if (type.TypeKind == TypeKind.Enum) { // We'll want to build a list of the actual enum members and all accessible instances of that enum, too var index = 0; var fields = type.GetMembers().OfType <IFieldSymbol>().Where(f => f.IsConst).Where(f => f.HasConstantValue); foreach (var field in fields.OrderBy(f => IntegerUtilities.ToInt64(f.ConstantValue))) { index++; if (!field.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) { continue; } var memberDisplayName = $"{displayText}.{field.Name}"; context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: memberDisplayName, displayTextSuffix: "", symbols: ImmutableArray.Create <ISymbol>(field), rules: CompletionItemRules.Default, contextPosition: position, sortText: $"{sortText}_{index:0000}", filterText: memberDisplayName)); } } else if (enclosingNamedType is not null) { // Build a list of the members with the same type as the target foreach (var member in type.GetMembers()) { ISymbol staticSymbol; ITypeSymbol symbolType; if (member is IFieldSymbol { IsStatic: true } field) { staticSymbol = field; symbolType = field.Type; } else if (member is IPropertySymbol { IsStatic: true, IsIndexer: false } property) { staticSymbol = property; symbolType = property.Type; }