' 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.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeGeneration Imports Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Friend Module VisualBasicCodeGenerationHelpers Friend Sub AddAccessibilityModifiers( accessibility As Accessibility, tokens As ArrayBuilder(Of SyntaxToken), destination As CodeGenerationDestination, options As CodeGenerationContextInfo, nonStructureAccessibility As Accessibility) If Not options.Context.GenerateDefaultAccessibility Then If destination = CodeGenerationDestination.StructType AndAlso accessibility = Accessibility.Public Then Return End If If destination <> CodeGenerationDestination.StructType AndAlso accessibility = nonStructureAccessibility Then Return End If End If Select Case accessibility Case Accessibility.Public tokens.Add(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) Case Accessibility.Protected tokens.Add(SyntaxFactory.Token(SyntaxKind.ProtectedKeyword)) Case Accessibility.Private tokens.Add(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)) Case Accessibility.Internal tokens.Add(SyntaxFactory.Token(SyntaxKind.FriendKeyword)) Case Accessibility.ProtectedAndInternal tokens.Add(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)) tokens.Add(SyntaxFactory.Token(SyntaxKind.ProtectedKeyword)) Case Accessibility.ProtectedOrInternal tokens.Add(SyntaxFactory.Token(SyntaxKind.ProtectedKeyword)) tokens.Add(SyntaxFactory.Token(SyntaxKind.FriendKeyword)) End Select End Sub Public Function InsertAtIndex(members As SyntaxList(Of StatementSyntax), member As StatementSyntax, index As Integer) As SyntaxList(Of StatementSyntax) Dim result = New List(Of StatementSyntax)(members) ' then insert the new member. result.Insert(index, member) Return SyntaxFactory.List(result) End Function Public Function GenerateImplementsClause(explicitInterfaceOpt As ISymbol) As ImplementsClauseSyntax If explicitInterfaceOpt IsNot Nothing AndAlso explicitInterfaceOpt.ContainingType IsNot Nothing Then Dim type = explicitInterfaceOpt.ContainingType.GenerateTypeSyntax() If TypeOf type Is NameSyntax Then Return SyntaxFactory.ImplementsClause( interfaceMembers:=SyntaxFactory.SingletonSeparatedList( SyntaxFactory.QualifiedName( DirectCast(type, NameSyntax), explicitInterfaceOpt.Name.ToIdentifierName()))) End If End If Return Nothing End Function Public Function EnsureLastElasticTrivia(Of T As StatementSyntax)(statement As T) As T Dim lastToken = statement.GetLastToken(includeZeroWidth:=True) If lastToken.TrailingTrivia.Any(Function(trivia) trivia.IsElastic()) Then Return statement End If Return statement.WithAppendedTrailingTrivia(SyntaxFactory.ElasticMarker) End Function Public Function FirstMember(Of TDeclaration As SyntaxNode)(members As SyntaxList(Of TDeclaration)) As TDeclaration Return members.FirstOrDefault() End Function Public Function FirstMethod(Of TDeclaration As SyntaxNode)(members As SyntaxList(Of TDeclaration)) As TDeclaration Return members.LastOrDefault(Function(m) TypeOf m Is MethodBlockBaseSyntax OrElse TypeOf m Is MethodStatementSyntax) End Function Public Function LastField(Of TDeclaration As SyntaxNode)(members As SyntaxList(Of TDeclaration)) As TDeclaration Return members.LastOrDefault(Function(m) m.Kind = SyntaxKind.FieldDeclaration) End Function Public Function LastConstructor(Of TDeclaration As SyntaxNode)(members As SyntaxList(Of TDeclaration)) As TDeclaration Return members.LastOrDefault(Function(m) m.Kind = SyntaxKind.ConstructorBlock OrElse m.Kind = SyntaxKind.SubNewStatement) End Function Public Function LastMethod(Of TDeclaration As SyntaxNode)(members As SyntaxList(Of TDeclaration)) As TDeclaration Return members.LastOrDefault(Function(m) TypeOf m Is MethodBlockBaseSyntax OrElse TypeOf m Is MethodStatementSyntax) End Function Public Function LastOperator(Of TDeclaration As SyntaxNode)(members As SyntaxList(Of TDeclaration)) As TDeclaration Return members.LastOrDefault(Function(m) m.Kind = SyntaxKind.OperatorBlock OrElse m.Kind = SyntaxKind.OperatorStatement) End Function Private Function AfterDeclaration(Of TDeclaration As SyntaxNode)( [next] As Func(Of SyntaxList(Of TDeclaration), TDeclaration)) As Func(Of SyntaxList(Of TDeclaration), TDeclaration) Return Function(list) [next]?(list) End Function Private Function BeforeDeclaration(Of TDeclaration As SyntaxNode)( [next] As Func(Of SyntaxList(Of TDeclaration), TDeclaration)) As Func(Of SyntaxList(Of TDeclaration), TDeclaration) Return Function(list) [next]?(list) End Function Public Function Insert(Of TDeclaration As SyntaxNode)( declarationList As SyntaxList(Of TDeclaration), declaration As TDeclaration, options As CodeGenerationContextInfo, availableIndices As IList(Of Boolean), Optional after As Func(Of SyntaxList(Of TDeclaration), TDeclaration) = Nothing, Optional before As Func(Of SyntaxList(Of TDeclaration), TDeclaration) = Nothing) As SyntaxList(Of TDeclaration) after = AfterDeclaration(after) before = BeforeDeclaration(before) Dim index = GetInsertionIndex( declarationList, declaration, options, availableIndices, VisualBasicDeclarationComparer.WithoutNamesInstance, VisualBasicDeclarationComparer.WithNamesInstance, after, before) If availableIndices IsNot Nothing Then availableIndices.Insert(index, True) End If Return declarationList.Insert(index, declaration) End Function Public Function GetDestination(destination As SyntaxNode) As CodeGenerationDestination If destination IsNot Nothing Then Select Case destination.Kind Case SyntaxKind.ClassBlock Return CodeGenerationDestination.ClassType Case SyntaxKind.CompilationUnit Return CodeGenerationDestination.CompilationUnit Case SyntaxKind.EnumBlock Return CodeGenerationDestination.EnumType Case SyntaxKind.InterfaceBlock Return CodeGenerationDestination.InterfaceType Case SyntaxKind.ModuleBlock Return CodeGenerationDestination.ModuleType Case SyntaxKind.NamespaceBlock Return CodeGenerationDestination.Namespace Case SyntaxKind.StructureBlock Return CodeGenerationDestination.StructType Case Else Return CodeGenerationDestination.Unspecified End Select End If Return CodeGenerationDestination.Unspecified End Function Public Function ConditionallyAddDocumentationCommentTo(Of TSyntaxNode As SyntaxNode)( node As TSyntaxNode, symbol As ISymbol, options As CodeGenerationContextInfo, Optional cancellationToken As CancellationToken = Nothing) As TSyntaxNode If Not options.Context.GenerateDocumentationComments OrElse node.GetLeadingTrivia().Any(Function(t) t.IsKind(SyntaxKind.DocumentationCommentTrivia)) Then Return node End If Dim comment As String = Nothing Dim result = If(TryGetDocumentationComment(symbol, "'''", comment, cancellationToken), node.WithPrependedLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(comment)) _ .WithPrependedLeadingTrivia(SyntaxFactory.ElasticMarker), node) Return result End Function ''' <summary> ''' Try use the existing syntax node and generate a new syntax node for the given <param name="symbol"/>. ''' Note: the returned syntax node might be modified, which means its parent information might be missing. ''' </summary> Public Function GetReuseableSyntaxNodeForSymbol(Of T As SyntaxNode)(symbol As ISymbol, options As CodeGenerationContextInfo) As T Contract.ThrowIfNull(symbol) If options.Context.ReuseSyntax AndAlso symbol.DeclaringSyntaxReferences.Length = 1 Then Dim reusableNode = symbol.DeclaringSyntaxReferences(0).GetSyntax() ' For VB method like symbol (Function, Sub, Property & Event), DeclaringSyntaxReferences will fetch ' the first line of the member's block. But what we want to reuse is the whole member's block If symbol.IsKind(SymbolKind.Method) OrElse symbol.IsKind(SymbolKind.Property) OrElse symbol.IsKind(SymbolKind.Event) Then Dim declarationStatementNode = TryCast(reusableNode, DeclarationStatementSyntax) If declarationStatementNode IsNot Nothing Then Dim declarationBlockFromBegin = declarationStatementNode.GetDeclarationBlockFromBegin() Return TryCast(RemoveLeadingDirectiveTrivia(declarationBlockFromBegin), T) End If End If Dim modifiedIdentifierNode = TryCast(reusableNode, ModifiedIdentifierSyntax) If modifiedIdentifierNode IsNot Nothing AndAlso symbol.IsKind(SymbolKind.Field) AndAlso GetType(T) Is GetType(FieldDeclarationSyntax) Then Dim variableDeclarator = TryCast(modifiedIdentifierNode.Parent, VariableDeclaratorSyntax) If variableDeclarator IsNot Nothing Then Dim fieldDecl = TryCast(variableDeclarator.Parent, FieldDeclarationSyntax) If fieldDecl IsNot Nothing Then Dim names = SyntaxFactory.SingletonSeparatedList(modifiedIdentifierNode) Dim newVariableDeclarator = variableDeclarator.WithNames(names) Return TryCast(RemoveLeadingDirectiveTrivia( fieldDecl.WithDeclarators(SyntaxFactory.SingletonSeparatedList(newVariableDeclarator))), T) End If End If End If Return TryCast(RemoveLeadingDirectiveTrivia(reusableNode), T) End If Return Nothing End Function End Module End Namespace |