/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/VisualBasic/Impl/CodeModel/MethodXML/MethodXmlBuilder.vb
702 строки
30 KB
Cyrus Najmabadi
Switch to more modern vb construc
04 янв 2025, 00:11
04 янв 2025, 00:11
03adb27
Код
Авторство
О чём код?
' 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 Imports Microsoft.CodeAnalysis.Shared.Extensions Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Extensions Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.MethodXml Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel.MethodXml Friend Class MethodXmlBuilder Inherits AbstractMethodXmlBuilder Private Shared ReadOnly s_fullNameFormat As New SymbolDisplayFormat( typeQualificationStyle:=SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, memberOptions:=SymbolDisplayMemberOptions.None) Private Sub New(symbol As IMethodSymbol, semanticModel As SemanticModel) MyBase.New(symbol, semanticModel) End Sub Private Sub GenerateStatementBlock(statementList As SyntaxList(Of StatementSyntax)) Using BlockTag() For Each statement In statementList GenerateStatement(statement) Next End Using End Sub Private Sub GenerateStatement(statement As StatementSyntax) Dim success = False Dim mark = GetMark() Select Case statement.Kind Case SyntaxKind.LocalDeclarationStatement success = TryGenerateLocal(DirectCast(statement, LocalDeclarationStatementSyntax)) Case SyntaxKind.SimpleAssignmentStatement success = TryGenerateAssignment(DirectCast(statement, AssignmentStatementSyntax)) Case SyntaxKind.CallStatement success = TryGenerateCall(DirectCast(statement, CallStatementSyntax)) Case SyntaxKind.ExpressionStatement success = TryGenerateExpressionStatement(DirectCast(statement, ExpressionStatementSyntax)) Case SyntaxKind.AddHandlerStatement, SyntaxKind.RemoveHandlerStatement success = TryGenerateAddOrRemoveHandlerStatement(DirectCast(statement, AddRemoveHandlerStatementSyntax)) End Select If Not success Then Rewind(mark) GenerateUnknown(statement) End If ' Just for readability LineBreak() End Sub Private Function TryGenerateLocal(localDeclaration As LocalDeclarationStatementSyntax) As Boolean For Each declarator In localDeclaration.Declarators Using LocalTag(GetLineNumber(localDeclaration)) ' Note: this is a difference from Dev10 where VB always generates ' a full assembly-qualified name. Because this extra information is ' generally wasteful, and C# never generates, we'll just generate ' the non-assembly-qualified name instead. Dim type = declarator.Type(SemanticModel) If type Is Nothing Then Return False End If GenerateType(type) For Each name In declarator.Names Using NameTag() EncodedText(name.Identifier.ToString()) End Using Dim initializer = declarator.GetInitializer() If type.TypeKind = TypeKind.Array AndAlso (name.ArrayBounds IsNot Nothing OrElse initializer IsNot Nothing) Then Using ExpressionTag() If Not TryGenerateNewArray(name.ArrayBounds, initializer, type) Then Return False End If End Using ElseIf initializer IsNot Nothing AndAlso Not TryGenerateExpression(initializer) Then Return False End If Next End Using Next Return True End Function Private Function TryGenerateAssignment(assignmentStatement As AssignmentStatementSyntax) As Boolean Using ExpressionStatementTag(GetLineNumber(assignmentStatement)) Using ExpressionTag() Using AssignmentTag() Return TryGenerateExpression(assignmentStatement.Left) AndAlso TryGenerateExpression(assignmentStatement.Right) End Using End Using End Using End Function Private Function TryGenerateCall(callStatement As CallStatementSyntax) As Boolean Using ExpressionStatementTag(GetLineNumber(callStatement)) Return TryGenerateExpression(callStatement.Invocation) End Using End Function Private Function TryGenerateExpressionStatement(expressionStatement As ExpressionStatementSyntax) As Boolean Using ExpressionStatementTag(GetLineNumber(expressionStatement)) Return TryGenerateExpression(expressionStatement.Expression) End Using End Function Private Function TryGenerateAddOrRemoveHandlerStatement(addHandlerStatement As AddRemoveHandlerStatementSyntax) As Boolean ' AddHandler statements are represented as invocations of an event's add_* method. ' RemoveHandler statements are represented as invocations of an event's remove_* method. Dim eventExpression = addHandlerStatement.EventExpression Dim eventSymbol = TryCast(SemanticModel.GetSymbolInfo(eventExpression).Symbol, IEventSymbol) Dim eventAccessor As IMethodSymbol If addHandlerStatement.Kind() = SyntaxKind.AddHandlerStatement Then eventAccessor = eventSymbol?.AddMethod ElseIf addHandlerStatement.Kind() = SyntaxKind.RemoveHandlerStatement eventAccessor = eventSymbol?.RemoveMethod Else eventAccessor = Nothing End If If eventAccessor Is Nothing Then Return False End If Using ExpressionStatementTag(GetLineNumber(addHandlerStatement)) Using ExpressionTag() Using MethodCallTag() If Not TryGenerateExpression(eventExpression, eventAccessor, generateAttributes:=True) Then Return False End If GenerateType(eventSymbol.ContainingType, implicit:=True, assemblyQualify:=True) Using ArgumentTag() If Not TryGenerateExpression(addHandlerStatement.DelegateExpression, generateAttributes:=True) Then Return False End If End Using Return True End Using End Using End Using End Function Private Function TryGenerateExpression(expression As ExpressionSyntax, Optional symbolOpt As ISymbol = Nothing, Optional generateAttributes As Boolean = False) As Boolean Using ExpressionTag() Return TryGenerateExpressionSansTag(expression, symbolOpt, generateAttributes) End Using End Function Private Function TryGenerateExpressionSansTag(expression As ExpressionSyntax, Optional symbolOpt As ISymbol = Nothing, Optional generateAttributes As Boolean = False) As Boolean Select Case expression.Kind Case SyntaxKind.CharacterLiteralExpression, SyntaxKind.UnaryMinusExpression, SyntaxKind.NumericLiteralExpression, SyntaxKind.StringLiteralExpression, SyntaxKind.TrueLiteralExpression, SyntaxKind.FalseLiteralExpression Return TryGenerateLiteral(expression) Case SyntaxKind.NothingLiteralExpression GenerateNullLiteral() Return True Case SyntaxKind.ParenthesizedExpression Return TryGenerateParenthesizedExpression(DirectCast(expression, ParenthesizedExpressionSyntax)) Case SyntaxKind.AddExpression, SyntaxKind.OrExpression, SyntaxKind.AndExpression, SyntaxKind.ConcatenateExpression Return TryGenerateBinaryOperation(DirectCast(expression, BinaryExpressionSyntax)) Case SyntaxKind.CollectionInitializer Return TryGenerateCollectionInitializer(DirectCast(expression, CollectionInitializerSyntax)) Case SyntaxKind.ArrayCreationExpression Return TryGenerateArrayCreation(DirectCast(expression, ArrayCreationExpressionSyntax)) Case SyntaxKind.ObjectCreationExpression Return TryGenerateNewClass(DirectCast(expression, ObjectCreationExpressionSyntax)) Case SyntaxKind.SimpleMemberAccessExpression Return TryGenerateNameRef(DirectCast(expression, MemberAccessExpressionSyntax), symbolOpt, generateAttributes) Case SyntaxKind.IdentifierName Return TryGenerateNameRef(DirectCast(expression, IdentifierNameSyntax), symbolOpt, generateAttributes) Case SyntaxKind.InvocationExpression Return TryGenerateMethodCall(DirectCast(expression, InvocationExpressionSyntax)) Case SyntaxKind.GetTypeExpression Return TryGenerateGetTypeExpression(DirectCast(expression, GetTypeExpressionSyntax)) Case SyntaxKind.CTypeExpression, SyntaxKind.DirectCastExpression, SyntaxKind.TryCastExpression Return TryGenerateCastExpression(DirectCast(expression, CastExpressionSyntax)) Case SyntaxKind.PredefinedCastExpression Return TryGeneratePredefinedCastExpression(DirectCast(expression, PredefinedCastExpressionSyntax)) Case SyntaxKind.MeExpression GenerateThisReference() Return True Case SyntaxKind.AddressOfExpression Return TryGenerateAddressOfExpression(DirectCast(expression, UnaryExpressionSyntax)) End Select Return False End Function Private Function TryGenerateLiteral(expression As ExpressionSyntax) As Boolean Using LiteralTag() Dim constantValue = SemanticModel.GetConstantValue(expression) If Not constantValue.HasValue Then Return False End If Dim type = SemanticModel.GetTypeInfo(expression).Type If type Is Nothing Then Return False End If Select Case expression.Kind Case SyntaxKind.UnaryMinusExpression, SyntaxKind.NumericLiteralExpression GenerateNumber(constantValue.Value, type) Return True Case SyntaxKind.CharacterLiteralExpression GenerateChar(CChar(constantValue.Value)) Return True Case SyntaxKind.StringLiteralExpression GenerateString(CStr(constantValue.Value)) Return True Case SyntaxKind.TrueLiteralExpression, SyntaxKind.FalseLiteralExpression GenerateBoolean(CBool(constantValue.Value)) Return True End Select End Using Return False End Function Private Function TryGenerateParenthesizedExpression(parenthesizedExpression As ParenthesizedExpressionSyntax) As Boolean Using ParenthesesTag() Return TryGenerateExpression(parenthesizedExpression.Expression) End Using End Function Private Function TryGenerateBinaryOperation(binaryExpression As BinaryExpressionSyntax) As Boolean Dim kind As BinaryOperatorKind Select Case binaryExpression.Kind Case SyntaxKind.AddExpression kind = BinaryOperatorKind.Plus Case SyntaxKind.OrExpression kind = BinaryOperatorKind.BitwiseOr Case SyntaxKind.AndExpression kind = BinaryOperatorKind.BitwiseAnd Case SyntaxKind.ConcatenateExpression kind = BinaryOperatorKind.Concatenate Case Else Return False End Select Using BinaryOperationTag(kind) Return TryGenerateExpression(binaryExpression.Left) AndAlso TryGenerateExpression(binaryExpression.Right) End Using End Function Private Function TryGenerateNewClass(objectCreationExpression As ObjectCreationExpressionSyntax) As Boolean Dim type = TryCast(SemanticModel.GetSymbolInfo(objectCreationExpression.Type).Symbol, ITypeSymbol) If type Is Nothing Then Return False End If Using NewClassTag() GenerateType(type) If objectCreationExpression.ArgumentList IsNot Nothing Then For Each argument In objectCreationExpression.ArgumentList.Arguments If Not TryGenerateArgument(argument) Then Return False End If Next End If End Using Return True End Function Private Function TryGenerateArgument(argument As ArgumentSyntax) As Boolean Using ArgumentTag() Return TryGenerateExpression(argument.GetArgumentExpression()) End Using End Function Protected Overrides Function GetVariableKind(symbol As ISymbol) As VariableKind Dim kind = MyBase.GetVariableKind(symbol) ' need to special case WithEvent properties. CodeModel wants them as fields. If symbol?.Kind = SymbolKind.Property Then Dim propertySymbol = TryCast(symbol, IPropertySymbol) If propertySymbol IsNot Nothing AndAlso propertySymbol.IsWithEvents Then kind = VariableKind.Field End If End If Return kind End Function Private Shared Function GetFullNameText(symbol As ISymbol) As String If symbol Is Nothing Then Return Nothing End If If symbol.IsAccessor() Then Return Nothing End If Return symbol.ContainingSymbol.ToDisplayString(s_fullNameFormat) & "." & symbol.Name End Function Private Function TryGenerateNameRef( memberAccess As MemberAccessExpressionSyntax, Optional symbolOpt As ISymbol = Nothing, Optional generateAttributes As Boolean = False ) As Boolean symbolOpt = If(symbolOpt, SemanticModel.GetSymbolInfo(memberAccess).Symbol) ' Note: There's no null check for 'symbolOpt' here. If 'symbolOpt' is Nothing, we'll generate an "unknown" name ref. Dim varKind As VariableKind = GetVariableKind(symbolOpt) Dim name = If(symbolOpt IsNot Nothing, symbolOpt.Name, memberAccess.Name.Identifier.ValueText) Dim nameAttribute = If(generateAttributes, name, Nothing) Dim fullNameAttribute = If(generateAttributes, GetFullNameText(symbolOpt), Nothing) Using NameRefTag(varKind, nameAttribute, fullNameAttribute) Dim leftHandSymbol = SemanticModel.GetSymbolInfo(memberAccess.GetExpressionOfMemberAccessExpression()).Symbol If leftHandSymbol IsNot Nothing Then If leftHandSymbol.Kind = SymbolKind.Alias Then leftHandSymbol = DirectCast(leftHandSymbol, IAliasSymbol).Target End If ' This can occur if a module member is referenced without the module name. In that case, ' we'll go ahead and try to use the module namespace name. If leftHandSymbol.Kind = SymbolKind.Namespace AndAlso symbolOpt?.ContainingType?.TypeKind = TypeKind.Module Then leftHandSymbol = symbolOpt.ContainingType End If End If ' If the left-hand side is a named type, we generate a literal expression ' with the type name. Otherwise, we generate the expression normally. If leftHandSymbol IsNot Nothing AndAlso leftHandSymbol.Kind = SymbolKind.NamedType Then Using ExpressionTag() Using LiteralTag() GenerateType(DirectCast(leftHandSymbol, ITypeSymbol)) End Using End Using ElseIf Not TryGenerateExpression(memberAccess.GetExpressionOfMemberAccessExpression(), generateAttributes:=generateAttributes) Then Return False End If If Not generateAttributes Then GenerateName(name) End If End Using Return True End Function Private Function TryGenerateNameRef( identifierName As IdentifierNameSyntax, Optional symbolOpt As ISymbol = Nothing, Optional generateAttributes As Boolean = False ) As Boolean symbolOpt = If(symbolOpt, SemanticModel.GetSymbolInfo(identifierName).Symbol) ' Note: There's no null check for 'symbolOpt' here. If 'symbolOpt' is Nothing, we'll generate an "unknown" name ref. Dim varKind = GetVariableKind(symbolOpt) Dim name = If(symbolOpt IsNot Nothing, symbolOpt.Name, identifierName.Identifier.ValueText) Dim nameAttribute = If(generateAttributes, name, Nothing) Dim fullNameAttribute = If(generateAttributes, GetFullNameText(symbolOpt), Nothing) Using NameRefTag(varKind, nameAttribute, fullNameAttribute) If symbolOpt IsNot Nothing AndAlso varKind <> VariableKind.Local Then GenerateLastNameRef(symbolOpt) End If If Not generateAttributes Then GenerateName(name) End If End Using Return True End Function Private Sub GenerateLastNameRef(symbol As ISymbol) ' This is a little tricky -- if our method symbol's containing type inherits from ' or is the same as the identifier symbol's containing type, we'll go ahead and ' generate a <ThisReference />. Otherwise, because this is an identifier, we assume ' that it is a Shared member or an imported type (for example, a Module) and generate ' an <Expression><Literal><Type/></Literal></Expression> If Me.Symbol.ContainingType.InheritsFromOrEquals(symbol.ContainingType) Then Using ExpressionTag() GenerateThisReference() End Using Else Using ExpressionTag() Using LiteralTag() GenerateType(symbol.ContainingType) End Using End Using End If End Sub Private Function TryGenerateMethodCall(invocationExpression As InvocationExpressionSyntax) As Boolean Using MethodCallTag() If Not TryGenerateExpression(invocationExpression.Expression) Then Return False End If If invocationExpression.ArgumentList IsNot Nothing Then For Each argument In invocationExpression.ArgumentList.Arguments If Not TryGenerateArgument(argument) Then Return False End If Next End If End Using Return True End Function Private Function TryGenerateGetTypeExpression(getTypeExpression As GetTypeExpressionSyntax) As Boolean If getTypeExpression.Type Is Nothing Then Return False End If Dim type = SemanticModel.GetTypeInfo(getTypeExpression.Type).Type If type Is Nothing Then Return False End If GenerateType(type) Return True End Function Private Function TryGenerateCast(type As ITypeSymbol, expression As ExpressionSyntax, Optional specialCastKind As SpecialCastKind? = Nothing) As Boolean Using (CastTag(specialCastKind)) GenerateType(type) Return TryGenerateExpression(expression) End Using End Function Private Function TryGenerateCastExpression(castExpression As CastExpressionSyntax) As Boolean If castExpression.Type Is Nothing Then Return False End If Dim type = SemanticModel.GetTypeInfo(castExpression.Type).Type If type Is Nothing Then Return False End If If Not TryGenerateCast(type, castExpression.Expression, GetSpecialCastKind(castExpression)) Then Return False End If Return True End Function Private Shared Function GetSpecialCastKind(castExpression As CastExpressionSyntax) As SpecialCastKind? Select Case castExpression.Kind() Case SyntaxKind.DirectCastExpression Return SpecialCastKind.DirectCast Case SyntaxKind.TryCastExpression Return SpecialCastKind.TryCast Case Else Return Nothing End Select End Function Private Function TryGeneratePredefinedCastExpression(predefinedCastExpression As PredefinedCastExpressionSyntax) As Boolean Dim type = GetTypeFromPredefinedCastKeyword(SemanticModel.Compilation, predefinedCastExpression.Keyword.Kind) If type Is Nothing Then Return False End If If Not TryGenerateCast(type, predefinedCastExpression.Expression) Then Return False End If Return True End Function Private Function TryGenerateConstantArrayBound(expression As ExpressionSyntax) As Boolean Dim constantValue = SemanticModel.GetConstantValue(expression) If Not constantValue.HasValue Then Return False End If If TypeOf constantValue.Value IsNot Integer Then Return False End If Dim upperBound = CInt(constantValue.Value) + 1 Using ExpressionTag() Using LiteralTag() GenerateNumber(upperBound, SemanticModel.Compilation.GetSpecialType(SpecialType.System_Int32)) End Using End Using Return True End Function Private Function TryGenerateSimpleArrayBound(argument As ArgumentSyntax) As Boolean If TypeOf argument IsNot SimpleArgumentSyntax Then Return False End If Return TryGenerateConstantArrayBound(DirectCast(argument, SimpleArgumentSyntax).Expression) End Function Private Function TryGenerateRangeArrayBound(argument As ArgumentSyntax) As Boolean If TypeOf argument IsNot RangeArgumentSyntax Then Return False End If Return TryGenerateConstantArrayBound(DirectCast(argument, RangeArgumentSyntax).UpperBound) End Function Private Function TryGenerateCollectionInitializer(collectionInitializer As CollectionInitializerSyntax) As Boolean For Each initializer In collectionInitializer.Initializers If Not TryGenerateExpression(initializer) Then Return False End If Next Return True End Function Private Function TryGenerateNewArray(arrayBounds As ArgumentListSyntax, initializer As ExpressionSyntax, type As ITypeSymbol) As Boolean Using NewArrayTag() GenerateType(type) If arrayBounds IsNot Nothing Then If Not TryGenerateArrayBounds(arrayBounds) Then Return False End If If initializer IsNot Nothing AndAlso initializer.Kind = SyntaxKind.CollectionInitializer Then If Not TryGenerateCollectionInitializer(DirectCast(initializer, CollectionInitializerSyntax)) Then Return False End If End If Else ' No array bounds... If initializer IsNot Nothing Then If type.TypeKind = TypeKind.Array Then Select Case initializer.Kind Case SyntaxKind.ArrayCreationExpression If Not TryGenerateArrayCreation(DirectCast(initializer, ArrayCreationExpressionSyntax)) Then Return False End If Case SyntaxKind.CollectionInitializer If Not TryGenerateArrayInitializer(DirectCast(initializer, CollectionInitializerSyntax)) Then Return False End If Case Else Return False End Select ElseIf Not TryGenerateExpression(initializer) Then Return False End If End If End If Return True End Using End Function Private Function TryGenerateArrayBounds(argumentList As ArgumentListSyntax) As Boolean For Each argument In argumentList.Arguments Using BoundTag() If Not TryGenerateSimpleArrayBound(argument) AndAlso Not TryGenerateRangeArrayBound(argument) Then Return False End If End Using Next Return True End Function Private Function TryGenerateArrayInitializer(collectionInitializer As CollectionInitializerSyntax) As Boolean Using BoundTag() Using ExpressionTag() Using LiteralTag() Using NumberTag() EncodedText(collectionInitializer.Initializers.Count.ToString()) End Using End Using End Using End Using Return TryGenerateCollectionInitializer(collectionInitializer) End Function Private Function TryGenerateArrayCreation(arrayCreationExpression As ArrayCreationExpressionSyntax) As Boolean Dim type = SemanticModel.GetTypeInfo(arrayCreationExpression).Type If type Is Nothing Then Return False End If If Not TryGenerateNewArray(arrayCreationExpression.ArrayBounds, arrayCreationExpression.Initializer, type) Then Return False End If Return True End Function Private Function TryGenerateAddressOfExpression(expression As UnaryExpressionSyntax) As Boolean Debug.Assert(expression.Kind() = SyntaxKind.AddressOfExpression) Dim delegateExpression = expression.Operand Dim delegateSymbol = TryCast(SemanticModel.GetSymbolInfo(delegateExpression).Symbol, IMethodSymbol) If delegateSymbol Is Nothing Then Return False End If Dim eventType = SemanticModel.GetTypeInfo(expression).Type If eventType Is Nothing Then Return False End If Using NewDelegateTag(delegateSymbol.Name) GenerateType(eventType, implicit:=True, assemblyQualify:=True) If delegateExpression.Kind() = SyntaxKind.IdentifierName Then GenerateLastNameRef(delegateSymbol) ElseIf delegateExpression.Kind() = SyntaxKind.SimpleMemberAccessExpression Dim memberAccess = DirectCast(delegateExpression, MemberAccessExpressionSyntax) If Not TryGenerateExpression(memberAccess.GetExpressionOfMemberAccessExpression(), generateAttributes:=True) Then Return False End If End If GenerateType(delegateSymbol.ContainingType, implicit:=True, assemblyQualify:=True) Return True End Using End Function Public Shared Function Generate(methodBlock As MethodBlockBaseSyntax, semanticModel As SemanticModel) As String Dim symbol = semanticModel.GetDeclaredSymbol(methodBlock) Dim builder = New MethodXmlBuilder(symbol, semanticModel) builder.GenerateStatementBlock(methodBlock.Statements) Return builder.ToString() End Function End Class End Namespace