File: ObjectBrowser\DescriptionBuilder.cs
Web Access
Project: src\src\VisualStudio\CSharp\Impl\Microsoft.VisualStudio.LanguageServices.CSharp_c2g5lz1t_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices.CSharp)
// 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.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser;
using Microsoft.VisualStudio.Shell.Interop;
 
namespace Microsoft.VisualStudio.LanguageServices.CSharp.ObjectBrowser
{
    internal class DescriptionBuilder : AbstractDescriptionBuilder
    {
        public DescriptionBuilder(
            IVsObjectBrowserDescription3 description,
            ObjectBrowserLibraryManager libraryManager,
            ObjectListItem listItem,
            Project project)
            : base(description, libraryManager, listItem, project)
        {
        }
 
        protected override void BuildNamespaceDeclaration(INamespaceSymbol namespaceSymbol, _VSOBJDESCOPTIONS options)
        {
            AddText("namespace ");
            AddName(namespaceSymbol.ToDisplayString());
        }
 
        protected override void BuildDelegateDeclaration(INamedTypeSymbol typeSymbol, _VSOBJDESCOPTIONS options)
        {
            Debug.Assert(typeSymbol.TypeKind == TypeKind.Delegate);
 
            BuildTypeModifiers(typeSymbol);
            AddText("delegate ");
 
            var delegateInvokeMethod = typeSymbol.DelegateInvokeMethod;
 
            AddTypeLink(delegateInvokeMethod.ReturnType, LinkFlags.None);
            AddText(" ");
 
            var typeQualificationStyle = (options & _VSOBJDESCOPTIONS.ODO_USEFULLNAME) != 0
                ? SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces
                : SymbolDisplayTypeQualificationStyle.NameOnly;
 
            var typeNameFormat = new SymbolDisplayFormat(
                typeQualificationStyle: typeQualificationStyle,
                genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance);
 
            AddName(typeSymbol.ToDisplayString(typeNameFormat));
 
            AddText("(");
            BuildParameterList(delegateInvokeMethod.Parameters);
            AddText(")");
 
            if (typeSymbol.IsGenericType)
            {
                BuildGenericConstraints(typeSymbol);
            }
        }
 
        protected override void BuildTypeDeclaration(INamedTypeSymbol typeSymbol, _VSOBJDESCOPTIONS options)
        {
            BuildTypeModifiers(typeSymbol);
 
            switch (typeSymbol.TypeKind)
            {
                case TypeKind.Enum:
                    AddText("enum ");
                    break;
 
                case TypeKind.Struct:
                    AddText("struct ");
                    break;
 
                case TypeKind.Interface:
                    AddText("interface ");
                    break;
 
                case TypeKind.Class:
                    AddText("class ");
                    break;
 
                default:
                    Debug.Fail("Invalid type kind encountered: " + typeSymbol.TypeKind.ToString());
                    break;
            }
 
            var typeNameFormat = new SymbolDisplayFormat(
                genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance);
 
            AddName(typeSymbol.ToDisplayString(typeNameFormat));
 
            if (typeSymbol.TypeKind == TypeKind.Enum)
            {
                var underlyingType = typeSymbol.EnumUnderlyingType;
                if (underlyingType != null)
                {
                    if (underlyingType.SpecialType != SpecialType.System_Int32)
                    {
                        AddText(" : ");
                        AddTypeLink(underlyingType, LinkFlags.None);
                    }
                }
            }
            else
            {
                var baseType = typeSymbol.BaseType;
                if (baseType != null)
                {
                    if (baseType.SpecialType is not SpecialType.System_Object and
                        not SpecialType.System_Delegate and
                        not SpecialType.System_MulticastDelegate and
                        not SpecialType.System_Enum and
                        not SpecialType.System_ValueType)
                    {
                        AddText(" : ");
                        AddTypeLink(baseType, LinkFlags.None);
                    }
                }
            }
 
            if (typeSymbol.IsGenericType)
            {
                BuildGenericConstraints(typeSymbol);
            }
        }
 
        private void BuildAccessibility(ISymbol symbol)
        {
            switch (symbol.DeclaredAccessibility)
            {
                case Accessibility.Public:
                    AddText("public ");
                    break;
 
                case Accessibility.Private:
                    AddText("private ");
                    break;
 
                case Accessibility.Protected:
                    AddText("protected ");
                    break;
 
                case Accessibility.Internal:
                    AddText("internal ");
                    break;
 
                case Accessibility.ProtectedOrInternal:
                    AddText("protected internal ");
                    break;
 
                case Accessibility.ProtectedAndInternal:
                    AddText("private protected ");
                    break;
 
                default:
                    AddText("internal ");
                    break;
            }
        }
 
        private void BuildTypeModifiers(INamedTypeSymbol typeSymbol)
        {
            BuildAccessibility(typeSymbol);
 
            if (typeSymbol.IsStatic)
            {
                AddText("static ");
            }
 
            if (typeSymbol.IsAbstract &&
                typeSymbol.TypeKind != TypeKind.Interface)
            {
                AddText("abstract ");
            }
 
            if (typeSymbol.IsSealed &&
                typeSymbol.TypeKind != TypeKind.Struct &&
                typeSymbol.TypeKind != TypeKind.Enum &&
                typeSymbol.TypeKind != TypeKind.Delegate)
            {
                AddText("sealed ");
            }
        }
 
        protected override void BuildMethodDeclaration(IMethodSymbol methodSymbol, _VSOBJDESCOPTIONS options)
        {
            BuildMemberModifiers(methodSymbol);
 
            if (methodSymbol.MethodKind is not MethodKind.Constructor and
                not MethodKind.Destructor and
                not MethodKind.StaticConstructor and
                not MethodKind.Conversion)
            {
                AddTypeLink(methodSymbol.ReturnType, LinkFlags.None);
                AddText(" ");
            }
 
            if (methodSymbol.MethodKind == MethodKind.Conversion)
            {
                switch (methodSymbol.Name)
                {
                    case WellKnownMemberNames.ImplicitConversionName:
                        AddName("implicit operator ");
                        break;
 
                    case WellKnownMemberNames.ExplicitConversionName:
                        AddName("explicit operator ");
                        break;
                }
 
                AddTypeLink(methodSymbol.ReturnType, LinkFlags.None);
            }
            else
            {
                var methodNameFormat = new SymbolDisplayFormat(
                    genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance);
 
                AddName(methodSymbol.ToDisplayString(methodNameFormat));
            }
 
            AddText("(");
 
            if (methodSymbol.IsExtensionMethod)
            {
                AddText("this ");
            }
 
            BuildParameterList(methodSymbol.Parameters);
            AddText(")");
 
            if (methodSymbol.IsGenericMethod)
            {
                BuildGenericConstraints(methodSymbol);
            }
        }
 
        private void BuildMemberModifiers(ISymbol memberSymbol)
        {
            if (memberSymbol.ContainingType != null && memberSymbol.ContainingType.TypeKind == TypeKind.Interface)
            {
                return;
            }
 
            var methodSymbol = memberSymbol as IMethodSymbol;
            var fieldSymbol = memberSymbol as IFieldSymbol;
 
            if (methodSymbol != null &&
                methodSymbol.MethodKind == MethodKind.Destructor)
            {
                return;
            }
 
            if (fieldSymbol != null &&
                fieldSymbol.ContainingType.TypeKind == TypeKind.Enum)
            {
                return;
            }
 
            // TODO: 'new' modifier isn't exposed on symbols. Do we need it?
 
            // Note: we don't display the access modifier for static constructors
            if (methodSymbol == null ||
                methodSymbol.MethodKind != MethodKind.StaticConstructor)
            {
                BuildAccessibility(memberSymbol);
            }
 
            if (memberSymbol.RequiresUnsafeModifier())
            {
                AddText("unsafe ");
            }
 
            // Note: we don't display 'static' for constant fields
            if (memberSymbol.IsStatic &&
                (fieldSymbol == null || !fieldSymbol.IsConst))
            {
                AddText("static ");
            }
 
            if (memberSymbol.IsExtern)
            {
                AddText("extern ");
            }
 
            if (fieldSymbol != null && fieldSymbol.IsReadOnly)
            {
                AddText("readonly ");
            }
 
            if (fieldSymbol != null && fieldSymbol.IsConst)
            {
                AddText("const ");
            }
 
            if (fieldSymbol != null && fieldSymbol.IsVolatile)
            {
                AddText("volatile ");
            }
 
            if (memberSymbol.IsAbstract)
            {
                AddText("abstract ");
            }
            else if (memberSymbol.IsOverride)
            {
                if (memberSymbol.IsSealed)
                {
                    AddText("sealed ");
                }
 
                AddText("override ");
            }
            else if (memberSymbol.IsVirtual)
            {
                AddText("virtual ");
            }
        }
 
        private void BuildGenericConstraints(INamedTypeSymbol typeSymbol)
        {
            foreach (var typeParameterSymbol in typeSymbol.TypeParameters)
            {
                BuildConstraints(typeParameterSymbol);
            }
        }
 
        private void BuildGenericConstraints(IMethodSymbol methodSymbol)
        {
            foreach (var typeParameterSymbol in methodSymbol.TypeParameters)
            {
                BuildConstraints(typeParameterSymbol);
            }
        }
 
        private void BuildConstraints(ITypeParameterSymbol typeParameterSymbol)
        {
            if (typeParameterSymbol.ConstraintTypes.Length == 0 &&
                !typeParameterSymbol.HasConstructorConstraint &&
                !typeParameterSymbol.HasReferenceTypeConstraint &&
                !typeParameterSymbol.HasValueTypeConstraint &&
                !typeParameterSymbol.AllowsRefLikeType)
            {
                return;
            }
 
            AddLineBreak();
            AddText("\t");
            AddText("where ");
            AddName(typeParameterSymbol.Name);
            AddText(" : ");
 
            var isFirst = true;
 
            if (typeParameterSymbol.HasReferenceTypeConstraint)
            {
                if (!isFirst)
                {
                    AddComma();
                }
 
                AddText("class");
                isFirst = false;
            }
 
            if (typeParameterSymbol.HasValueTypeConstraint)
            {
                if (!isFirst)
                {
                    AddComma();
                }
 
                AddText("struct");
                isFirst = false;
            }
 
            foreach (var constraintType in typeParameterSymbol.ConstraintTypes)
            {
                if (!isFirst)
                {
                    AddComma();
                }
 
                AddTypeLink(constraintType, LinkFlags.None);
                isFirst = false;
            }
 
            if (typeParameterSymbol.HasConstructorConstraint)
            {
                if (!isFirst)
                {
                    AddComma();
                }
 
                AddText("new()");
                isFirst = false;
            }
 
            if (typeParameterSymbol.AllowsRefLikeType)
            {
                if (!isFirst)
                {
                    AddComma();
                }
 
                AddText("allows ref struct");
            }
        }
 
        private void BuildParameterList(ImmutableArray<IParameterSymbol> parameters)
        {
            var count = parameters.Length;
            if (count == 0)
            {
                return;
            }
 
            for (var i = 0; i < count; i++)
            {
                if (i > 0)
                {
                    AddComma();
                }
 
                var current = parameters[i];
 
                if (current.IsOptional)
                {
                    AddText("[");
                }
 
                if (current.RefKind == RefKind.Ref)
                {
                    AddText("ref ");
                }
                else if (current.RefKind == RefKind.Out)
                {
                    AddText("out ");
                }
 
                if (current.IsParams)
                {
                    AddText("params ");
                }
 
                AddTypeLink(current.Type, LinkFlags.None);
                AddText(" ");
                AddParam(current.Name);
 
                if (current.HasExplicitDefaultValue)
                {
                    AddText(" = ");
                    if (current.ExplicitDefaultValue == null)
                    {
                        AddText("null");
                    }
                    else
                    {
                        AddText(current.ExplicitDefaultValue.ToString());
                    }
                }
 
                if (current.IsOptional)
                {
                    AddText("]");
                }
            }
        }
 
        protected override void BuildFieldDeclaration(IFieldSymbol fieldSymbol, _VSOBJDESCOPTIONS options)
        {
            BuildMemberModifiers(fieldSymbol);
 
            if (fieldSymbol.ContainingType.TypeKind != TypeKind.Enum)
            {
                AddTypeLink(fieldSymbol.Type, LinkFlags.None);
                AddText(" ");
            }
 
            AddName(fieldSymbol.Name);
        }
 
        protected override void BuildPropertyDeclaration(IPropertySymbol propertySymbol, _VSOBJDESCOPTIONS options)
        {
            BuildMemberModifiers(propertySymbol);
 
            AddTypeLink(propertySymbol.Type, LinkFlags.None);
            AddText(" ");
 
            if (propertySymbol.IsIndexer)
            {
                AddName("this");
                AddText("[");
                BuildParameterList(propertySymbol.Parameters);
                AddText("]");
            }
            else
            {
                AddName(propertySymbol.Name);
            }
 
            AddText(" { ");
 
            if (propertySymbol.GetMethod != null)
            {
                if (propertySymbol.GetMethod.DeclaredAccessibility != propertySymbol.DeclaredAccessibility)
                {
                    BuildAccessibility(propertySymbol.GetMethod);
                }
 
                AddText("get; ");
            }
 
            if (propertySymbol.SetMethod != null)
            {
                if (propertySymbol.SetMethod.DeclaredAccessibility != propertySymbol.DeclaredAccessibility)
                {
                    BuildAccessibility(propertySymbol.SetMethod);
                }
 
                AddText("set; ");
            }
 
            AddText("}");
        }
 
        protected override void BuildEventDeclaration(IEventSymbol eventSymbol, _VSOBJDESCOPTIONS options)
        {
            BuildMemberModifiers(eventSymbol);
 
            AddText("event ");
 
            AddTypeLink(eventSymbol.Type, LinkFlags.None);
            AddText(" ");
 
            AddName(eventSymbol.Name);
        }
    }
}