From 25593905ed2f698524ed51873e02595b30c87830 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Wed, 16 Sep 2020 15:25:00 -0700 Subject: [PATCH 01/10] Add tests --- .../TypeImportCompletionProviderTests.cs | 138 +++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs index 4fdd5cf8a655c..ec78e49417ed3 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Experiments; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.Composition; using Roslyn.Test.Utilities; using Xunit; @@ -35,12 +34,15 @@ internal override Type GetCompletionProviderType() private bool DisallowAddingImports { get; set; } + private bool HideAdvancedMembers { get; set; } + protected override OptionSet WithChangedOptions(OptionSet options) { return options .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, ShowImportCompletionItemsOptionValue) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, IsExpandedCompletion) - .WithChangedOption(CompletionServiceOptions.DisallowAddingImports, DisallowAddingImports); + .WithChangedOption(CompletionServiceOptions.DisallowAddingImports, DisallowAddingImports) + .WithChangedOption(CompletionOptions.HideAdvancedMembers, LanguageNames.CSharp, HideAdvancedMembers); } protected override TestComposition GetComposition() @@ -1413,6 +1415,138 @@ private static void AssertRelativeOrder(List expectedTypesInRelativeOrde } } + [InlineData(true)] + [InlineData(false)] + [Theory, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task TestBrowsableAwaysFromReferences(bool isProjectReference) + { + var srcDoc = @" +class Program +{ + void M() + { + $$ + } +}"; + + var refDoc = @" +namespace Foo +{ + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Always)] + public class Goo + { + } +}"; + + var markup = isProjectReference switch + { + true => CreateMarkupForProjectWithProjectReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), + false => CreateMarkupForProjectWithMetadataReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp) + }; + + await VerifyTypeImportItemExistsAsync( + markup, + "Goo", + glyph: (int)Glyph.ClassPublic, + inlineDescription: "Foo"); + } + + [InlineData(true)] + [InlineData(false)] + [Theory, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task TestBrowsableNeverFromReferences(bool isProjectReference) + { + var srcDoc = @" +class Program +{ + void M() + { + $$ + } +}"; + + var refDoc = @" +namespace Foo +{ + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public class Goo + { + } +}"; + + var (markup, shouldContainItem) = isProjectReference switch + { + true => (CreateMarkupForProjectWithProjectReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), true), + false => (CreateMarkupForProjectWithMetadataReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), false), + }; + + if (shouldContainItem) + { + await VerifyTypeImportItemExistsAsync( + markup, + "Goo", + glyph: (int)Glyph.ClassPublic, + inlineDescription: "Foo"); + } + else + { + await VerifyTypeImportItemIsAbsentAsync( + markup, + "Goo", + inlineDescription: "Foo"); + } + } + + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + [Theory, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task TestBrowsableAdvancedFromReferences(bool isProjectReference, bool hideAdvancedMembers) + { + HideAdvancedMembers = hideAdvancedMembers; + + var srcDoc = @" +class Program +{ + void M() + { + $$ + } +}"; + + var refDoc = @" +namespace Foo +{ + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public class Goo + { + } +}"; + + var (markup, shouldContainItem) = isProjectReference switch + { + true => (CreateMarkupForProjectWithProjectReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), true), + false => (CreateMarkupForProjectWithMetadataReference(srcDoc, refDoc, LanguageNames.CSharp, LanguageNames.CSharp), !hideAdvancedMembers), + }; + + if (shouldContainItem) + { + await VerifyTypeImportItemExistsAsync( + markup, + "Goo", + glyph: (int)Glyph.ClassPublic, + inlineDescription: "Foo"); + } + else + { + await VerifyTypeImportItemIsAbsentAsync( + markup, + "Goo", + inlineDescription: "Foo"); + } + } + private Task VerifyTypeImportItemExistsAsync(string markup, string expectedItem, int glyph, string inlineDescription, string displayTextSuffix = null, string expectedDescriptionOrNull = null, CompletionItemFlags? flags = null) => VerifyItemExistsAsync(markup, expectedItem, displayTextSuffix: displayTextSuffix, glyph: glyph, inlineDescription: inlineDescription, expectedDescriptionOrNull: expectedDescriptionOrNull, flags: flags); From 37f0eb9ac1b064dec89554214593bdf0f78f80d2 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Wed, 16 Sep 2020 15:38:47 -0700 Subject: [PATCH 02/10] Only show editor browsable types from unimported namespace --- ...tTypeImportCompletionService.CacheEntry.cs | 42 ++++++++++++-- .../AbstractTypeImportCompletionService.cs | 58 ++++++++++++++++--- .../Shared/Extensions/ISymbolExtensions.cs | 58 ++++++++++++++----- 3 files changed, 132 insertions(+), 26 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index aefc6aa941180..ca391806b04c5 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -25,15 +25,20 @@ private readonly struct CacheEntry private ImmutableArray ItemInfos { get; } + public bool ContainsAdvancedMembers { get; } + private CacheEntry( Checksum checksum, string language, + bool containsAdvancedmembers, ImmutableArray items) { Checksum = checksum; Language = language; + ContainsAdvancedMembers = containsAdvancedmembers; ItemInfos = items; + } public ImmutableArray GetItemsForContext( @@ -41,15 +46,17 @@ public ImmutableArray GetItemsForContext( string genericTypeSuffix, bool isInternalsVisible, bool isAttributeContext, - bool isCaseSensitive) + bool isCaseSensitive, + bool hideAdvancedMembers) { // We will need to adjust some items if the request is made in: // 1. attribute context, then we will not show or complete with "Attribute" suffix. // 2. a project with different language than when the cache entry was created, // then we will change the generic suffix accordingly. + // 3. asked to hide advanced members and there is advanced member in the cache // Otherwise, we can simply return cached items. var isSameLanguage = Language == language; - if (isSameLanguage && !isAttributeContext) + if (isSameLanguage && !isAttributeContext && !(hideAdvancedMembers && ContainsAdvancedMembers)) { return ItemInfos.Where(info => info.IsPublic || isInternalsVisible).SelectAsArray(info => info.Item); } @@ -59,6 +66,11 @@ public ImmutableArray GetItemsForContext( { if (info.IsPublic || isInternalsVisible) { + if (hideAdvancedMembers && info.IsEditorBrowsableStateAdvanced) + { + continue; + } + var item = info.Item; if (isAttributeContext) { @@ -99,14 +111,18 @@ public class Builder : IDisposable private readonly string _language; private readonly string _genericTypeSuffix; private readonly Checksum _checksum; + private readonly EditorBrowsableInfo _editorBrowsableInfo; private readonly ArrayBuilder _itemsBuilder; - public Builder(Checksum checksum, string language, string genericTypeSuffix) + private bool _containsAdvancedMembers; + + public Builder(Checksum checksum, string language, string genericTypeSuffix, EditorBrowsableInfo editorBrowsableInfo) { _checksum = checksum; _language = language; _genericTypeSuffix = genericTypeSuffix; + _editorBrowsableInfo = editorBrowsableInfo; _itemsBuilder = ArrayBuilder.GetInstance(); } @@ -116,11 +132,28 @@ public CacheEntry ToReferenceCacheEntry() return new CacheEntry( _checksum, _language, + _containsAdvancedMembers, _itemsBuilder.ToImmutable()); } public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool isPublic) { + // We want to cache items with EditoBrowsableState == Advanced regarless of current "hide adv members" option value + var (isBrowsable, isEditorBrowsableStateAdvanced) = symbol.IsEditorBrowsableWithState( + hideAdvancedMembers: false, + _editorBrowsableInfo.Compilation, + _editorBrowsableInfo.EditorBrowsableAttributeConstructor, + _editorBrowsableInfo.typeLibTypeAttributeConstructors, + _editorBrowsableInfo.TypeLibFuncAttributeConstructors, + _editorBrowsableInfo.TypeLibVarAttributeConstructors, + _editorBrowsableInfo.HideModuleNameAttribute); + + if (!isBrowsable) + { + // Hide this item from completion + return; + } + var isGeneric = symbol.Arity > 0; // Need to determine if a type is an attribute up front since we want to filter out @@ -140,7 +173,8 @@ public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool is CompletionItemFlags.CachedAndExpanded, extensionMethodData: null); - _itemsBuilder.Add(new TypeImportCompletionItemInfo(item, isPublic, isGeneric, isAttribute)); + _containsAdvancedMembers = _containsAdvancedMembers || isEditorBrowsableStateAdvanced; + _itemsBuilder.Add(new TypeImportCompletionItemInfo(item, isPublic, isGeneric, isAttribute, isEditorBrowsableStateAdvanced)); } public void Dispose() diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs index a92cecaa6830e..5dde8d76baeda 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion @@ -40,6 +41,7 @@ internal AbstractTypeImportCompletionService(Workspace workspace) bool forceCacheCreation, CancellationToken cancellationToken) { + var hideAdvancedmembers = currentProject.Solution.Workspace.Options.GetOption(CompletionOptions.HideAdvancedMembers, currentProject.Language); var getCacheResults = await GetCacheEntriesAsync(currentProject, syntaxContext, forceCacheCreation, cancellationToken).ConfigureAwait(false); if (getCacheResults == null) @@ -68,7 +70,8 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul GenericTypeSuffix, currentCompilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(cacheResult.Assembly), syntaxContext.IsAttributeNameContext, - IsCaseSensitive); + IsCaseSensitive, + hideAdvancedmembers); } } @@ -114,11 +117,13 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul } } + var editorBrowsableInfo = new EditorBrowsableInfo(currentCompilation); + foreach (var peReference in currentProject.MetadataReferences.OfType()) { if (HasGlobalAlias(peReference) && currentCompilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assembly && - TryGetCacheForPEReference(solution, currentCompilation, peReference, syntaxContext, forceCacheCreation, cancellationToken, out cacheResult)) + TryGetCacheForPEReference(solution, editorBrowsableInfo, peReference, syntaxContext, forceCacheCreation, cancellationToken, out cacheResult)) { if (cacheResult.HasValue) { @@ -162,6 +167,7 @@ static bool HasGlobalAlias(MetadataReference? metadataReference) syntaxContext, forceCacheCreation, CacheService.ProjectItemsCache, + new EditorBrowsableInfo(compilation), cancellationToken); } @@ -170,7 +176,7 @@ static bool HasGlobalAlias(MetadataReference? metadataReference) /// private bool TryGetCacheForPEReference( Solution solution, - Compilation compilation, + EditorBrowsableInfo editorBrowsableInfo, PortableExecutableReference peReference, SyntaxContext syntaxContext, bool forceCacheCreation, @@ -188,7 +194,7 @@ private bool TryGetCacheForPEReference( return false; } - if (!(compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assemblySymbol)) + if (!(editorBrowsableInfo.Compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assemblySymbol)) { result = null; return false; @@ -202,6 +208,7 @@ private bool TryGetCacheForPEReference( syntaxContext, forceCacheCreation, CacheService.PEItemsCache, + editorBrowsableInfo, cancellationToken); return true; } @@ -214,6 +221,7 @@ private bool TryGetCacheForPEReference( SyntaxContext syntaxContext, bool forceCacheCreation, IDictionary cache, + EditorBrowsableInfo editorBrowsableInfo, CancellationToken cancellationToken) where TKey : notnull { @@ -228,7 +236,7 @@ private bool TryGetCacheForPEReference( // Cache miss, create all items only when asked. if (forceCacheCreation) { - using var builder = new CacheEntry.Builder(checksum, language, GenericTypeSuffix); + using var builder = new CacheEntry.Builder(checksum, language, GenericTypeSuffix, editorBrowsableInfo); GetCompletionItemsForTopLevelTypeDeclarations(assembly.GlobalNamespace, builder, cancellationToken); cacheEntry = builder.ToReferenceCacheEntry(); cache[key] = cacheEntry; @@ -353,12 +361,13 @@ private readonly struct TypeImportCompletionItemInfo { private readonly ItemPropertyKind _properties; - public TypeImportCompletionItemInfo(CompletionItem item, bool isPublic, bool isGeneric, bool isAttribute) + public TypeImportCompletionItemInfo(CompletionItem item, bool isPublic, bool isGeneric, bool isAttribute, bool isEditorBrowsableStateAdvanced = false) { Item = item; _properties = (isPublic ? ItemPropertyKind.IsPublic : 0) | (isGeneric ? ItemPropertyKind.IsGeneric : 0) - | (isAttribute ? ItemPropertyKind.IsAttribute : 0); + | (isAttribute ? ItemPropertyKind.IsAttribute : 0) + | (isEditorBrowsableStateAdvanced ? ItemPropertyKind.IsEditorBrowsableStateAdvanced : 0); } public CompletionItem Item { get; } @@ -372,8 +381,11 @@ public bool IsGeneric public bool IsAttribute => (_properties & ItemPropertyKind.IsAttribute) != 0; + public bool IsEditorBrowsableStateAdvanced + => (_properties & ItemPropertyKind.IsEditorBrowsableStateAdvanced) != 0; + public TypeImportCompletionItemInfo WithItem(CompletionItem item) - => new TypeImportCompletionItemInfo(item, IsPublic, IsGeneric, IsAttribute); + => new TypeImportCompletionItemInfo(item, IsPublic, IsGeneric, IsAttribute, IsEditorBrowsableStateAdvanced); [Flags] private enum ItemPropertyKind : byte @@ -381,7 +393,37 @@ private enum ItemPropertyKind : byte IsPublic = 0x1, IsGeneric = 0x2, IsAttribute = 0x4, + IsEditorBrowsableStateAdvanced = 0x8, } } + + private class EditorBrowsableInfo + { + public Compilation Compilation { get; } + private IMethodSymbol? _editorBrowsableAttributeConstructor; + private List? _typeLibTypeAttributeConstructors; + private List? _typeLibFuncAttributeConstructors; + private List? _typeLibVarAttributeConstructors; + + public EditorBrowsableInfo(Compilation compilation) + { + Compilation = compilation; + HideModuleNameAttribute = Compilation.HideModuleNameAttribute(); + } + + public IMethodSymbol EditorBrowsableAttributeConstructor + => _editorBrowsableAttributeConstructor ??= EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(Compilation); + + public List typeLibTypeAttributeConstructors + => _typeLibTypeAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(Compilation); + + public List TypeLibFuncAttributeConstructors + => _typeLibFuncAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(Compilation); + + public List TypeLibVarAttributeConstructors + => _typeLibVarAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(Compilation); + + public INamedTypeSymbol? HideModuleNameAttribute { get; } + } } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index c9902d37d0bf7..0c251ad475719 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -54,13 +54,35 @@ public static bool IsEditorBrowsable( List? typeLibFuncAttributeConstructors = null, List? typeLibVarAttributeConstructors = null, INamedTypeSymbol? hideModuleNameAttribute = null) + { + return IsEditorBrowsableWithState( + symbol, + hideAdvancedMembers, + compilation, + editorBrowsableAttributeConstructor, + typeLibTypeAttributeConstructors, + typeLibFuncAttributeConstructors, + typeLibVarAttributeConstructors, + hideModuleNameAttribute).isBrowsable; + } + + // In addition to given symbol's browsability, also returns its EditorBrowsableState if it contains EditorBrowsableAttribute. + public static (bool isBrowsable, bool isEditorBrowsableStateAdvanced) IsEditorBrowsableWithState( + this ISymbol symbol, + bool hideAdvancedMembers, + Compilation compilation, + IMethodSymbol? editorBrowsableAttributeConstructor = null, + List? typeLibTypeAttributeConstructors = null, + List? typeLibFuncAttributeConstructors = null, + List? typeLibVarAttributeConstructors = null, + INamedTypeSymbol? hideModuleNameAttribute = null) { // Namespaces can't have attributes, so just return true here. This also saves us a // costly check if this namespace has any locations in source (since a merged namespace // needs to go collect all the locations). if (symbol.Kind == SymbolKind.Namespace) { - return true; + return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); } // check for IsImplicitlyDeclared so we don't spend time examining VB's embedded types. @@ -68,7 +90,7 @@ public static bool IsEditorBrowsable( // have attributes, so it can't be hidden by them. if (symbol.IsImplicitlyDeclared) { - return true; + return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); } // Ignore browsability limiting attributes if the symbol is declared in source. @@ -77,10 +99,10 @@ public static bool IsEditorBrowsable( if (symbol.Locations.All(loc => loc.IsInSource)) { // The HideModuleNameAttribute still applies to Modules defined in source - return !IsBrowsingProhibitedByHideModuleNameAttribute(symbol, compilation, hideModuleNameAttribute); + return (!IsBrowsingProhibitedByHideModuleNameAttribute(symbol, compilation, hideModuleNameAttribute), isEditorBrowsableStateAdvanced: false); } - return !IsBrowsingProhibited( + var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibited( symbol, hideAdvancedMembers, compilation, @@ -89,9 +111,11 @@ public static bool IsEditorBrowsable( typeLibFuncAttributeConstructors, typeLibVarAttributeConstructors, hideModuleNameAttribute); + + return (!isProhibited, isEditorBrowsableStateAdvanced); } - private static bool IsBrowsingProhibited( + private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibited( ISymbol symbol, bool hideAdvancedMembers, Compilation compilation, @@ -104,14 +128,16 @@ private static bool IsBrowsingProhibited( var attributes = symbol.GetAttributes(); if (attributes.Length == 0) { - return false; + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); } - return IsBrowsingProhibitedByEditorBrowsableAttribute(attributes, hideAdvancedMembers, compilation, editorBrowsableAttributeConstructor) + var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibitedByEditorBrowsableAttribute(attributes, hideAdvancedMembers, compilation, editorBrowsableAttributeConstructor); + + return ((isProhibited || IsBrowsingProhibitedByTypeLibTypeAttribute(attributes, compilation, typeLibTypeAttributeConstructors) || IsBrowsingProhibitedByTypeLibFuncAttribute(attributes, compilation, typeLibFuncAttributeConstructors) || IsBrowsingProhibitedByTypeLibVarAttribute(attributes, compilation, typeLibVarAttributeConstructors) - || IsBrowsingProhibitedByHideModuleNameAttribute(symbol, compilation, hideModuleNameAttribute, attributes); + || IsBrowsingProhibitedByHideModuleNameAttribute(symbol, compilation, hideModuleNameAttribute, attributes)), isEditorBrowsableStateAdvanced); } private static bool IsBrowsingProhibitedByHideModuleNameAttribute( @@ -135,13 +161,13 @@ private static bool IsBrowsingProhibitedByHideModuleNameAttribute( return false; } - private static bool IsBrowsingProhibitedByEditorBrowsableAttribute( + private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibitedByEditorBrowsableAttribute( ImmutableArray attributes, bool hideAdvancedMembers, Compilation compilation, IMethodSymbol? constructor) { constructor ??= EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation); if (constructor == null) { - return false; + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); } foreach (var attribute in attributes) @@ -154,15 +180,19 @@ private static bool IsBrowsingProhibitedByEditorBrowsableAttribute( var state = (EditorBrowsableState)attribute.ConstructorArguments.First().Value; #nullable enable - if (EditorBrowsableState.Never == state || - (hideAdvancedMembers && EditorBrowsableState.Advanced == state)) + if (EditorBrowsableState.Never == state) { - return true; + return (isProhibited: true, isEditorBrowsableStateAdvanced: false); + } + + if (EditorBrowsableState.Advanced == state) + { + return (isProhibited: hideAdvancedMembers, isEditorBrowsableStateAdvanced: true); } } } - return false; + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); } private static bool IsBrowsingProhibitedByTypeLibTypeAttribute( From 71ae6c7dfb8f7764a4525fff6c275765497976da Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Wed, 16 Sep 2020 17:25:58 -0700 Subject: [PATCH 03/10] Use ImmutableArray and Optional --- .../AbstractTypeImportCompletionService.cs | 35 ++++----- .../Shared/Extensions/ISymbolExtensions.cs | 71 +++++++++++-------- .../Utilities/EditorBrowsableHelpers.cs | 15 ++-- 3 files changed, 67 insertions(+), 54 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs index 5dde8d76baeda..53de67c3ef01a 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs @@ -397,33 +397,36 @@ private enum ItemPropertyKind : byte } } + // Things needed for determining whether a symbol is EditorBrowsable. + // Grouped together and reused within each compilation. private class EditorBrowsableInfo { - public Compilation Compilation { get; } - private IMethodSymbol? _editorBrowsableAttributeConstructor; - private List? _typeLibTypeAttributeConstructors; - private List? _typeLibFuncAttributeConstructors; - private List? _typeLibVarAttributeConstructors; + private Optional? _editorBrowsableAttributeConstructor; + private ImmutableArray? _typeLibTypeAttributeConstructors; + private ImmutableArray? _typeLibFuncAttributeConstructors; + private ImmutableArray? _typeLibVarAttributeConstructors; - public EditorBrowsableInfo(Compilation compilation) - { - Compilation = compilation; - HideModuleNameAttribute = Compilation.HideModuleNameAttribute(); - } + public Compilation Compilation { get; } - public IMethodSymbol EditorBrowsableAttributeConstructor - => _editorBrowsableAttributeConstructor ??= EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(Compilation); + public Optional EditorBrowsableAttributeConstructor + => _editorBrowsableAttributeConstructor ??= new(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(Compilation)); - public List typeLibTypeAttributeConstructors + public ImmutableArray typeLibTypeAttributeConstructors => _typeLibTypeAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(Compilation); - public List TypeLibFuncAttributeConstructors + public ImmutableArray TypeLibFuncAttributeConstructors => _typeLibFuncAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(Compilation); - public List TypeLibVarAttributeConstructors + public ImmutableArray TypeLibVarAttributeConstructors => _typeLibVarAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(Compilation); - public INamedTypeSymbol? HideModuleNameAttribute { get; } + public Optional HideModuleNameAttribute { get; } + + public EditorBrowsableInfo(Compilation compilation) + { + Compilation = compilation; + HideModuleNameAttribute = new(Compilation.HideModuleNameAttribute()); + } } } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index 0c251ad475719..40bb96b70f948 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -49,11 +49,11 @@ public static bool IsEditorBrowsable( this ISymbol symbol, bool hideAdvancedMembers, Compilation compilation, - IMethodSymbol? editorBrowsableAttributeConstructor = null, - List? typeLibTypeAttributeConstructors = null, - List? typeLibFuncAttributeConstructors = null, - List? typeLibVarAttributeConstructors = null, - INamedTypeSymbol? hideModuleNameAttribute = null) + Optional editorBrowsableAttributeConstructor = default, + ImmutableArray typeLibTypeAttributeConstructors = default, + ImmutableArray typeLibFuncAttributeConstructors = default, + ImmutableArray typeLibVarAttributeConstructors = default, + Optional hideModuleNameAttribute = default) { return IsEditorBrowsableWithState( symbol, @@ -71,11 +71,11 @@ public static (bool isBrowsable, bool isEditorBrowsableStateAdvanced) IsEditorBr this ISymbol symbol, bool hideAdvancedMembers, Compilation compilation, - IMethodSymbol? editorBrowsableAttributeConstructor = null, - List? typeLibTypeAttributeConstructors = null, - List? typeLibFuncAttributeConstructors = null, - List? typeLibVarAttributeConstructors = null, - INamedTypeSymbol? hideModuleNameAttribute = null) + Optional editorBrowsableAttributeConstructor = default, + ImmutableArray typeLibTypeAttributeConstructors = default, + ImmutableArray typeLibFuncAttributeConstructors = default, + ImmutableArray typeLibVarAttributeConstructors = default, + Optional hideModuleNameAttribute = default) { // Namespaces can't have attributes, so just return true here. This also saves us a // costly check if this namespace has any locations in source (since a merged namespace @@ -119,11 +119,11 @@ private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsi ISymbol symbol, bool hideAdvancedMembers, Compilation compilation, - IMethodSymbol? editorBrowsableAttributeConstructor, - List? typeLibTypeAttributeConstructors, - List? typeLibFuncAttributeConstructors, - List? typeLibVarAttributeConstructors, - INamedTypeSymbol? hideModuleNameAttribute) + Optional editorBrowsableAttributeConstructor, + ImmutableArray typeLibTypeAttributeConstructors, + ImmutableArray typeLibFuncAttributeConstructors, + ImmutableArray typeLibVarAttributeConstructors, + Optional hideModuleNameAttribute) { var attributes = symbol.GetAttributes(); if (attributes.Length == 0) @@ -141,7 +141,7 @@ private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsi } private static bool IsBrowsingProhibitedByHideModuleNameAttribute( - ISymbol symbol, Compilation compilation, INamedTypeSymbol? hideModuleNameAttribute, ImmutableArray attributes = default) + ISymbol symbol, Compilation compilation, Optional hideModuleNameAttribute, ImmutableArray attributes = default) { if (!symbol.IsModuleType()) { @@ -149,10 +149,15 @@ private static bool IsBrowsingProhibitedByHideModuleNameAttribute( } attributes = attributes.IsDefault ? symbol.GetAttributes() : attributes; - hideModuleNameAttribute ??= compilation.HideModuleNameAttribute(); + + if (!hideModuleNameAttribute.HasValue) + { + hideModuleNameAttribute = new(compilation.HideModuleNameAttribute()); + } + foreach (var attribute in attributes) { - if (Equals(attribute.AttributeClass, hideModuleNameAttribute)) + if (Equals(attribute.AttributeClass, hideModuleNameAttribute.Value)) { return true; } @@ -162,17 +167,21 @@ private static bool IsBrowsingProhibitedByHideModuleNameAttribute( } private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibitedByEditorBrowsableAttribute( - ImmutableArray attributes, bool hideAdvancedMembers, Compilation compilation, IMethodSymbol? constructor) + ImmutableArray attributes, bool hideAdvancedMembers, Compilation compilation, Optional constructor) { - constructor ??= EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation); - if (constructor == null) + if (!constructor.HasValue) + { + constructor = new(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation)); + } + + if (constructor.Value == null) { return (isProhibited: false, isEditorBrowsableStateAdvanced: false); } foreach (var attribute in attributes) { - if (Equals(attribute.AttributeConstructor, constructor) && + if (Equals(attribute.AttributeConstructor, constructor.Value) && attribute.ConstructorArguments.Length == 1 && attribute.ConstructorArguments.First().Value is int) { @@ -196,29 +205,29 @@ private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsi } private static bool IsBrowsingProhibitedByTypeLibTypeAttribute( - ImmutableArray attributes, Compilation compilation, List? constructors) + ImmutableArray attributes, Compilation compilation, ImmutableArray constructors) { return IsBrowsingProhibitedByTypeLibAttributeWorker( attributes, - constructors ?? EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(compilation), + constructors.IsDefault ? EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(compilation) : constructors, TypeLibTypeFlagsFHidden); } private static bool IsBrowsingProhibitedByTypeLibFuncAttribute( - ImmutableArray attributes, Compilation compilation, List? constructors) + ImmutableArray attributes, Compilation compilation, ImmutableArray constructors) { return IsBrowsingProhibitedByTypeLibAttributeWorker( attributes, - constructors ?? EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(compilation), + constructors.IsDefault ? EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(compilation) : constructors, TypeLibFuncFlagsFHidden); } private static bool IsBrowsingProhibitedByTypeLibVarAttribute( - ImmutableArray attributes, Compilation compilation, List? constructors) + ImmutableArray attributes, Compilation compilation, ImmutableArray constructors) { return IsBrowsingProhibitedByTypeLibAttributeWorker( attributes, - constructors ?? EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(compilation), + constructors.IsDefault ? EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(compilation) : constructors, TypeLibVarFlagsFHidden); } @@ -227,7 +236,7 @@ private static bool IsBrowsingProhibitedByTypeLibVarAttribute( private const int TypeLibVarFlagsFHidden = 0x0040; private static bool IsBrowsingProhibitedByTypeLibAttributeWorker( - ImmutableArray attributes, List attributeConstructors, int hiddenFlag) + ImmutableArray attributes, ImmutableArray attributeConstructors, int hiddenFlag) { foreach (var attribute in attributes) { @@ -670,11 +679,11 @@ public static ImmutableArray FilterToVisibleAndBrowsableSymbols( // Since all symbols are from the same compilation, find the required attribute // constructors once and reuse. - var editorBrowsableAttributeConstructor = EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation); + var editorBrowsableAttributeConstructor = new Optional(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation)); var typeLibTypeAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(compilation); var typeLibFuncAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(compilation); var typeLibVarAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(compilation); - var hideModuleNameAttribute = compilation.HideModuleNameAttribute(); + var hideModuleNameAttribute = new Optional(compilation.HideModuleNameAttribute()); // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first // check to see if we're referencing a symbol defined in source. diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs b/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs index b4ed7e415e74a..0764138d9ea77 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs @@ -2,10 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities { @@ -48,7 +49,7 @@ public static IMethodSymbol GetSpecialEditorBrowsableAttributeConstructor(Compil } } - public static List GetSpecialTypeLibTypeAttributeConstructors(Compilation compilation) + public static ImmutableArray GetSpecialTypeLibTypeAttributeConstructors(Compilation compilation) { return GetSpecialTypeLibAttributeConstructorsWorker( compilation, @@ -56,7 +57,7 @@ public static List GetSpecialTypeLibTypeAttributeConstructors(Com "System.Runtime.InteropServices.TypeLibTypeFlags"); } - public static List GetSpecialTypeLibFuncAttributeConstructors(Compilation compilation) + public static ImmutableArray GetSpecialTypeLibFuncAttributeConstructors(Compilation compilation) { return GetSpecialTypeLibAttributeConstructorsWorker( compilation, @@ -64,7 +65,7 @@ public static List GetSpecialTypeLibFuncAttributeConstructors(Com "System.Runtime.InteropServices.TypeLibFuncFlags"); } - public static List GetSpecialTypeLibVarAttributeConstructors(Compilation compilation) + public static ImmutableArray GetSpecialTypeLibVarAttributeConstructors(Compilation compilation) { return GetSpecialTypeLibAttributeConstructorsWorker( compilation, @@ -79,7 +80,7 @@ public static List GetSpecialTypeLibVarAttributeConstructors(Comp /// but it does demand the types found follow the expected pattern. If at any point that pattern appears to be /// violated, return an empty enumerable to indicate that no appropriate constructors were found. /// - private static List GetSpecialTypeLibAttributeConstructorsWorker( + private static ImmutableArray GetSpecialTypeLibAttributeConstructorsWorker( Compilation compilation, string attributeMetadataName, string flagsMetadataName) @@ -90,7 +91,7 @@ private static List GetSpecialTypeLibAttributeConstructorsWorker( if (typeLibAttributeType == null || typeLibFlagsType == null || shortType == null) { - return new List(); + return ImmutableArray.Empty; } var candidateConstructors = typeLibAttributeType.Constructors @@ -101,7 +102,7 @@ private static List GetSpecialTypeLibAttributeConstructorsWorker( !c.Parameters[0].IsRefOrOut() && !c.Parameters[0].CustomModifiers.Any())); - return candidateConstructors.ToList(); + return candidateConstructors.ToImmutableArrayOrEmpty(); } } } From b368fe38c0dc0cf7322fdd9186adfa0607ce731a Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Thu, 17 Sep 2020 13:01:36 -0700 Subject: [PATCH 04/10] Address review comment --- .../AbstractTypeImportCompletionService.CacheEntry.cs | 6 ++++-- .../AbstractTypeImportCompletionService.cs | 4 ++-- .../Core/Portable/Shared/Extensions/DocumentExtensions.cs | 4 ++-- .../Library/ObjectBrowser/AbstractListItemFactory.cs | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index ca391806b04c5..8ace9c3cc4af6 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -56,7 +56,9 @@ public ImmutableArray GetItemsForContext( // 3. asked to hide advanced members and there is advanced member in the cache // Otherwise, we can simply return cached items. var isSameLanguage = Language == language; - if (isSameLanguage && !isAttributeContext && !(hideAdvancedMembers && ContainsAdvancedMembers)) + if (isSameLanguage && + !isAttributeContext && + !(hideAdvancedMembers && ContainsAdvancedMembers)) { return ItemInfos.Where(info => info.IsPublic || isInternalsVisible).SelectAsArray(info => info.Item); } @@ -143,7 +145,7 @@ public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool is hideAdvancedMembers: false, _editorBrowsableInfo.Compilation, _editorBrowsableInfo.EditorBrowsableAttributeConstructor, - _editorBrowsableInfo.typeLibTypeAttributeConstructors, + _editorBrowsableInfo.TypeLibTypeAttributeConstructors, _editorBrowsableInfo.TypeLibFuncAttributeConstructors, _editorBrowsableInfo.TypeLibVarAttributeConstructors, _editorBrowsableInfo.HideModuleNameAttribute); diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs index 53de67c3ef01a..2f789edf2ca3c 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs @@ -41,7 +41,7 @@ internal AbstractTypeImportCompletionService(Workspace workspace) bool forceCacheCreation, CancellationToken cancellationToken) { - var hideAdvancedmembers = currentProject.Solution.Workspace.Options.GetOption(CompletionOptions.HideAdvancedMembers, currentProject.Language); + var hideAdvancedmembers = currentProject.Solution.Options.GetOption(CompletionOptions.HideAdvancedMembers, currentProject.Language); var getCacheResults = await GetCacheEntriesAsync(currentProject, syntaxContext, forceCacheCreation, cancellationToken).ConfigureAwait(false); if (getCacheResults == null) @@ -411,7 +411,7 @@ private class EditorBrowsableInfo public Optional EditorBrowsableAttributeConstructor => _editorBrowsableAttributeConstructor ??= new(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(Compilation)); - public ImmutableArray typeLibTypeAttributeConstructors + public ImmutableArray TypeLibTypeAttributeConstructors => _typeLibTypeAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(Compilation); public ImmutableArray TypeLibFuncAttributeConstructors diff --git a/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs b/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs index 8d4c0cc7f29aa..8addec0491571 100644 --- a/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs +++ b/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs @@ -22,8 +22,8 @@ internal static class DocumentExtensions { public static bool ShouldHideAdvancedMembers(this Document document) { - // Since we don't actually have a way to configure this per-document, we can fetch from the core workspace - return document.Project.Solution.Workspace.Options.GetOption(CompletionOptions.HideAdvancedMembers, document.Project.Language); + // Since we don't actually have a way to configure this per-document, we can fetch from the solution + return document.Project.Solution.Options.GetOption(CompletionOptions.HideAdvancedMembers, document.Project.Language); } public static async Task ReplaceNodeAsync(this Document document, TNode oldNode, TNode newNode, CancellationToken cancellationToken) where TNode : SyntaxNode diff --git a/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs b/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs index 0488e5023dc6c..1a620f1133ba8 100644 --- a/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs @@ -200,7 +200,7 @@ private static void AddListItemsFromSymbols( var isHidden = !symbol.IsEditorBrowsable( hideAdvancedMembers, compilation, - editorBrowsableAttributeConstructor, + new(editorBrowsableAttributeConstructor), typeLibFuncAttributeConstructors, typeLibTypeAttributeConstructors, typeLibVarAttributeConstructors); From 6945e722601dd5b797d819ebd905d196b0c8e127 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Fri, 18 Sep 2020 16:50:50 -0700 Subject: [PATCH 05/10] Address review comments --- .../AbstractTypeImportCompletionService.CacheEntry.cs | 1 - .../AbstractTypeImportCompletionService.cs | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index 8ace9c3cc4af6..a9ebec9c12812 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -38,7 +38,6 @@ private CacheEntry( ContainsAdvancedMembers = containsAdvancedmembers; ItemInfos = items; - } public ImmutableArray GetItemsForContext( diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs index 2f789edf2ca3c..cf2eb5a6a49d1 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs @@ -361,7 +361,7 @@ private readonly struct TypeImportCompletionItemInfo { private readonly ItemPropertyKind _properties; - public TypeImportCompletionItemInfo(CompletionItem item, bool isPublic, bool isGeneric, bool isAttribute, bool isEditorBrowsableStateAdvanced = false) + public TypeImportCompletionItemInfo(CompletionItem item, bool isPublic, bool isGeneric, bool isAttribute, bool isEditorBrowsableStateAdvanced) { Item = item; _properties = (isPublic ? ItemPropertyKind.IsPublic : 0) @@ -401,6 +401,7 @@ private enum ItemPropertyKind : byte // Grouped together and reused within each compilation. private class EditorBrowsableInfo { + private Optional? _hideModuleNameAttribute; private Optional? _editorBrowsableAttributeConstructor; private ImmutableArray? _typeLibTypeAttributeConstructors; private ImmutableArray? _typeLibFuncAttributeConstructors; @@ -408,6 +409,9 @@ private class EditorBrowsableInfo public Compilation Compilation { get; } + public Optional HideModuleNameAttribute + => _hideModuleNameAttribute ??= new(Compilation.HideModuleNameAttribute()); + public Optional EditorBrowsableAttributeConstructor => _editorBrowsableAttributeConstructor ??= new(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(Compilation)); @@ -420,12 +424,9 @@ public ImmutableArray TypeLibFuncAttributeConstructors public ImmutableArray TypeLibVarAttributeConstructors => _typeLibVarAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(Compilation); - public Optional HideModuleNameAttribute { get; } - public EditorBrowsableInfo(Compilation compilation) { Compilation = compilation; - HideModuleNameAttribute = new(Compilation.HideModuleNameAttribute()); } } } From 32ec65e62d4b36fba3dd404b791ab395bacae075 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Tue, 22 Sep 2020 17:59:04 -0700 Subject: [PATCH 06/10] Address review comments --- ...actTypeImportCompletionService.CacheEntry.cs | 8 +++++--- .../AbstractTypeImportCompletionService.cs | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index a9ebec9c12812..c52462b9cb6ae 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -52,12 +52,14 @@ public ImmutableArray GetItemsForContext( // 1. attribute context, then we will not show or complete with "Attribute" suffix. // 2. a project with different language than when the cache entry was created, // then we will change the generic suffix accordingly. - // 3. asked to hide advanced members and there is advanced member in the cache + // 3. option to show advanced members is false and there is advanced member items in this cache entry + // (which need to be filtered out) // Otherwise, we can simply return cached items. var isSameLanguage = Language == language; + var needToFilterOutAdvancedMembers = hideAdvancedMembers && ContainsAdvancedMembers; if (isSameLanguage && !isAttributeContext && - !(hideAdvancedMembers && ContainsAdvancedMembers)) + !needToFilterOutAdvancedMembers) { return ItemInfos.Where(info => info.IsPublic || isInternalsVisible).SelectAsArray(info => info.Item); } @@ -139,7 +141,7 @@ public CacheEntry ToReferenceCacheEntry() public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool isPublic) { - // We want to cache items with EditoBrowsableState == Advanced regarless of current "hide adv members" option value + // We want to cache items with EditoBrowsableState == Advanced regardless of current "hide adv members" option value var (isBrowsable, isEditorBrowsableStateAdvanced) = symbol.IsEditorBrowsableWithState( hideAdvancedMembers: false, _editorBrowsableInfo.Compilation, diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs index cf2eb5a6a49d1..6b2bcd352fc22 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs @@ -79,7 +79,10 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul { var _ = ArrayBuilder.GetInstance(out var builder); - var cacheResult = await GetCacheForProjectAsync(currentProject, syntaxContext, forceCacheCreation: true, cancellationToken).ConfigureAwait(false); + var currentCompilation = await currentProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + var editorBrowsableInfo = new EditorBrowsableInfo(currentCompilation); + + var cacheResult = await GetCacheForProjectAsync(currentProject, syntaxContext, forceCacheCreation: true, editorBrowsableInfo, cancellationToken).ConfigureAwait(false); // We always force create a cache for current project. Debug.Assert(cacheResult.HasValue); @@ -88,7 +91,6 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul var solution = currentProject.Solution; var graph = solution.GetProjectDependencyGraph(); var referencedProjects = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id).SelectAsArray(id => solution.GetRequiredProject(id)); - var currentCompilation = await currentProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); foreach (var referencedProject in referencedProjects.Where(p => p.SupportsCompilation)) { @@ -102,6 +104,7 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul referencedProject, syntaxContext, forceCacheCreation, + editorBrowsableInfo: null, cancellationToken).ConfigureAwait(false); if (cacheResult.HasValue) @@ -117,8 +120,6 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul } } - var editorBrowsableInfo = new EditorBrowsableInfo(currentCompilation); - foreach (var peReference in currentProject.MetadataReferences.OfType()) { if (HasGlobalAlias(peReference) && @@ -153,6 +154,7 @@ static bool HasGlobalAlias(MetadataReference? metadataReference) Project project, SyntaxContext syntaxContext, bool forceCacheCreation, + EditorBrowsableInfo? editorBrowsableInfo, CancellationToken cancellationToken) { var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); @@ -167,7 +169,7 @@ static bool HasGlobalAlias(MetadataReference? metadataReference) syntaxContext, forceCacheCreation, CacheService.ProjectItemsCache, - new EditorBrowsableInfo(compilation), + editorBrowsableInfo ?? new EditorBrowsableInfo(compilation), cancellationToken); } @@ -399,6 +401,11 @@ private enum ItemPropertyKind : byte // Things needed for determining whether a symbol is EditorBrowsable. // Grouped together and reused within each compilation. + // + // Based on profiling results, initializing these symbols upfront for each referenced + // project every time a completion is triggered is expensive. Making them lazy would + // eliminate this overhead when we have a cache hit while keeping it easy to share + // between original projects and PE references when trying to get completion items. private class EditorBrowsableInfo { private Optional? _hideModuleNameAttribute; From c2eb3b57e34ea1199393d8ab459ec58504abe539 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Thu, 24 Sep 2020 15:52:01 -0700 Subject: [PATCH 07/10] Combine loops --- ...tTypeImportCompletionService.CacheEntry.cs | 71 ++++++++----------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index c52462b9cb6ae..22993109e8c81 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -25,18 +24,14 @@ private readonly struct CacheEntry private ImmutableArray ItemInfos { get; } - public bool ContainsAdvancedMembers { get; } - private CacheEntry( Checksum checksum, string language, - bool containsAdvancedmembers, ImmutableArray items) { Checksum = checksum; Language = language; - ContainsAdvancedMembers = containsAdvancedmembers; ItemInfos = items; } @@ -48,54 +43,48 @@ public ImmutableArray GetItemsForContext( bool isCaseSensitive, bool hideAdvancedMembers) { - // We will need to adjust some items if the request is made in: - // 1. attribute context, then we will not show or complete with "Attribute" suffix. - // 2. a project with different language than when the cache entry was created, - // then we will change the generic suffix accordingly. - // 3. option to show advanced members is false and there is advanced member items in this cache entry - // (which need to be filtered out) - // Otherwise, we can simply return cached items. var isSameLanguage = Language == language; - var needToFilterOutAdvancedMembers = hideAdvancedMembers && ContainsAdvancedMembers; - if (isSameLanguage && - !isAttributeContext && - !needToFilterOutAdvancedMembers) - { - return ItemInfos.Where(info => info.IsPublic || isInternalsVisible).SelectAsArray(info => info.Item); - } + using var _ = ArrayBuilder.GetInstance(out var builder); - var builder = ArrayBuilder.GetInstance(); foreach (var info in ItemInfos) { - if (info.IsPublic || isInternalsVisible) + if (!info.IsPublic && !isInternalsVisible) { - if (hideAdvancedMembers && info.IsEditorBrowsableStateAdvanced) - { - continue; - } + continue; + } - var item = info.Item; - if (isAttributeContext) - { - if (!info.IsAttribute) - { - continue; - } + // Option to show advanced members is false so we need to exclude them. + if (hideAdvancedMembers && info.IsEditorBrowsableStateAdvanced) + { + continue; + } - item = GetAppropriateAttributeItem(info.Item, isCaseSensitive); - } + var item = info.Item; - if (!isSameLanguage && info.IsGeneric) + if (isAttributeContext) + { + // Don't show non attribute item in attribute context + if (!info.IsAttribute) { - // We don't want to cache this item. - item = ImportCompletionItem.CreateItemWithGenericDisplaySuffix(item, genericTypeSuffix); + continue; } - builder.Add(item); + // We are in attribute context, will not show or complete with "Attribute" suffix. + item = GetAppropriateAttributeItem(info.Item, isCaseSensitive); } + + // A project with different language than when the cache entry was created for, + // then we will change the generic suffix accordingly. + if (!isSameLanguage && info.IsGeneric) + { + // We don't want to cache this item. + item = ImportCompletionItem.CreateItemWithGenericDisplaySuffix(item, genericTypeSuffix); + } + + builder.Add(item); } - return builder.ToImmutableAndFree(); + return builder.ToImmutable(); static CompletionItem GetAppropriateAttributeItem(CompletionItem attributeItem, bool isCaseSensitive) { @@ -118,8 +107,6 @@ public class Builder : IDisposable private readonly ArrayBuilder _itemsBuilder; - private bool _containsAdvancedMembers; - public Builder(Checksum checksum, string language, string genericTypeSuffix, EditorBrowsableInfo editorBrowsableInfo) { _checksum = checksum; @@ -135,7 +122,6 @@ public CacheEntry ToReferenceCacheEntry() return new CacheEntry( _checksum, _language, - _containsAdvancedMembers, _itemsBuilder.ToImmutable()); } @@ -176,7 +162,6 @@ public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool is CompletionItemFlags.CachedAndExpanded, extensionMethodData: null); - _containsAdvancedMembers = _containsAdvancedMembers || isEditorBrowsableStateAdvanced; _itemsBuilder.Add(new TypeImportCompletionItemInfo(item, isPublic, isGeneric, isAttribute, isEditorBrowsableStateAdvanced)); } From 01ac9272b3c1c00a71f32438793822287cacd7a8 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Thu, 24 Sep 2020 15:55:54 -0700 Subject: [PATCH 08/10] Address review comments --- ...tTypeImportCompletionService.CacheEntry.cs | 7 +- .../AbstractTypeImportCompletionService.cs | 68 +--- .../ObjectBrowser/AbstractListItemFactory.cs | 13 +- .../Shared/Extensions/ISymbolExtensions.cs | 304 ------------------ .../Utilities/EditorBrowsableHelpers.cs | 288 ++++++++++++++++- 5 files changed, 307 insertions(+), 373 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index 22993109e8c81..390351bc25ac3 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.Shared.Utilities.EditorBrowsableHelpers; namespace Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion { @@ -131,11 +132,7 @@ public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool is var (isBrowsable, isEditorBrowsableStateAdvanced) = symbol.IsEditorBrowsableWithState( hideAdvancedMembers: false, _editorBrowsableInfo.Compilation, - _editorBrowsableInfo.EditorBrowsableAttributeConstructor, - _editorBrowsableInfo.TypeLibTypeAttributeConstructors, - _editorBrowsableInfo.TypeLibFuncAttributeConstructors, - _editorBrowsableInfo.TypeLibVarAttributeConstructors, - _editorBrowsableInfo.HideModuleNameAttribute); + _editorBrowsableInfo); if (!isBrowsable) { diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs index 6b2bcd352fc22..85e3d4e2ea5a7 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.cs @@ -16,9 +16,10 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; -using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.Shared.Utilities.EditorBrowsableHelpers; + namespace Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion { internal abstract partial class AbstractTypeImportCompletionService : ITypeImportCompletionService @@ -80,7 +81,7 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul var _ = ArrayBuilder.GetInstance(out var builder); var currentCompilation = await currentProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - var editorBrowsableInfo = new EditorBrowsableInfo(currentCompilation); + var editorBrowsableInfo = new Lazy(() => new EditorBrowsableInfo(currentCompilation)); var cacheResult = await GetCacheForProjectAsync(currentProject, syntaxContext, forceCacheCreation: true, editorBrowsableInfo, cancellationToken).ConfigureAwait(false); @@ -124,7 +125,7 @@ ImmutableArray GetItemsFromCacheResult(GetCacheResult cacheResul { if (HasGlobalAlias(peReference) && currentCompilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assembly && - TryGetCacheForPEReference(solution, editorBrowsableInfo, peReference, syntaxContext, forceCacheCreation, cancellationToken, out cacheResult)) + TryGetCacheForPEReference(solution, assembly, editorBrowsableInfo, peReference, syntaxContext, forceCacheCreation, cancellationToken, out cacheResult)) { if (cacheResult.HasValue) { @@ -154,7 +155,7 @@ static bool HasGlobalAlias(MetadataReference? metadataReference) Project project, SyntaxContext syntaxContext, bool forceCacheCreation, - EditorBrowsableInfo? editorBrowsableInfo, + Lazy? editorBrowsableInfo, CancellationToken cancellationToken) { var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); @@ -169,7 +170,7 @@ static bool HasGlobalAlias(MetadataReference? metadataReference) syntaxContext, forceCacheCreation, CacheService.ProjectItemsCache, - editorBrowsableInfo ?? new EditorBrowsableInfo(compilation), + editorBrowsableInfo ?? new Lazy(() => new EditorBrowsableInfo(compilation)), cancellationToken); } @@ -178,7 +179,8 @@ static bool HasGlobalAlias(MetadataReference? metadataReference) /// private bool TryGetCacheForPEReference( Solution solution, - EditorBrowsableInfo editorBrowsableInfo, + IAssemblySymbol assemblySymbol, + Lazy editorBrowsableInfo, PortableExecutableReference peReference, SyntaxContext syntaxContext, bool forceCacheCreation, @@ -196,12 +198,6 @@ private bool TryGetCacheForPEReference( return false; } - if (!(editorBrowsableInfo.Compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assemblySymbol)) - { - result = null; - return false; - } - var checksum = SymbolTreeInfo.GetMetadataChecksum(solution, peReference, cancellationToken); result = GetCacheWorker( key, @@ -216,6 +212,12 @@ private bool TryGetCacheForPEReference( } // Returns null if cache miss and forceCacheCreation == false + // + // PERF: + // Based on profiling results, initializing EditorBrowsableInfo upfront for each referenced + // project every time a completion is triggered is expensive. Making them lazy would + // eliminate this overhead when we have a cache hit while keeping it easy to share + // between original projects and PE references when trying to get completion items. private GetCacheResult? GetCacheWorker( TKey key, IAssemblySymbol assembly, @@ -223,7 +225,7 @@ private bool TryGetCacheForPEReference( SyntaxContext syntaxContext, bool forceCacheCreation, IDictionary cache, - EditorBrowsableInfo editorBrowsableInfo, + Lazy editorBrowsableInfo, CancellationToken cancellationToken) where TKey : notnull { @@ -238,7 +240,7 @@ private bool TryGetCacheForPEReference( // Cache miss, create all items only when asked. if (forceCacheCreation) { - using var builder = new CacheEntry.Builder(checksum, language, GenericTypeSuffix, editorBrowsableInfo); + using var builder = new CacheEntry.Builder(checksum, language, GenericTypeSuffix, editorBrowsableInfo.Value); GetCompletionItemsForTopLevelTypeDeclarations(assembly.GlobalNamespace, builder, cancellationToken); cacheEntry = builder.ToReferenceCacheEntry(); cache[key] = cacheEntry; @@ -398,43 +400,5 @@ private enum ItemPropertyKind : byte IsEditorBrowsableStateAdvanced = 0x8, } } - - // Things needed for determining whether a symbol is EditorBrowsable. - // Grouped together and reused within each compilation. - // - // Based on profiling results, initializing these symbols upfront for each referenced - // project every time a completion is triggered is expensive. Making them lazy would - // eliminate this overhead when we have a cache hit while keeping it easy to share - // between original projects and PE references when trying to get completion items. - private class EditorBrowsableInfo - { - private Optional? _hideModuleNameAttribute; - private Optional? _editorBrowsableAttributeConstructor; - private ImmutableArray? _typeLibTypeAttributeConstructors; - private ImmutableArray? _typeLibFuncAttributeConstructors; - private ImmutableArray? _typeLibVarAttributeConstructors; - - public Compilation Compilation { get; } - - public Optional HideModuleNameAttribute - => _hideModuleNameAttribute ??= new(Compilation.HideModuleNameAttribute()); - - public Optional EditorBrowsableAttributeConstructor - => _editorBrowsableAttributeConstructor ??= new(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(Compilation)); - - public ImmutableArray TypeLibTypeAttributeConstructors - => _typeLibTypeAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(Compilation); - - public ImmutableArray TypeLibFuncAttributeConstructors - => _typeLibFuncAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(Compilation); - - public ImmutableArray TypeLibVarAttributeConstructors - => _typeLibVarAttributeConstructors ??= EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(Compilation); - - public EditorBrowsableInfo(Compilation compilation) - { - Compilation = compilation; - } - } } } diff --git a/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs b/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs index 1a620f1133ba8..926d8225ad606 100644 --- a/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Library/ObjectBrowser/AbstractListItemFactory.cs @@ -184,10 +184,7 @@ private static void AddListItemsFromSymbols( ImmutableArray.Builder builder) where TSymbol : class, ISymbol { - var editorBrowsableAttributeConstructor = EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation); - var typeLibFuncAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(compilation); - var typeLibTypeAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(compilation); - var typeLibVarAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(compilation); + var editorBrowsableInfo = new EditorBrowsableHelpers.EditorBrowsableInfo(compilation); foreach (var symbol in symbols) { @@ -197,13 +194,7 @@ private static void AddListItemsFromSymbols( } var hideAdvancedMembers = false; - var isHidden = !symbol.IsEditorBrowsable( - hideAdvancedMembers, - compilation, - new(editorBrowsableAttributeConstructor), - typeLibFuncAttributeConstructors, - typeLibTypeAttributeConstructors, - typeLibVarAttributeConstructors); + var isHidden = !symbol.IsEditorBrowsable(hideAdvancedMembers, compilation, editorBrowsableInfo); builder.Add(listItemCreator(symbol, projectId, isHidden)); } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index 40bb96b70f948..0d2853c5634a7 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -7,8 +7,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; -using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -35,246 +33,6 @@ public static DeclarationModifiers GetSymbolModifiers(this ISymbol symbol) isSealed: symbol.IsSealed); } - /// - /// Checks a given symbol for browsability based on its declaration location, attributes - /// explicitly limiting browsability, and whether showing of advanced members is enabled. - /// The optional attribute constructor parameters may be used to specify the symbols of the - /// constructors of the various browsability limiting attributes because finding these - /// repeatedly over a large list of symbols can be slow. If providing these constructor - /// symbols, they should be in the format provided by - /// EditorBrowsableHelpers.GetSpecial*AttributeConstructor(). If these are not provided, - /// they will be found in the compilation. - /// - public static bool IsEditorBrowsable( - this ISymbol symbol, - bool hideAdvancedMembers, - Compilation compilation, - Optional editorBrowsableAttributeConstructor = default, - ImmutableArray typeLibTypeAttributeConstructors = default, - ImmutableArray typeLibFuncAttributeConstructors = default, - ImmutableArray typeLibVarAttributeConstructors = default, - Optional hideModuleNameAttribute = default) - { - return IsEditorBrowsableWithState( - symbol, - hideAdvancedMembers, - compilation, - editorBrowsableAttributeConstructor, - typeLibTypeAttributeConstructors, - typeLibFuncAttributeConstructors, - typeLibVarAttributeConstructors, - hideModuleNameAttribute).isBrowsable; - } - - // In addition to given symbol's browsability, also returns its EditorBrowsableState if it contains EditorBrowsableAttribute. - public static (bool isBrowsable, bool isEditorBrowsableStateAdvanced) IsEditorBrowsableWithState( - this ISymbol symbol, - bool hideAdvancedMembers, - Compilation compilation, - Optional editorBrowsableAttributeConstructor = default, - ImmutableArray typeLibTypeAttributeConstructors = default, - ImmutableArray typeLibFuncAttributeConstructors = default, - ImmutableArray typeLibVarAttributeConstructors = default, - Optional hideModuleNameAttribute = default) - { - // Namespaces can't have attributes, so just return true here. This also saves us a - // costly check if this namespace has any locations in source (since a merged namespace - // needs to go collect all the locations). - if (symbol.Kind == SymbolKind.Namespace) - { - return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); - } - - // check for IsImplicitlyDeclared so we don't spend time examining VB's embedded types. - // This saves a few percent in typing scenarios. An implicitly declared symbol can't - // have attributes, so it can't be hidden by them. - if (symbol.IsImplicitlyDeclared) - { - return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); - } - - // Ignore browsability limiting attributes if the symbol is declared in source. - // Check all locations since some of VB's embedded My symbols are declared in - // both source and the MyTemplateLocation. - if (symbol.Locations.All(loc => loc.IsInSource)) - { - // The HideModuleNameAttribute still applies to Modules defined in source - return (!IsBrowsingProhibitedByHideModuleNameAttribute(symbol, compilation, hideModuleNameAttribute), isEditorBrowsableStateAdvanced: false); - } - - var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibited( - symbol, - hideAdvancedMembers, - compilation, - editorBrowsableAttributeConstructor, - typeLibTypeAttributeConstructors, - typeLibFuncAttributeConstructors, - typeLibVarAttributeConstructors, - hideModuleNameAttribute); - - return (!isProhibited, isEditorBrowsableStateAdvanced); - } - - private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibited( - ISymbol symbol, - bool hideAdvancedMembers, - Compilation compilation, - Optional editorBrowsableAttributeConstructor, - ImmutableArray typeLibTypeAttributeConstructors, - ImmutableArray typeLibFuncAttributeConstructors, - ImmutableArray typeLibVarAttributeConstructors, - Optional hideModuleNameAttribute) - { - var attributes = symbol.GetAttributes(); - if (attributes.Length == 0) - { - return (isProhibited: false, isEditorBrowsableStateAdvanced: false); - } - - var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibitedByEditorBrowsableAttribute(attributes, hideAdvancedMembers, compilation, editorBrowsableAttributeConstructor); - - return ((isProhibited - || IsBrowsingProhibitedByTypeLibTypeAttribute(attributes, compilation, typeLibTypeAttributeConstructors) - || IsBrowsingProhibitedByTypeLibFuncAttribute(attributes, compilation, typeLibFuncAttributeConstructors) - || IsBrowsingProhibitedByTypeLibVarAttribute(attributes, compilation, typeLibVarAttributeConstructors) - || IsBrowsingProhibitedByHideModuleNameAttribute(symbol, compilation, hideModuleNameAttribute, attributes)), isEditorBrowsableStateAdvanced); - } - - private static bool IsBrowsingProhibitedByHideModuleNameAttribute( - ISymbol symbol, Compilation compilation, Optional hideModuleNameAttribute, ImmutableArray attributes = default) - { - if (!symbol.IsModuleType()) - { - return false; - } - - attributes = attributes.IsDefault ? symbol.GetAttributes() : attributes; - - if (!hideModuleNameAttribute.HasValue) - { - hideModuleNameAttribute = new(compilation.HideModuleNameAttribute()); - } - - foreach (var attribute in attributes) - { - if (Equals(attribute.AttributeClass, hideModuleNameAttribute.Value)) - { - return true; - } - } - - return false; - } - - private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibitedByEditorBrowsableAttribute( - ImmutableArray attributes, bool hideAdvancedMembers, Compilation compilation, Optional constructor) - { - if (!constructor.HasValue) - { - constructor = new(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation)); - } - - if (constructor.Value == null) - { - return (isProhibited: false, isEditorBrowsableStateAdvanced: false); - } - - foreach (var attribute in attributes) - { - if (Equals(attribute.AttributeConstructor, constructor.Value) && - attribute.ConstructorArguments.Length == 1 && - attribute.ConstructorArguments.First().Value is int) - { -#nullable disable // Should use unboxed value from previous 'is int' https://github.com/dotnet/roslyn/issues/39166 - var state = (EditorBrowsableState)attribute.ConstructorArguments.First().Value; -#nullable enable - - if (EditorBrowsableState.Never == state) - { - return (isProhibited: true, isEditorBrowsableStateAdvanced: false); - } - - if (EditorBrowsableState.Advanced == state) - { - return (isProhibited: hideAdvancedMembers, isEditorBrowsableStateAdvanced: true); - } - } - } - - return (isProhibited: false, isEditorBrowsableStateAdvanced: false); - } - - private static bool IsBrowsingProhibitedByTypeLibTypeAttribute( - ImmutableArray attributes, Compilation compilation, ImmutableArray constructors) - { - return IsBrowsingProhibitedByTypeLibAttributeWorker( - attributes, - constructors.IsDefault ? EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(compilation) : constructors, - TypeLibTypeFlagsFHidden); - } - - private static bool IsBrowsingProhibitedByTypeLibFuncAttribute( - ImmutableArray attributes, Compilation compilation, ImmutableArray constructors) - { - return IsBrowsingProhibitedByTypeLibAttributeWorker( - attributes, - constructors.IsDefault ? EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(compilation) : constructors, - TypeLibFuncFlagsFHidden); - } - - private static bool IsBrowsingProhibitedByTypeLibVarAttribute( - ImmutableArray attributes, Compilation compilation, ImmutableArray constructors) - { - return IsBrowsingProhibitedByTypeLibAttributeWorker( - attributes, - constructors.IsDefault ? EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(compilation) : constructors, - TypeLibVarFlagsFHidden); - } - - private const int TypeLibTypeFlagsFHidden = 0x0010; - private const int TypeLibFuncFlagsFHidden = 0x0040; - private const int TypeLibVarFlagsFHidden = 0x0040; - - private static bool IsBrowsingProhibitedByTypeLibAttributeWorker( - ImmutableArray attributes, ImmutableArray attributeConstructors, int hiddenFlag) - { - foreach (var attribute in attributes) - { - if (attribute.ConstructorArguments.Length == 1) - { - foreach (var constructor in attributeConstructors) - { - if (Equals(attribute.AttributeConstructor, constructor)) - { - // Check for both constructor signatures. The constructor that takes a TypeLib*Flags reports an int argument. - var argumentValue = attribute.ConstructorArguments.First().Value; - - int actualFlags; - if (argumentValue is int i) - { - actualFlags = i; - } - else if (argumentValue is short sh) - { - actualFlags = sh; - } - else - { - continue; - } - - if ((actualFlags & hiddenFlag) == hiddenFlag) - { - return true; - } - } - } - } - } - - return false; - } - public static DocumentationComment GetDocumentationComment(this ISymbol symbol, Compilation compilation, CultureInfo? preferredCulture = null, bool expandIncludes = false, bool expandInheritdoc = false, CancellationToken cancellationToken = default) => GetDocumentationComment(symbol, visitedSymbols: null, compilation, preferredCulture, expandIncludes, expandInheritdoc, cancellationToken); @@ -662,67 +420,5 @@ private static void CopyAnnotations(XObject source, XObject target) private static bool ElementNameIs(XElement element, string name) => string.IsNullOrEmpty(element.Name.NamespaceName) && DocumentationCommentXmlNames.ElementEquals(element.Name.LocalName, name); - - /// - /// First, remove symbols from the set if they are overridden by other symbols in the set. - /// If a symbol is overridden only by symbols outside of the set, then it is not removed. - /// This is useful for filtering out symbols that cannot be accessed in a given context due - /// to the existence of overriding members. Second, remove remaining symbols that are - /// unsupported (e.g. pointer types in VB) or not editor browsable based on the EditorBrowsable - /// attribute. - /// - public static ImmutableArray FilterToVisibleAndBrowsableSymbols( - this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol - { - symbols = symbols.RemoveOverriddenSymbolsWithinSet(); - - // Since all symbols are from the same compilation, find the required attribute - // constructors once and reuse. - - var editorBrowsableAttributeConstructor = new Optional(EditorBrowsableHelpers.GetSpecialEditorBrowsableAttributeConstructor(compilation)); - var typeLibTypeAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibTypeAttributeConstructors(compilation); - var typeLibFuncAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibFuncAttributeConstructors(compilation); - var typeLibVarAttributeConstructors = EditorBrowsableHelpers.GetSpecialTypeLibVarAttributeConstructors(compilation); - var hideModuleNameAttribute = new Optional(compilation.HideModuleNameAttribute()); - - // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first - // check to see if we're referencing a symbol defined in source. - static bool isSymbolDefinedInSource(Location l) => l.IsInSource; - return symbols.WhereAsArray((s, arg) => - (s.Locations.Any(isSymbolDefinedInSource) || !s.HasUnsupportedMetadata) && - !s.IsDestructor() && - s.IsEditorBrowsable( - arg.hideAdvancedMembers, - arg.compilation, - arg.editorBrowsableAttributeConstructor, - arg.typeLibTypeAttributeConstructors, - arg.typeLibFuncAttributeConstructors, - arg.typeLibVarAttributeConstructors, - arg.hideModuleNameAttribute), - (hideAdvancedMembers, compilation, editorBrowsableAttributeConstructor, typeLibTypeAttributeConstructors, typeLibFuncAttributeConstructors, typeLibVarAttributeConstructors, hideModuleNameAttribute)); - } - - private static ImmutableArray RemoveOverriddenSymbolsWithinSet(this ImmutableArray symbols) where T : ISymbol - { - var overriddenSymbols = new HashSet(); - - foreach (var symbol in symbols) - { - var overriddenMember = symbol.OverriddenMember(); - if (overriddenMember != null && !overriddenSymbols.Contains(overriddenMember)) - { - overriddenSymbols.Add(overriddenMember); - } - } - - return symbols.WhereAsArray(s => !overriddenSymbols.Contains(s)); - } - - public static ImmutableArray FilterToVisibleAndBrowsableSymbolsAndNotUnsafeSymbols( - this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol - { - return symbols.FilterToVisibleAndBrowsableSymbols(hideAdvancedMembers, compilation) - .WhereAsArray(s => !s.RequiresUnsafeModifier()); - } } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs b/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs index 0764138d9ea77..cbd87d89ca532 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs @@ -2,16 +2,302 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; +#nullable enable + namespace Microsoft.CodeAnalysis.Shared.Utilities { internal static class EditorBrowsableHelpers { + public struct EditorBrowsableInfo + { + public Compilation Compilation { get; } + public INamedTypeSymbol? HideModuleNameAttribute { get; } + public IMethodSymbol? EditorBrowsableAttributeConstructor { get; } + public ImmutableArray TypeLibTypeAttributeConstructors { get; } + public ImmutableArray TypeLibFuncAttributeConstructors { get; } + public ImmutableArray TypeLibVarAttributeConstructors { get; } + public bool IsDefault => Compilation == null; + + public EditorBrowsableInfo(Compilation compilation) + { + Compilation = compilation; + HideModuleNameAttribute = compilation.HideModuleNameAttribute(); + EditorBrowsableAttributeConstructor = GetSpecialEditorBrowsableAttributeConstructor(compilation); + TypeLibTypeAttributeConstructors = GetSpecialTypeLibTypeAttributeConstructors(compilation); + TypeLibFuncAttributeConstructors = GetSpecialTypeLibFuncAttributeConstructors(compilation); + TypeLibVarAttributeConstructors = GetSpecialTypeLibVarAttributeConstructors(compilation); + } + } + + /// + /// Checks a given symbol for browsability based on its declaration location, attributes + /// explicitly limiting browsability, and whether showing of advanced members is enabled. + /// The optional editorBrowsableInfo parameters may be used to specify the symbols of the + /// constructors of the various browsability limiting attributes because finding these + /// repeatedly over a large list of symbols can be slow. If these are not provided, + /// they will be found in the compilation. + /// + public static bool IsEditorBrowsable( + this ISymbol symbol, + bool hideAdvancedMembers, + Compilation compilation, + EditorBrowsableInfo editorBrowsableInfo = default) + { + return IsEditorBrowsableWithState( + symbol, + hideAdvancedMembers, + compilation, + editorBrowsableInfo).isBrowsable; + } + + // In addition to given symbol's browsability, also returns its EditorBrowsableState if it contains EditorBrowsableAttribute. + public static (bool isBrowsable, bool isEditorBrowsableStateAdvanced) IsEditorBrowsableWithState( + this ISymbol symbol, + bool hideAdvancedMembers, + Compilation compilation, + EditorBrowsableInfo editorBrowsableInfo = default) + { + // Namespaces can't have attributes, so just return true here. This also saves us a + // costly check if this namespace has any locations in source (since a merged namespace + // needs to go collect all the locations). + if (symbol.Kind == SymbolKind.Namespace) + { + return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); + } + + // check for IsImplicitlyDeclared so we don't spend time examining VB's embedded types. + // This saves a few percent in typing scenarios. An implicitly declared symbol can't + // have attributes, so it can't be hidden by them. + if (symbol.IsImplicitlyDeclared) + { + return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); + } + + if (editorBrowsableInfo.IsDefault) + { + editorBrowsableInfo = new EditorBrowsableInfo(compilation); + } + + // Ignore browsability limiting attributes if the symbol is declared in source. + // Check all locations since some of VB's embedded My symbols are declared in + // both source and the MyTemplateLocation. + if (symbol.Locations.All(loc => loc.IsInSource)) + { + // The HideModuleNameAttribute still applies to Modules defined in source + return (!IsBrowsingProhibitedByHideModuleNameAttribute(symbol, editorBrowsableInfo.HideModuleNameAttribute), isEditorBrowsableStateAdvanced: false); + } + + var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibited(symbol, hideAdvancedMembers, editorBrowsableInfo); + + return (!isProhibited, isEditorBrowsableStateAdvanced); + } + + private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibited( + ISymbol symbol, + bool hideAdvancedMembers, + EditorBrowsableInfo editorBrowsableInfo) + { + var attributes = symbol.GetAttributes(); + if (attributes.Length == 0) + { + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); + } + + var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibitedByEditorBrowsableAttribute(attributes, hideAdvancedMembers, editorBrowsableInfo.EditorBrowsableAttributeConstructor); + + return ((isProhibited + || IsBrowsingProhibitedByTypeLibTypeAttribute(attributes, editorBrowsableInfo.TypeLibTypeAttributeConstructors) + || IsBrowsingProhibitedByTypeLibFuncAttribute(attributes, editorBrowsableInfo.TypeLibFuncAttributeConstructors) + || IsBrowsingProhibitedByTypeLibVarAttribute(attributes, editorBrowsableInfo.TypeLibVarAttributeConstructors) + || IsBrowsingProhibitedByHideModuleNameAttribute(symbol, editorBrowsableInfo.HideModuleNameAttribute, attributes)), isEditorBrowsableStateAdvanced); + } + + private static bool IsBrowsingProhibitedByHideModuleNameAttribute( + ISymbol symbol, INamedTypeSymbol? hideModuleNameAttribute, ImmutableArray attributes = default) + { + if (hideModuleNameAttribute == null || !symbol.IsModuleType()) + { + return false; + } + + attributes = attributes.IsDefault ? symbol.GetAttributes() : attributes; + + foreach (var attribute in attributes) + { + if (Equals(attribute.AttributeClass, hideModuleNameAttribute)) + { + return true; + } + } + + return false; + } + + private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibitedByEditorBrowsableAttribute( + ImmutableArray attributes, bool hideAdvancedMembers, IMethodSymbol? constructor) + { + if (constructor == null) + { + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); + } + + foreach (var attribute in attributes) + { + if (Equals(attribute.AttributeConstructor, constructor) && + attribute.ConstructorArguments.Length == 1 && + attribute.ConstructorArguments.First().Value is int) + { +#nullable disable // Should use unboxed value from previous 'is int' https://github.com/dotnet/roslyn/issues/39166 + var state = (EditorBrowsableState)attribute.ConstructorArguments.First().Value; +#nullable enable + + if (EditorBrowsableState.Never == state) + { + return (isProhibited: true, isEditorBrowsableStateAdvanced: false); + } + + if (EditorBrowsableState.Advanced == state) + { + return (isProhibited: hideAdvancedMembers, isEditorBrowsableStateAdvanced: true); + } + } + } + + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); + } + + private static bool IsBrowsingProhibitedByTypeLibTypeAttribute( + ImmutableArray attributes, ImmutableArray constructors) + { + return IsBrowsingProhibitedByTypeLibAttributeWorker( + attributes, + constructors, + TypeLibTypeFlagsFHidden); + } + + private static bool IsBrowsingProhibitedByTypeLibFuncAttribute( + ImmutableArray attributes, ImmutableArray constructors) + { + return IsBrowsingProhibitedByTypeLibAttributeWorker( + attributes, + constructors, + TypeLibFuncFlagsFHidden); + } + + private static bool IsBrowsingProhibitedByTypeLibVarAttribute( + ImmutableArray attributes, ImmutableArray constructors) + { + return IsBrowsingProhibitedByTypeLibAttributeWorker( + attributes, + constructors, + TypeLibVarFlagsFHidden); + } + + private const int TypeLibTypeFlagsFHidden = 0x0010; + private const int TypeLibFuncFlagsFHidden = 0x0040; + private const int TypeLibVarFlagsFHidden = 0x0040; + + private static bool IsBrowsingProhibitedByTypeLibAttributeWorker( + ImmutableArray attributes, ImmutableArray attributeConstructors, int hiddenFlag) + { + foreach (var attribute in attributes) + { + if (attribute.ConstructorArguments.Length == 1) + { + foreach (var constructor in attributeConstructors) + { + if (Equals(attribute.AttributeConstructor, constructor)) + { + // Check for both constructor signatures. The constructor that takes a TypeLib*Flags reports an int argument. + var argumentValue = attribute.ConstructorArguments.First().Value; + + int actualFlags; + if (argumentValue is int i) + { + actualFlags = i; + } + else if (argumentValue is short sh) + { + actualFlags = sh; + } + else + { + continue; + } + + if ((actualFlags & hiddenFlag) == hiddenFlag) + { + return true; + } + } + } + } + } + + return false; + } + + /// + /// First, remove symbols from the set if they are overridden by other symbols in the set. + /// If a symbol is overridden only by symbols outside of the set, then it is not removed. + /// This is useful for filtering out symbols that cannot be accessed in a given context due + /// to the existence of overriding members. Second, remove remaining symbols that are + /// unsupported (e.g. pointer types in VB) or not editor browsable based on the EditorBrowsable + /// attribute. + /// + public static ImmutableArray FilterToVisibleAndBrowsableSymbols( + this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol + { + symbols = symbols.RemoveOverriddenSymbolsWithinSet(); + + // Since all symbols are from the same compilation, find the required attribute + // constructors once and reuse. + var editorBrowsableInfo = new EditorBrowsableInfo(compilation); + + // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first + // check to see if we're referencing a symbol defined in source. + static bool isSymbolDefinedInSource(Location l) => l.IsInSource; + return symbols.WhereAsArray((s, arg) => + (s.Locations.Any(isSymbolDefinedInSource) || !s.HasUnsupportedMetadata) && + !s.IsDestructor() && + s.IsEditorBrowsable( + arg.hideAdvancedMembers, + arg.editorBrowsableInfo.Compilation, + arg.editorBrowsableInfo), + (hideAdvancedMembers, editorBrowsableInfo)); + } + + public static ImmutableArray FilterToVisibleAndBrowsableSymbolsAndNotUnsafeSymbols( + this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol + { + return symbols.FilterToVisibleAndBrowsableSymbols(hideAdvancedMembers, compilation) + .WhereAsArray(s => !s.RequiresUnsafeModifier()); + } + + private static ImmutableArray RemoveOverriddenSymbolsWithinSet(this ImmutableArray symbols) where T : ISymbol + { + var overriddenSymbols = new HashSet(); + + foreach (var symbol in symbols) + { + var overriddenMember = symbol.OverriddenMember(); + if (overriddenMember != null && !overriddenSymbols.Contains(overriddenMember)) + { + overriddenSymbols.Add(overriddenMember); + } + } + + return symbols.WhereAsArray(s => !overriddenSymbols.Contains(s)); + } + /// /// Finds the constructor which takes exactly one argument, which must be of type EditorBrowsableState. /// It does not require that the EditorBrowsableAttribute and EditorBrowsableState types be those @@ -19,7 +305,7 @@ internal static class EditorBrowsableHelpers /// point that pattern appears to be violated, return null to indicate that an appropriate constructor /// could not be found. /// - public static IMethodSymbol GetSpecialEditorBrowsableAttributeConstructor(Compilation compilation) + public static IMethodSymbol? GetSpecialEditorBrowsableAttributeConstructor(Compilation compilation) { var editorBrowsableAttributeType = compilation.EditorBrowsableAttributeType(); var editorBrowsableStateType = compilation.EditorBrowsableStateType(); From 064b403f167636a9e6d8b773764c627363c3122d Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Thu, 24 Sep 2020 16:04:08 -0700 Subject: [PATCH 09/10] Add comments --- .../AbstractTypeImportCompletionService.CacheEntry.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index 390351bc25ac3..b9bb62e6b245b 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -74,8 +74,9 @@ public ImmutableArray GetItemsForContext( item = GetAppropriateAttributeItem(info.Item, isCaseSensitive); } - // A project with different language than when the cache entry was created for, - // then we will change the generic suffix accordingly. + // C# and VB the display text is different for generics, i.e. and (Of T). For simpllicity, we only cache for one language. + // But when we trigger in a project with different language than when the cache entry was created for, we will need to + // change the generic suffix accordingly. if (!isSameLanguage && info.IsGeneric) { // We don't want to cache this item. From 4c00f6c84c3152c6b2ed353adec69ed6902b95a6 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Thu, 24 Sep 2020 16:49:00 -0700 Subject: [PATCH 10/10] Undo code move --- ...tTypeImportCompletionService.CacheEntry.cs | 1 + .../Shared/Extensions/ISymbolExtensions.cs | 265 ++++++++++++++++++ .../Utilities/EditorBrowsableHelpers.cs | 263 ----------------- 3 files changed, 266 insertions(+), 263 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs index b9bb62e6b245b..aad1815ef3b26 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractTypeImportCompletionService.CacheEntry.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; + using static Microsoft.CodeAnalysis.Shared.Utilities.EditorBrowsableHelpers; namespace Microsoft.CodeAnalysis.Completion.Providers.ImportCompletion diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index 0d2853c5634a7..68b7c315a905e 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -7,6 +7,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; +using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -18,6 +20,8 @@ using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.Shared.Utilities.EditorBrowsableHelpers; + namespace Microsoft.CodeAnalysis.Shared.Extensions { internal static partial class ISymbolExtensions @@ -33,6 +37,214 @@ public static DeclarationModifiers GetSymbolModifiers(this ISymbol symbol) isSealed: symbol.IsSealed); } + /// + /// Checks a given symbol for browsability based on its declaration location, attributes + /// explicitly limiting browsability, and whether showing of advanced members is enabled. + /// The optional editorBrowsableInfo parameters may be used to specify the symbols of the + /// constructors of the various browsability limiting attributes because finding these + /// repeatedly over a large list of symbols can be slow. If these are not provided, + /// they will be found in the compilation. + /// + public static bool IsEditorBrowsable( + this ISymbol symbol, + bool hideAdvancedMembers, + Compilation compilation, + EditorBrowsableInfo editorBrowsableInfo = default) + { + return IsEditorBrowsableWithState( + symbol, + hideAdvancedMembers, + compilation, + editorBrowsableInfo).isBrowsable; + } + + // In addition to given symbol's browsability, also returns its EditorBrowsableState if it contains EditorBrowsableAttribute. + public static (bool isBrowsable, bool isEditorBrowsableStateAdvanced) IsEditorBrowsableWithState( + this ISymbol symbol, + bool hideAdvancedMembers, + Compilation compilation, + EditorBrowsableInfo editorBrowsableInfo = default) + { + // Namespaces can't have attributes, so just return true here. This also saves us a + // costly check if this namespace has any locations in source (since a merged namespace + // needs to go collect all the locations). + if (symbol.Kind == SymbolKind.Namespace) + { + return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); + } + + // check for IsImplicitlyDeclared so we don't spend time examining VB's embedded types. + // This saves a few percent in typing scenarios. An implicitly declared symbol can't + // have attributes, so it can't be hidden by them. + if (symbol.IsImplicitlyDeclared) + { + return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); + } + + if (editorBrowsableInfo.IsDefault) + { + editorBrowsableInfo = new EditorBrowsableInfo(compilation); + } + + // Ignore browsability limiting attributes if the symbol is declared in source. + // Check all locations since some of VB's embedded My symbols are declared in + // both source and the MyTemplateLocation. + if (symbol.Locations.All(loc => loc.IsInSource)) + { + // The HideModuleNameAttribute still applies to Modules defined in source + return (!IsBrowsingProhibitedByHideModuleNameAttribute(symbol, editorBrowsableInfo.HideModuleNameAttribute), isEditorBrowsableStateAdvanced: false); + } + + var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibited(symbol, hideAdvancedMembers, editorBrowsableInfo); + + return (!isProhibited, isEditorBrowsableStateAdvanced); + } + + private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibited( + ISymbol symbol, + bool hideAdvancedMembers, + EditorBrowsableInfo editorBrowsableInfo) + { + var attributes = symbol.GetAttributes(); + if (attributes.Length == 0) + { + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); + } + + var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibitedByEditorBrowsableAttribute(attributes, hideAdvancedMembers, editorBrowsableInfo.EditorBrowsableAttributeConstructor); + + return ((isProhibited + || IsBrowsingProhibitedByTypeLibTypeAttribute(attributes, editorBrowsableInfo.TypeLibTypeAttributeConstructors) + || IsBrowsingProhibitedByTypeLibFuncAttribute(attributes, editorBrowsableInfo.TypeLibFuncAttributeConstructors) + || IsBrowsingProhibitedByTypeLibVarAttribute(attributes, editorBrowsableInfo.TypeLibVarAttributeConstructors) + || IsBrowsingProhibitedByHideModuleNameAttribute(symbol, editorBrowsableInfo.HideModuleNameAttribute, attributes)), isEditorBrowsableStateAdvanced); + } + + private static bool IsBrowsingProhibitedByHideModuleNameAttribute( + ISymbol symbol, INamedTypeSymbol? hideModuleNameAttribute, ImmutableArray attributes = default) + { + if (hideModuleNameAttribute == null || !symbol.IsModuleType()) + { + return false; + } + + attributes = attributes.IsDefault ? symbol.GetAttributes() : attributes; + + foreach (var attribute in attributes) + { + if (Equals(attribute.AttributeClass, hideModuleNameAttribute)) + { + return true; + } + } + + return false; + } + + private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibitedByEditorBrowsableAttribute( + ImmutableArray attributes, bool hideAdvancedMembers, IMethodSymbol? constructor) + { + if (constructor == null) + { + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); + } + + foreach (var attribute in attributes) + { + if (Equals(attribute.AttributeConstructor, constructor) && + attribute.ConstructorArguments.Length == 1 && + attribute.ConstructorArguments.First().Value is int) + { +#nullable disable // Should use unboxed value from previous 'is int' https://github.com/dotnet/roslyn/issues/39166 + var state = (EditorBrowsableState)attribute.ConstructorArguments.First().Value; +#nullable enable + + if (EditorBrowsableState.Never == state) + { + return (isProhibited: true, isEditorBrowsableStateAdvanced: false); + } + + if (EditorBrowsableState.Advanced == state) + { + return (isProhibited: hideAdvancedMembers, isEditorBrowsableStateAdvanced: true); + } + } + } + + return (isProhibited: false, isEditorBrowsableStateAdvanced: false); + } + + private static bool IsBrowsingProhibitedByTypeLibTypeAttribute( + ImmutableArray attributes, ImmutableArray constructors) + { + return IsBrowsingProhibitedByTypeLibAttributeWorker( + attributes, + constructors, + TypeLibTypeFlagsFHidden); + } + + private static bool IsBrowsingProhibitedByTypeLibFuncAttribute( + ImmutableArray attributes, ImmutableArray constructors) + { + return IsBrowsingProhibitedByTypeLibAttributeWorker( + attributes, + constructors, + TypeLibFuncFlagsFHidden); + } + + private static bool IsBrowsingProhibitedByTypeLibVarAttribute( + ImmutableArray attributes, ImmutableArray constructors) + { + return IsBrowsingProhibitedByTypeLibAttributeWorker( + attributes, + constructors, + TypeLibVarFlagsFHidden); + } + + private const int TypeLibTypeFlagsFHidden = 0x0010; + private const int TypeLibFuncFlagsFHidden = 0x0040; + private const int TypeLibVarFlagsFHidden = 0x0040; + + private static bool IsBrowsingProhibitedByTypeLibAttributeWorker( + ImmutableArray attributes, ImmutableArray attributeConstructors, int hiddenFlag) + { + foreach (var attribute in attributes) + { + if (attribute.ConstructorArguments.Length == 1) + { + foreach (var constructor in attributeConstructors) + { + if (Equals(attribute.AttributeConstructor, constructor)) + { + // Check for both constructor signatures. The constructor that takes a TypeLib*Flags reports an int argument. + var argumentValue = attribute.ConstructorArguments.First().Value; + + int actualFlags; + if (argumentValue is int i) + { + actualFlags = i; + } + else if (argumentValue is short sh) + { + actualFlags = sh; + } + else + { + continue; + } + + if ((actualFlags & hiddenFlag) == hiddenFlag) + { + return true; + } + } + } + } + } + + return false; + } + public static DocumentationComment GetDocumentationComment(this ISymbol symbol, Compilation compilation, CultureInfo? preferredCulture = null, bool expandIncludes = false, bool expandInheritdoc = false, CancellationToken cancellationToken = default) => GetDocumentationComment(symbol, visitedSymbols: null, compilation, preferredCulture, expandIncludes, expandInheritdoc, cancellationToken); @@ -420,5 +632,58 @@ private static void CopyAnnotations(XObject source, XObject target) private static bool ElementNameIs(XElement element, string name) => string.IsNullOrEmpty(element.Name.NamespaceName) && DocumentationCommentXmlNames.ElementEquals(element.Name.LocalName, name); + + /// + /// First, remove symbols from the set if they are overridden by other symbols in the set. + /// If a symbol is overridden only by symbols outside of the set, then it is not removed. + /// This is useful for filtering out symbols that cannot be accessed in a given context due + /// to the existence of overriding members. Second, remove remaining symbols that are + /// unsupported (e.g. pointer types in VB) or not editor browsable based on the EditorBrowsable + /// attribute. + /// + public static ImmutableArray FilterToVisibleAndBrowsableSymbols( + this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol + { + symbols = symbols.RemoveOverriddenSymbolsWithinSet(); + + // Since all symbols are from the same compilation, find the required attribute + // constructors once and reuse. + var editorBrowsableInfo = new EditorBrowsableInfo(compilation); + + // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first + // check to see if we're referencing a symbol defined in source. + static bool isSymbolDefinedInSource(Location l) => l.IsInSource; + return symbols.WhereAsArray((s, arg) => + (s.Locations.Any(isSymbolDefinedInSource) || !s.HasUnsupportedMetadata) && + !s.IsDestructor() && + s.IsEditorBrowsable( + arg.hideAdvancedMembers, + arg.editorBrowsableInfo.Compilation, + arg.editorBrowsableInfo), + (hideAdvancedMembers, editorBrowsableInfo)); + } + + private static ImmutableArray RemoveOverriddenSymbolsWithinSet(this ImmutableArray symbols) where T : ISymbol + { + var overriddenSymbols = new HashSet(); + + foreach (var symbol in symbols) + { + var overriddenMember = symbol.OverriddenMember(); + if (overriddenMember != null && !overriddenSymbols.Contains(overriddenMember)) + { + overriddenSymbols.Add(overriddenMember); + } + } + + return symbols.WhereAsArray(s => !overriddenSymbols.Contains(s)); + } + + public static ImmutableArray FilterToVisibleAndBrowsableSymbolsAndNotUnsafeSymbols( + this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol + { + return symbols.FilterToVisibleAndBrowsableSymbols(hideAdvancedMembers, compilation) + .WhereAsArray(s => !s.RequiresUnsafeModifier()); + } } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs b/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs index cbd87d89ca532..e6706d23b4a98 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/EditorBrowsableHelpers.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; -using System.ComponentModel; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -37,267 +35,6 @@ public EditorBrowsableInfo(Compilation compilation) } } - /// - /// Checks a given symbol for browsability based on its declaration location, attributes - /// explicitly limiting browsability, and whether showing of advanced members is enabled. - /// The optional editorBrowsableInfo parameters may be used to specify the symbols of the - /// constructors of the various browsability limiting attributes because finding these - /// repeatedly over a large list of symbols can be slow. If these are not provided, - /// they will be found in the compilation. - /// - public static bool IsEditorBrowsable( - this ISymbol symbol, - bool hideAdvancedMembers, - Compilation compilation, - EditorBrowsableInfo editorBrowsableInfo = default) - { - return IsEditorBrowsableWithState( - symbol, - hideAdvancedMembers, - compilation, - editorBrowsableInfo).isBrowsable; - } - - // In addition to given symbol's browsability, also returns its EditorBrowsableState if it contains EditorBrowsableAttribute. - public static (bool isBrowsable, bool isEditorBrowsableStateAdvanced) IsEditorBrowsableWithState( - this ISymbol symbol, - bool hideAdvancedMembers, - Compilation compilation, - EditorBrowsableInfo editorBrowsableInfo = default) - { - // Namespaces can't have attributes, so just return true here. This also saves us a - // costly check if this namespace has any locations in source (since a merged namespace - // needs to go collect all the locations). - if (symbol.Kind == SymbolKind.Namespace) - { - return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); - } - - // check for IsImplicitlyDeclared so we don't spend time examining VB's embedded types. - // This saves a few percent in typing scenarios. An implicitly declared symbol can't - // have attributes, so it can't be hidden by them. - if (symbol.IsImplicitlyDeclared) - { - return (isBrowsable: true, isEditorBrowsableStateAdvanced: false); - } - - if (editorBrowsableInfo.IsDefault) - { - editorBrowsableInfo = new EditorBrowsableInfo(compilation); - } - - // Ignore browsability limiting attributes if the symbol is declared in source. - // Check all locations since some of VB's embedded My symbols are declared in - // both source and the MyTemplateLocation. - if (symbol.Locations.All(loc => loc.IsInSource)) - { - // The HideModuleNameAttribute still applies to Modules defined in source - return (!IsBrowsingProhibitedByHideModuleNameAttribute(symbol, editorBrowsableInfo.HideModuleNameAttribute), isEditorBrowsableStateAdvanced: false); - } - - var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibited(symbol, hideAdvancedMembers, editorBrowsableInfo); - - return (!isProhibited, isEditorBrowsableStateAdvanced); - } - - private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibited( - ISymbol symbol, - bool hideAdvancedMembers, - EditorBrowsableInfo editorBrowsableInfo) - { - var attributes = symbol.GetAttributes(); - if (attributes.Length == 0) - { - return (isProhibited: false, isEditorBrowsableStateAdvanced: false); - } - - var (isProhibited, isEditorBrowsableStateAdvanced) = IsBrowsingProhibitedByEditorBrowsableAttribute(attributes, hideAdvancedMembers, editorBrowsableInfo.EditorBrowsableAttributeConstructor); - - return ((isProhibited - || IsBrowsingProhibitedByTypeLibTypeAttribute(attributes, editorBrowsableInfo.TypeLibTypeAttributeConstructors) - || IsBrowsingProhibitedByTypeLibFuncAttribute(attributes, editorBrowsableInfo.TypeLibFuncAttributeConstructors) - || IsBrowsingProhibitedByTypeLibVarAttribute(attributes, editorBrowsableInfo.TypeLibVarAttributeConstructors) - || IsBrowsingProhibitedByHideModuleNameAttribute(symbol, editorBrowsableInfo.HideModuleNameAttribute, attributes)), isEditorBrowsableStateAdvanced); - } - - private static bool IsBrowsingProhibitedByHideModuleNameAttribute( - ISymbol symbol, INamedTypeSymbol? hideModuleNameAttribute, ImmutableArray attributes = default) - { - if (hideModuleNameAttribute == null || !symbol.IsModuleType()) - { - return false; - } - - attributes = attributes.IsDefault ? symbol.GetAttributes() : attributes; - - foreach (var attribute in attributes) - { - if (Equals(attribute.AttributeClass, hideModuleNameAttribute)) - { - return true; - } - } - - return false; - } - - private static (bool isProhibited, bool isEditorBrowsableStateAdvanced) IsBrowsingProhibitedByEditorBrowsableAttribute( - ImmutableArray attributes, bool hideAdvancedMembers, IMethodSymbol? constructor) - { - if (constructor == null) - { - return (isProhibited: false, isEditorBrowsableStateAdvanced: false); - } - - foreach (var attribute in attributes) - { - if (Equals(attribute.AttributeConstructor, constructor) && - attribute.ConstructorArguments.Length == 1 && - attribute.ConstructorArguments.First().Value is int) - { -#nullable disable // Should use unboxed value from previous 'is int' https://github.com/dotnet/roslyn/issues/39166 - var state = (EditorBrowsableState)attribute.ConstructorArguments.First().Value; -#nullable enable - - if (EditorBrowsableState.Never == state) - { - return (isProhibited: true, isEditorBrowsableStateAdvanced: false); - } - - if (EditorBrowsableState.Advanced == state) - { - return (isProhibited: hideAdvancedMembers, isEditorBrowsableStateAdvanced: true); - } - } - } - - return (isProhibited: false, isEditorBrowsableStateAdvanced: false); - } - - private static bool IsBrowsingProhibitedByTypeLibTypeAttribute( - ImmutableArray attributes, ImmutableArray constructors) - { - return IsBrowsingProhibitedByTypeLibAttributeWorker( - attributes, - constructors, - TypeLibTypeFlagsFHidden); - } - - private static bool IsBrowsingProhibitedByTypeLibFuncAttribute( - ImmutableArray attributes, ImmutableArray constructors) - { - return IsBrowsingProhibitedByTypeLibAttributeWorker( - attributes, - constructors, - TypeLibFuncFlagsFHidden); - } - - private static bool IsBrowsingProhibitedByTypeLibVarAttribute( - ImmutableArray attributes, ImmutableArray constructors) - { - return IsBrowsingProhibitedByTypeLibAttributeWorker( - attributes, - constructors, - TypeLibVarFlagsFHidden); - } - - private const int TypeLibTypeFlagsFHidden = 0x0010; - private const int TypeLibFuncFlagsFHidden = 0x0040; - private const int TypeLibVarFlagsFHidden = 0x0040; - - private static bool IsBrowsingProhibitedByTypeLibAttributeWorker( - ImmutableArray attributes, ImmutableArray attributeConstructors, int hiddenFlag) - { - foreach (var attribute in attributes) - { - if (attribute.ConstructorArguments.Length == 1) - { - foreach (var constructor in attributeConstructors) - { - if (Equals(attribute.AttributeConstructor, constructor)) - { - // Check for both constructor signatures. The constructor that takes a TypeLib*Flags reports an int argument. - var argumentValue = attribute.ConstructorArguments.First().Value; - - int actualFlags; - if (argumentValue is int i) - { - actualFlags = i; - } - else if (argumentValue is short sh) - { - actualFlags = sh; - } - else - { - continue; - } - - if ((actualFlags & hiddenFlag) == hiddenFlag) - { - return true; - } - } - } - } - } - - return false; - } - - /// - /// First, remove symbols from the set if they are overridden by other symbols in the set. - /// If a symbol is overridden only by symbols outside of the set, then it is not removed. - /// This is useful for filtering out symbols that cannot be accessed in a given context due - /// to the existence of overriding members. Second, remove remaining symbols that are - /// unsupported (e.g. pointer types in VB) or not editor browsable based on the EditorBrowsable - /// attribute. - /// - public static ImmutableArray FilterToVisibleAndBrowsableSymbols( - this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol - { - symbols = symbols.RemoveOverriddenSymbolsWithinSet(); - - // Since all symbols are from the same compilation, find the required attribute - // constructors once and reuse. - var editorBrowsableInfo = new EditorBrowsableInfo(compilation); - - // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first - // check to see if we're referencing a symbol defined in source. - static bool isSymbolDefinedInSource(Location l) => l.IsInSource; - return symbols.WhereAsArray((s, arg) => - (s.Locations.Any(isSymbolDefinedInSource) || !s.HasUnsupportedMetadata) && - !s.IsDestructor() && - s.IsEditorBrowsable( - arg.hideAdvancedMembers, - arg.editorBrowsableInfo.Compilation, - arg.editorBrowsableInfo), - (hideAdvancedMembers, editorBrowsableInfo)); - } - - public static ImmutableArray FilterToVisibleAndBrowsableSymbolsAndNotUnsafeSymbols( - this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol - { - return symbols.FilterToVisibleAndBrowsableSymbols(hideAdvancedMembers, compilation) - .WhereAsArray(s => !s.RequiresUnsafeModifier()); - } - - private static ImmutableArray RemoveOverriddenSymbolsWithinSet(this ImmutableArray symbols) where T : ISymbol - { - var overriddenSymbols = new HashSet(); - - foreach (var symbol in symbols) - { - var overriddenMember = symbol.OverriddenMember(); - if (overriddenMember != null && !overriddenSymbols.Contains(overriddenMember)) - { - overriddenSymbols.Add(overriddenMember); - } - } - - return symbols.WhereAsArray(s => !overriddenSymbols.Contains(s)); - } - /// /// Finds the constructor which takes exactly one argument, which must be of type EditorBrowsableState. /// It does not require that the EditorBrowsableAttribute and EditorBrowsableState types be those