File: Shared\Utilities\EditorBrowsableHelpers.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// 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.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Utilities;
 
internal static class EditorBrowsableHelpers
{
    public readonly struct EditorBrowsableInfo(Compilation compilation)
    {
        public Compilation Compilation { get; } = compilation;
        public INamedTypeSymbol? HideModuleNameAttribute { get; } = compilation.HideModuleNameAttribute();
        public IMethodSymbol? EditorBrowsableAttributeConstructor { get; } = GetSpecialEditorBrowsableAttributeConstructor(compilation);
        public ImmutableArray<IMethodSymbol> TypeLibTypeAttributeConstructors { get; } = GetSpecialTypeLibTypeAttributeConstructors(compilation);
        public ImmutableArray<IMethodSymbol> TypeLibFuncAttributeConstructors { get; } = GetSpecialTypeLibFuncAttributeConstructors(compilation);
        public ImmutableArray<IMethodSymbol> TypeLibVarAttributeConstructors { get; } = GetSpecialTypeLibVarAttributeConstructors(compilation);
        public bool IsDefault => Compilation == null;
    }
 
    /// <summary>
    /// 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
    /// shipped by Microsoft, but it does demand the types found follow the expected pattern. If at any
    /// point that pattern appears to be violated, return null to indicate that an appropriate constructor
    /// could not be found.
    /// </summary>
    public static IMethodSymbol? GetSpecialEditorBrowsableAttributeConstructor(Compilation compilation)
    {
        var editorBrowsableAttributeType = compilation.EditorBrowsableAttributeType();
        var editorBrowsableStateType = compilation.EditorBrowsableStateType();
 
        if (editorBrowsableAttributeType == null || editorBrowsableStateType == null)
        {
            return null;
        }
 
        var candidateConstructors = editorBrowsableAttributeType.Constructors
                                                                .Where(c => c.Parameters.Length == 1 && Equals(c.Parameters[0].Type, editorBrowsableStateType));
 
        // Ensure the constructor adheres to the expected EditorBrowsable pattern
        candidateConstructors = candidateConstructors.Where(c => (!c.IsVararg &&
                                                                  !c.Parameters[0].IsRefOrOut() &&
                                                                  !c.Parameters[0].CustomModifiers.Any()));
 
        // If there are multiple constructors that look correct then the discovered types do not match the
        // expected pattern, so return null.
        if (candidateConstructors.Count() <= 1)
        {
            return candidateConstructors.FirstOrDefault();
        }
        else
        {
            return null;
        }
    }
 
    public static ImmutableArray<IMethodSymbol> GetSpecialTypeLibTypeAttributeConstructors(Compilation compilation)
    {
        return GetSpecialTypeLibAttributeConstructorsWorker(
            compilation,
            "System.Runtime.InteropServices.TypeLibTypeAttribute",
            "System.Runtime.InteropServices.TypeLibTypeFlags");
    }
 
    public static ImmutableArray<IMethodSymbol> GetSpecialTypeLibFuncAttributeConstructors(Compilation compilation)
    {
        return GetSpecialTypeLibAttributeConstructorsWorker(
            compilation,
            "System.Runtime.InteropServices.TypeLibFuncAttribute",
            "System.Runtime.InteropServices.TypeLibFuncFlags");
    }
 
    public static ImmutableArray<IMethodSymbol> GetSpecialTypeLibVarAttributeConstructors(Compilation compilation)
    {
        return GetSpecialTypeLibAttributeConstructorsWorker(
            compilation,
            "System.Runtime.InteropServices.TypeLibVarAttribute",
            "System.Runtime.InteropServices.TypeLibVarFlags");
    }
 
    /// <summary>
    /// The TypeLib*Attribute classes that accept TypeLib*Flags with FHidden as an option all have two constructors,
    /// one accepting a TypeLib*Flags and the other a short. This methods gets those two constructor symbols for any
    /// of these attribute classes. It does not require that the either of these types be those shipped by Microsoft,
    /// 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.
    /// </summary>
    private static ImmutableArray<IMethodSymbol> GetSpecialTypeLibAttributeConstructorsWorker(
        Compilation compilation,
        string attributeMetadataName,
        string flagsMetadataName)
    {
        var typeLibAttributeType = compilation.GetTypeByMetadataName(attributeMetadataName);
        var typeLibFlagsType = compilation.GetTypeByMetadataName(flagsMetadataName);
        var shortType = compilation.GetSpecialType(SpecialType.System_Int16);
 
        if (typeLibAttributeType == null || typeLibFlagsType == null || shortType == null)
        {
            return [];
        }
 
        var candidateConstructors = typeLibAttributeType.Constructors
                                                        .Where(c => c.Parameters.Length == 1 &&
                                                                    (Equals(c.Parameters[0].Type, typeLibFlagsType) || Equals(c.Parameters[0].Type, shortType)));
 
        candidateConstructors = candidateConstructors.Where(c => (!c.IsVararg &&
                                                                  !c.Parameters[0].IsRefOrOut() &&
                                                                  !c.Parameters[0].CustomModifiers.Any()));
 
        return candidateConstructors.ToImmutableArrayOrEmpty();
    }
}