File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\CodeGeneration\MethodGenerator.vb
Web Access
Project: src\src\Workspaces\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj (Microsoft.CodeAnalysis.VisualBasic.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.
 
Imports Microsoft.CodeAnalysis.CodeGeneration
Imports Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
    Friend Class MethodGenerator
 
        Friend Shared Function AddMethodTo(destination As NamespaceBlockSyntax,
                                           method As IMethodSymbol,
                                           options As CodeGenerationContextInfo,
                                           availableIndices As IList(Of Boolean)) As NamespaceBlockSyntax
 
            Dim declaration = GenerateMethodDeclaration(method, CodeGenerationDestination.Namespace, options)
 
            Dim members = Insert(destination.Members, declaration, options, availableIndices,
                                 after:=AddressOf LastMethod)
 
            Return destination.WithMembers(SyntaxFactory.List(members))
        End Function
 
        Friend Shared Function AddMethodTo(destination As CompilationUnitSyntax,
                                           method As IMethodSymbol,
                                           options As CodeGenerationContextInfo,
                                           availableIndices As IList(Of Boolean)) As CompilationUnitSyntax
 
            Dim declaration = GenerateMethodDeclaration(method, CodeGenerationDestination.Namespace, options)
 
            Dim members = Insert(destination.Members, declaration, options, availableIndices,
                                 after:=AddressOf LastMethod)
 
            Return destination.WithMembers(SyntaxFactory.List(members))
        End Function
 
        Friend Shared Function AddMethodTo(destination As TypeBlockSyntax,
                                           method As IMethodSymbol,
                                           options As CodeGenerationContextInfo,
                                           availableIndices As IList(Of Boolean)) As TypeBlockSyntax
 
            Dim methodDeclaration = GenerateMethodDeclaration(method, GetDestination(destination), options)
 
            Dim members = Insert(destination.Members, methodDeclaration, options, availableIndices,
                                 after:=AddressOf LastMethod)
 
            Return FixTerminators(destination.WithMembers(members))
        End Function
 
        Public Shared Function GenerateMethodDeclaration(method As IMethodSymbol,
                                                         destination As CodeGenerationDestination,
                                                         options As CodeGenerationContextInfo) As StatementSyntax
            Dim reusableSyntax = GetReuseableSyntaxNodeForSymbol(Of DeclarationStatementSyntax)(method, options)
            If reusableSyntax IsNot Nothing Then
                Return reusableSyntax
            End If
 
            Dim declaration = GenerateMethodDeclarationWorker(method, destination, options)
 
            Return AddAnnotationsTo(method,
                AddFormatterAndCodeGeneratorAnnotationsTo(
                    ConditionallyAddDocumentationCommentTo(declaration, method, options)))
        End Function
 
        Private Shared Function GenerateMethodDeclarationWorker(method As IMethodSymbol,
                                                                destination As CodeGenerationDestination,
                                                                options As CodeGenerationContextInfo) As StatementSyntax
            Dim isSub = method.ReturnType.SpecialType = SpecialType.System_Void
            Dim kind = If(isSub, SyntaxKind.SubStatement, SyntaxKind.FunctionStatement)
            Dim keyword = If(isSub, SyntaxKind.SubKeyword, SyntaxKind.FunctionKeyword)
            Dim asClauseOpt = GenerateAsClause(method, isSub, options)
            Dim implementsClauseOpt = GenerateImplementsClause(method.ExplicitInterfaceImplementations.FirstOrDefault())
            Dim handlesClauseOpt = GenerateHandlesClause(CodeGenerationMethodInfo.GetHandlesExpressions(method))
 
            Dim begin =
                SyntaxFactory.MethodStatement(kind, subOrFunctionKeyword:=SyntaxFactory.Token(keyword), identifier:=method.Name.ToIdentifierToken).
                    WithAttributeLists(AttributeGenerator.GenerateAttributeBlocks(method.GetAttributes(), options)).
                    WithModifiers(GenerateModifiers(method, destination, options)).
                    WithTypeParameterList(GenerateTypeParameterList(method)).
                    WithParameterList(ParameterGenerator.GenerateParameterList(method.Parameters, options)).
                    WithAsClause(asClauseOpt).
                    WithImplementsClause(implementsClauseOpt).
                    WithHandlesClause(handlesClauseOpt)
 
            Dim hasNoBody = Not options.Context.GenerateMethodBodies OrElse
                            method.IsAbstract OrElse
                            destination = CodeGenerationDestination.InterfaceType
 
            If hasNoBody Then
                Return begin
            End If
 
            Dim endConstruct = If(isSub, SyntaxFactory.EndSubStatement(), SyntaxFactory.EndFunctionStatement())
            Return SyntaxFactory.MethodBlock(
                If(isSub, SyntaxKind.SubBlock, SyntaxKind.FunctionBlock),
                begin,
                statements:=StatementGenerator.GenerateStatements(method),
                endSubOrFunctionStatement:=endConstruct)
        End Function
 
        Private Shared Function GenerateAsClause(method As IMethodSymbol, isSub As Boolean, options As CodeGenerationContextInfo) As SimpleAsClauseSyntax
            If isSub Then
                Return Nothing
            End If
 
            Return SyntaxFactory.SimpleAsClause(
                AttributeGenerator.GenerateAttributeBlocks(method.GetReturnTypeAttributes(), options),
                method.ReturnType.GenerateTypeSyntax())
        End Function
 
        Private Shared Function GenerateHandlesClause(expressions As IList(Of SyntaxNode)) As HandlesClauseSyntax
            Dim memberAccessExpressions = expressions.OfType(Of MemberAccessExpressionSyntax).ToList()
 
            Dim items = From def In memberAccessExpressions
                        Let expr1 = def.Expression
                        Where expr1 IsNot Nothing
                        Let expr2 = If(TypeOf expr1 Is ParenthesizedExpressionSyntax, DirectCast(expr1, ParenthesizedExpressionSyntax).Expression, expr1)
                        Let children = expr2.ChildNodesAndTokens()
                        Where children.Count = 1 AndAlso children(0).IsToken
                        Let token = children(0).AsToken()
                        Where token.Kind = SyntaxKind.IdentifierToken OrElse
                              token.Kind = SyntaxKind.MyBaseKeyword OrElse
                              token.Kind = SyntaxKind.MyClassKeyword OrElse
                              token.Kind = SyntaxKind.MeKeyword
                        Where TypeOf def.Name Is IdentifierNameSyntax
                        Let identifier = def.Name.Identifier.ValueText.ToIdentifierName()
                        Select SyntaxFactory.HandlesClauseItem(If(token.Kind = SyntaxKind.IdentifierToken,
                                                           DirectCast(SyntaxFactory.WithEventsEventContainer(token.ValueText.ToIdentifierToken()), EventContainerSyntax),
                                                           SyntaxFactory.KeywordEventContainer(token)), identifier)
 
            Dim array = items.ToArray()
            Return If(array.Length = 0, Nothing, SyntaxFactory.HandlesClause(array))
        End Function
 
        Private Overloads Shared Function GenerateTypeParameterList(method As IMethodSymbol) As TypeParameterListSyntax
            Return TypeParameterGenerator.GenerateTypeParameterList(method.TypeParameters)
        End Function
 
        Private Shared Function GenerateModifiers(method As IMethodSymbol,
                                                  destination As CodeGenerationDestination,
                                                  options As CodeGenerationContextInfo) As SyntaxTokenList
            Dim result As ArrayBuilder(Of SyntaxToken) = Nothing
            Using x = ArrayBuilder(Of SyntaxToken).GetInstance(result)
                If destination <> CodeGenerationDestination.InterfaceType Then
                    AddAccessibilityModifiers(method.DeclaredAccessibility, result, destination, options, Accessibility.Public)
 
                    If method.IsAbstract Then
                        result.Add(SyntaxFactory.Token(SyntaxKind.MustOverrideKeyword))
                    End If
 
                    If method.IsSealed Then
                        result.Add(SyntaxFactory.Token(SyntaxKind.NotOverridableKeyword))
                    End If
 
                    If method.IsVirtual Then
                        result.Add(SyntaxFactory.Token(SyntaxKind.OverridableKeyword))
                    End If
 
                    If method.IsOverride Then
                        result.Add(SyntaxFactory.Token(SyntaxKind.OverridesKeyword))
                    End If
 
                    If method.IsStatic AndAlso destination <> CodeGenerationDestination.ModuleType Then
                        result.Add(SyntaxFactory.Token(SyntaxKind.SharedKeyword))
                    End If
 
                    If CodeGenerationMethodInfo.GetIsNew(method) Then
                        result.Add(SyntaxFactory.Token(SyntaxKind.ShadowsKeyword))
                    End If
 
                    If CodeGenerationMethodInfo.GetIsAsyncMethod(method) Then
                        result.Add(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
                    End If
                End If
 
                Return SyntaxFactory.TokenList(result)
            End Using
        End Function
    End Class
End Namespace