File: ObjectBrowser\DescriptionBuilder.vb
Web Access
Project: src\src\VisualStudio\VisualBasic\Impl\Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj (Microsoft.VisualStudio.LanguageServices.VisualBasic)
' 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.
 
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis
Imports Microsoft.VisualStudio.Shell.Interop
Imports Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser
 
Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ObjectBrowser
    Friend Class DescriptionBuilder
        Inherits AbstractDescriptionBuilder
 
        Public Sub New(
            description As IVsObjectBrowserDescription3,
            libraryManager As ObjectBrowserLibraryManager,
            listItem As ObjectListItem,
            project As Project
        )
 
            MyBase.New(description, libraryManager, listItem, project)
        End Sub
 
        Protected Overrides Sub BuildNamespaceDeclaration(namespaceSymbol As INamespaceSymbol, options As _VSOBJDESCOPTIONS)
            AddText("Namespace ")
            AddName(namespaceSymbol.ToDisplayString())
        End Sub
 
        Protected Overrides Sub BuildDelegateDeclaration(typeSymbol As INamedTypeSymbol, options As _VSOBJDESCOPTIONS)
            Debug.Assert(typeSymbol.TypeKind = TypeKind.Delegate)
 
            BuildTypeModifiers(typeSymbol)
            AddText("Delegate ")
 
            Dim delegateInvokeMethod = typeSymbol.DelegateInvokeMethod
            If delegateInvokeMethod.ReturnsVoid Then
                AddText("Sub ")
            Else
                AddText("Function ")
            End If
 
            Dim typeNameFormat = New SymbolDisplayFormat(
                typeQualificationStyle:=SymbolDisplayTypeQualificationStyle.NameOnly,
                genericsOptions:=SymbolDisplayGenericsOptions.None)
 
            AddName(typeSymbol.ToDisplayString(typeNameFormat))
 
            If typeSymbol.TypeParameters.Length > 0 Then
                AddText("(Of ")
                BuildTypeParameterList(typeSymbol.TypeParameters)
                AddText(")")
            End If
 
            AddText("(")
            BuildParameterList(delegateInvokeMethod.Parameters)
            AddText(")")
 
            If Not delegateInvokeMethod.ReturnsVoid Then
                AddText(" As ")
                AddTypeLink(delegateInvokeMethod.ReturnType, LinkFlags.None)
            End If
        End Sub
 
        Protected Overrides Sub BuildTypeDeclaration(typeSymbol As INamedTypeSymbol, options As _VSOBJDESCOPTIONS)
            BuildTypeModifiers(typeSymbol)
 
            Select Case typeSymbol.TypeKind
                Case TypeKind.Class
                    AddText("Class ")
                Case TypeKind.Interface
                    AddText("Interface ")
                Case TypeKind.Module
                    AddText("Module ")
                Case TypeKind.Structure
                    AddText("Structure ")
                Case TypeKind.Enum
                    AddText("Enum ")
                Case Else
                    Debug.Fail("Invalid type kind encountered: " & typeSymbol.TypeKind.ToString())
            End Select
 
            Dim typeNameFormat = New SymbolDisplayFormat(
                typeQualificationStyle:=SymbolDisplayTypeQualificationStyle.NameOnly,
                genericsOptions:=SymbolDisplayGenericsOptions.None)
 
            AddName(typeSymbol.ToDisplayString(typeNameFormat))
 
            If typeSymbol.TypeParameters.Length > 0 Then
                AddText("(Of ")
                BuildTypeParameterList(typeSymbol.TypeParameters)
                AddText(")")
            End If
 
            If typeSymbol.TypeKind = TypeKind.Class Then
                Dim baseType = typeSymbol.BaseType
                If baseType IsNot Nothing Then
                    AddLineBreak()
                    AddIndent()
                    AddIndent()
                    AddText("Inherits ")
                    AddTypeLink(baseType, LinkFlags.None)
                End If
            End If
 
            If typeSymbol.TypeKind = TypeKind.Enum Then
                Dim underlyingType = typeSymbol.EnumUnderlyingType
                If underlyingType IsNot Nothing AndAlso underlyingType.SpecialType <> SpecialType.System_Int32 Then
                    AddText(" As ")
                    AddTypeLink(underlyingType, LinkFlags.None)
                End If
            End If
        End Sub
 
        Protected Overrides Sub BuildMethodDeclaration(methodSymbol As IMethodSymbol, options As _VSOBJDESCOPTIONS)
            Select Case methodSymbol.MethodKind
                Case MethodKind.Conversion, MethodKind.UserDefinedOperator
                    BuildOperatorDeclaration(methodSymbol)
                Case MethodKind.DeclareMethod
                    BuildDeclareMethodDeclaration(methodSymbol)
                Case Else
                    BuildRegularMethodDeclaration(methodSymbol)
            End Select
        End Sub
 
        Private Sub BuildOperatorDeclaration(methodSymbol As IMethodSymbol)
            BuildMemberModifiers(methodSymbol)
 
            Select Case methodSymbol.Name
                Case WellKnownMemberNames.ImplicitConversionName
                    AddText("Widening ")
                Case WellKnownMemberNames.ExplicitConversionName
                    AddText("Narrowing ")
            End Select
 
            AddText("Operator ")
 
            Dim methodNameFormat = New SymbolDisplayFormat()
            AddName(methodSymbol.ToDisplayString(methodNameFormat))
 
            AddText("(")
            BuildParameterList(methodSymbol.Parameters)
            AddText(")")
 
            If Not methodSymbol.ReturnsVoid Then
                AddText(" As ")
                AddTypeLink(methodSymbol.ReturnType, LinkFlags.None)
            End If
        End Sub
 
        Private Sub BuildDeclareMethodDeclaration(methodSymbol As IMethodSymbol)
            BuildMemberModifiers(methodSymbol)
 
            AddText("Declare ")
 
            Dim dllImportData = methodSymbol.GetDllImportData()
 
            Select Case dllImportData.CharacterSet
                Case System.Runtime.InteropServices.CharSet.Ansi
                    AddText("Ansi ")
                Case System.Runtime.InteropServices.CharSet.Auto
                    AddText("Auto ")
                Case System.Runtime.InteropServices.CharSet.Unicode
                    AddText("Unicode ")
            End Select
 
            If methodSymbol.ReturnsVoid Then
                AddText("Sub ")
            Else
                AddText("Function ")
            End If
 
            Dim methodNameFormat = New SymbolDisplayFormat()
            AddName(methodSymbol.ToDisplayString(methodNameFormat))
 
            If dllImportData.ModuleName IsNot Nothing Then
                AddText(" Lib """)
                AddText(dllImportData.ModuleName)
                AddText("""")
            End If
 
            If dllImportData.EntryPointName IsNot Nothing Then
                AddText(" Alias """)
                AddText(dllImportData.EntryPointName)
                AddText("""")
            End If
 
            AddText("(")
            BuildParameterList(methodSymbol.Parameters)
            AddText(")")
 
            If Not methodSymbol.ReturnsVoid Then
                AddText(" As ")
                AddTypeLink(methodSymbol.ReturnType, LinkFlags.None)
            End If
        End Sub
 
        Private Sub BuildRegularMethodDeclaration(methodSymbol As IMethodSymbol)
            BuildMemberModifiers(methodSymbol)
 
            If methodSymbol.ReturnsVoid Then
                AddText("Sub ")
            Else
                AddText("Function ")
            End If
 
            Dim methodNameFormat = New SymbolDisplayFormat()
            AddName(methodSymbol.ToDisplayString(methodNameFormat))
 
            If methodSymbol.TypeParameters.Length > 0 Then
                AddText("(Of ")
                BuildTypeParameterList(methodSymbol.TypeParameters)
                AddText(")")
            End If
 
            AddText("(")
            BuildParameterList(methodSymbol.Parameters)
            AddText(")")
 
            If Not methodSymbol.ReturnsVoid Then
                AddText(" As ")
                AddTypeLink(methodSymbol.ReturnType, LinkFlags.None)
            End If
        End Sub
 
        Protected Overrides Sub BuildFieldDeclaration(fieldSymbol As IFieldSymbol, options As _VSOBJDESCOPTIONS)
            BuildMemberModifiers(fieldSymbol)
 
            AddText(fieldSymbol.Name)
 
            AddText(" As ")
            AddTypeLink(fieldSymbol.Type, LinkFlags.None)
 
            If fieldSymbol.HasConstantValue Then
                AddText(" = ")
 
                If fieldSymbol.ConstantValue Is Nothing Then
                    AddText("Nothing")
                Else
                    AddText(fieldSymbol.ConstantValue.ToString())
                End If
            End If
        End Sub
 
        Protected Overrides Sub BuildPropertyDeclaration(propertySymbol As IPropertySymbol, options As _VSOBJDESCOPTIONS)
            BuildMemberModifiers(propertySymbol)
 
            If propertySymbol.GetMethod IsNot Nothing Then
                If propertySymbol.SetMethod Is Nothing Then
                    AddText("ReadOnly ")
                End If
            ElseIf propertySymbol.SetMethod IsNot Nothing Then
                AddText("WriteOnly ")
            End If
 
            AddText("Property ")
 
            AddName(propertySymbol.Name)
 
            If propertySymbol.Parameters.Length > 0 Then
                AddText("(")
                BuildParameterList(propertySymbol.Parameters)
                AddText(")")
            End If
 
            AddText(" As ")
            AddTypeLink(propertySymbol.Type, LinkFlags.None)
        End Sub
 
        Protected Overrides Sub BuildEventDeclaration(eventSymbol As IEventSymbol, options As _VSOBJDESCOPTIONS)
            BuildMemberModifiers(eventSymbol)
 
            AddText("Event ")
 
            AddName(eventSymbol.Name)
 
            AddText("(")
 
            Dim eventType = eventSymbol.Type
            If eventType IsNot Nothing AndAlso eventType.TypeKind = TypeKind.Delegate Then
                Dim delegateInvokeMethod = CType(eventType, INamedTypeSymbol).DelegateInvokeMethod
                If delegateInvokeMethod IsNot Nothing Then
                    BuildParameterList(delegateInvokeMethod.Parameters)
                End If
            End If
 
            AddText(")")
        End Sub
 
        Private Sub BuildAccessibility(symbol As ISymbol)
            Select Case symbol.DeclaredAccessibility
                Case Accessibility.Public
                    AddText("Public ")
                Case Accessibility.Private
                    AddText("Private ")
                Case Accessibility.Friend
                    AddText("Friend ")
                Case Accessibility.Protected
                    AddText("Protected ")
                Case Accessibility.ProtectedOrFriend
                    AddText("Protected Friend ")
                Case Accessibility.ProtectedAndFriend
                    AddText("Private Protected ")
                Case Else
                    AddText("Friend ")
            End Select
        End Sub
 
        Private Sub BuildTypeModifiers(typeSymbol As INamedTypeSymbol)
            BuildAccessibility(typeSymbol)
 
            ' Note: There are no "Shared" types in VB.
 
            If typeSymbol.IsAbstract AndAlso
               typeSymbol.TypeKind <> TypeKind.Interface Then
                AddText("MustInherit ")
            End If
 
            If typeSymbol.IsSealed AndAlso
               typeSymbol.TypeKind <> TypeKind.Delegate AndAlso
               typeSymbol.TypeKind <> TypeKind.Module AndAlso
               typeSymbol.TypeKind <> TypeKind.Enum AndAlso
               typeSymbol.TypeKind <> TypeKind.Structure Then
 
                AddText("NotInheritable ")
            End If
        End Sub
 
        Private Sub BuildMemberModifiers(memberSymbol As ISymbol)
            If memberSymbol.ContainingType IsNot Nothing And memberSymbol.ContainingType.TypeKind = TypeKind.Interface Then
                Return
            End If
 
            Dim fieldSymbol = TryCast(memberSymbol, IFieldSymbol)
            Dim methodSymbol = TryCast(memberSymbol, IMethodSymbol)
 
            BuildAccessibility(memberSymbol)
 
            ' TODO 'Shadows' modifier isn't exposed on symbols. Do we need it?
 
            If memberSymbol.IsStatic AndAlso
               (methodSymbol Is Nothing OrElse Not methodSymbol.MethodKind = MethodKind.DeclareMethod) AndAlso
               (fieldSymbol Is Nothing OrElse Not fieldSymbol.IsConst) Then
 
                AddText("Shared ")
            End If
 
            If fieldSymbol IsNot Nothing AndAlso fieldSymbol.IsReadOnly Then
                AddText("ReadOnly ")
            End If
 
            If fieldSymbol IsNot Nothing AndAlso fieldSymbol.IsConst Then
                AddText("Const ")
            End If
 
            If memberSymbol.IsAbstract Then
                AddText("MustOverride ")
            End If
 
            If memberSymbol.IsOverride Then
                AddText("Overrides ")
            End If
 
            If memberSymbol.IsVirtual Then
                AddText("Overridable ")
            End If
 
            If memberSymbol.IsSealed Then
                AddText("NotOverridable ")
            End If
        End Sub
 
        Private Sub BuildParameterList(parameters As ImmutableArray(Of IParameterSymbol))
            Dim count = parameters.Length
            If count = 0 Then
                Return
            End If
 
            For i = 0 To count - 1
                If i > 0 Then
                    AddComma()
                End If
 
                Dim current = parameters(i)
 
                If current.IsOptional Then
                    AddText("Optional ")
                End If
 
                If current.RefKind = RefKind.Ref Then
                    ' TODO: Declare methods may implicitly make string parameters ByRef. To fix this,
                    ' we'll need support from the compiler to expose IsExplicitByRef or something similar
                    ' (Note: symbol display uses IsExplicitByRef to handle this case).
                    AddText("ByRef ")
                End If
 
                If current.IsParams Then
                    AddText("ParamArray ")
                End If
 
                AddParam(current.Name)
                AddText(" As ")
                AddTypeLink(current.Type, LinkFlags.None)
 
                If current.HasExplicitDefaultValue Then
                    AddText(" = ")
                    If current.ExplicitDefaultValue Is Nothing Then
                        AddText("Nothing")
                    Else
                        AddText(current.ExplicitDefaultValue.ToString())
                    End If
                End If
            Next
        End Sub
 
        Private Sub BuildTypeParameterList(typeParameters As ImmutableArray(Of ITypeParameterSymbol))
            Dim count = typeParameters.Length
            If count = 0 Then
                Return
            End If
 
            For i = 0 To count - 1
                If i > 0 Then
                    AddName(", ")
                End If
 
                Dim current = typeParameters(i)
 
                AddName(current.Name)
                AddConstraints(current)
            Next
        End Sub
 
        Private Sub AddConstraints(typeParameter As ITypeParameterSymbol)
            Dim count = CountConstraints(typeParameter)
            If count = 0 Then
                Return
            End If
 
            If count = 1 Then
                AddSingleConstraint(typeParameter)
            Else
                AddMultipleConstraints(typeParameter)
            End If
        End Sub
 
        Private Sub AddSingleConstraint(typeParameter As ITypeParameterSymbol)
            AddName(" As ")
 
            If typeParameter.HasReferenceTypeConstraint Then
                AddName("Class")
            ElseIf typeParameter.HasValueTypeConstraint Then
                AddName("Structure")
            ElseIf typeParameter.HasConstructorConstraint Then
                AddName("New")
            Else
                Debug.Assert(typeParameter.ConstraintTypes.Length = 1)
                AddTypeLink(typeParameter.ConstraintTypes(0), LinkFlags.None)
            End If
        End Sub
 
        Private Sub AddMultipleConstraints(typeParameter As ITypeParameterSymbol)
            AddName(" As {")
 
            Dim constraintAdded = False
 
            If typeParameter.HasReferenceTypeConstraint Then
                AddName("Class")
                constraintAdded = True
            End If
 
            If typeParameter.HasValueTypeConstraint Then
                If constraintAdded Then
                    AddName(", ")
                End If
 
                AddName("Structure")
                constraintAdded = True
            End If
 
            If typeParameter.HasConstructorConstraint Then
                If constraintAdded Then
                    AddName(", ")
                End If
 
                AddName("New")
                constraintAdded = True
            End If
 
            If typeParameter.ConstraintTypes.Length > 0 Then
                For Each constraintType In typeParameter.ConstraintTypes
                    If constraintAdded Then
                        AddName(", ")
                    End If
 
                    AddTypeLink(constraintType, LinkFlags.None)
                    constraintAdded = True
                Next
            End If
 
            AddName("}")
        End Sub
 
        Private Shared Function CountConstraints(typeParameter As ITypeParameterSymbol) As Integer
            Dim result = typeParameter.ConstraintTypes.Length
 
            If typeParameter.HasReferenceTypeConstraint Then
                result += 1
            End If
 
            If typeParameter.HasValueTypeConstraint Then
                result += 1
            End If
 
            If typeParameter.HasConstructorConstraint Then
                result += 1
            End If
 
            Return result
        End Function
    End Class
End Namespace