|
' 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.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.Utilities
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification
Partial Friend Class VisualBasicSimplificationService
Private Class Expander
Inherits VisualBasicSyntaxRewriter
Private ReadOnly _semanticModel As SemanticModel
Private ReadOnly _expandInsideNode As Func(Of SyntaxNode, Boolean)
Private ReadOnly _expandParameter As Boolean
Private ReadOnly _cancellationToken As CancellationToken
Private ReadOnly _annotationForReplacedAliasIdentifier As SyntaxAnnotation
Public Sub New(
semanticModel As SemanticModel,
expandInsideNode As Func(Of SyntaxNode, Boolean),
cancellationToken As CancellationToken,
Optional expandParameter As Boolean = False,
Optional aliasReplacementAnnotation As SyntaxAnnotation = Nothing)
MyBase.New(visitIntoStructuredTrivia:=True)
_semanticModel = semanticModel
_expandInsideNode = expandInsideNode
_cancellationToken = cancellationToken
_expandParameter = expandParameter
_annotationForReplacedAliasIdentifier = aliasReplacementAnnotation
End Sub
Public Overrides Function Visit(node As SyntaxNode) As SyntaxNode
If _expandInsideNode Is Nothing OrElse _expandInsideNode(node) Then
Return MyBase.Visit(node)
End If
Return node
End Function
Private Function AddCast(expression As ExpressionSyntax, targetType As ITypeSymbol, oldExpression As ExpressionSyntax) As ExpressionSyntax
Dim semanticModel = _semanticModel
If expression.SyntaxTree IsNot oldExpression.SyntaxTree Then
Dim specAnalyzer = New SpeculationAnalyzer(oldExpression, expression, _semanticModel, _cancellationToken)
semanticModel = specAnalyzer.SpeculativeSemanticModel
If semanticModel Is Nothing Then
Return expression
End If
expression = specAnalyzer.ReplacedExpression
End If
Return AddCast(expression, targetType, semanticModel)
End Function
Private Function AddCast(expression As ExpressionSyntax, targetType As ITypeSymbol, semanticModel As SemanticModel) As ExpressionSyntax
Dim wasCastAdded As Boolean = False
Dim result = expression.CastIfPossible(targetType, expression.SpanStart, semanticModel, wasCastAdded, _cancellationToken)
If wasCastAdded Then
result = result.Parenthesize()
End If
Return result
End Function
Private Function AddCasts(expression As ExpressionSyntax, typeInfo As TypeInfo, conversion As Conversion, oldExpression As ExpressionSyntax) As ExpressionSyntax
Dim result = expression
If typeInfo.Type IsNot Nothing AndAlso
typeInfo.Type.IsAnonymousDelegateType() AndAlso
conversion.IsUserDefined AndAlso
conversion.IsWidening AndAlso
conversion.MethodSymbol IsNot Nothing AndAlso
conversion.MethodSymbol.Parameters.Length > 0 Then
Dim conversionType = conversion.MethodSymbol.Parameters(0).Type
If conversionType IsNot Nothing Then
result = AddCast(result, conversionType, oldExpression)
End If
End If
If typeInfo.ConvertedType IsNot Nothing Then
result = AddCast(result, typeInfo.ConvertedType, oldExpression)
End If
' If we didn't add a cast, at least parenthesize the expression.
If result Is expression Then
result = result.Parenthesize()
End If
Return result
End Function
Public Overrides Function VisitParameter(node As ParameterSyntax) As SyntaxNode
Dim newNode = DirectCast(MyBase.VisitParameter(node), ParameterSyntax)
If newNode IsNot Nothing AndAlso newNode.AsClause Is Nothing AndAlso _expandParameter Then
Dim newNodeSymbol = _semanticModel.GetDeclaredSymbol(node)
If newNodeSymbol IsNot Nothing AndAlso newNodeSymbol.Kind = SymbolKind.Parameter Then
Dim symbolType = newNodeSymbol.Type
If symbolType IsNot Nothing Then
Dim typeSyntax = symbolType.GenerateTypeSyntax(True)
Dim asClause = SyntaxFactory.SimpleAsClause(typeSyntax).NormalizeWhitespace()
If newNode.Default Is Nothing Then
Dim newAsClause = asClause.WithTrailingTrivia(newNode.Identifier.GetTrailingTrivia())
Dim newIdentifier = newNode.Identifier.WithTrailingTrivia({SyntaxFactory.WhitespaceTrivia(" ")}.ToSyntaxTriviaList())
Return SyntaxFactory.Parameter(newNode.AttributeLists, newNode.Modifiers, newIdentifier, newAsClause, newNode.Default) _
.WithAdditionalAnnotations(Simplifier.Annotation)
End If
Return SyntaxFactory.Parameter(newNode.AttributeLists, newNode.Modifiers, newNode.Identifier, asClause, newNode.Default).WithAdditionalAnnotations(Simplifier.Annotation)
End If
End If
End If
Return newNode
End Function
Public Overrides Function VisitAssignmentStatement(node As AssignmentStatementSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newAssignment = DirectCast(MyBase.VisitAssignmentStatement(node), AssignmentStatementSyntax)
Dim typeInfo = _semanticModel.GetTypeInfo(node.Right)
Dim conversion = _semanticModel.GetConversion(node.Right)
Dim newExpression = AddCasts(newAssignment.Right, typeInfo, conversion, node.Right)
newAssignment = newAssignment _
.WithRight(newExpression) _
.WithAdditionalAnnotations(Simplifier.Annotation)
Return newAssignment
End Function
Public Overrides Function VisitExpressionStatement(node As ExpressionStatementSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newExpressionStatement = DirectCast(MyBase.VisitExpressionStatement(node), ExpressionStatementSyntax)
If newExpressionStatement.Expression.IsKind(SyntaxKind.InvocationExpression) Then
' move all leading trivia before the call keyword
Dim leadingTrivia = newExpressionStatement.GetLeadingTrivia()
newExpressionStatement = newExpressionStatement.WithLeadingTrivia({SyntaxFactory.WhitespaceTrivia(" ")}.ToSyntaxTriviaList())
Dim callStatement = SyntaxFactory.CallStatement(newExpressionStatement.Expression) _
.WithLeadingTrivia(leadingTrivia) _
.WithAdditionalAnnotations(Simplifier.Annotation)
' copy over annotations, if any.
callStatement = newExpressionStatement.CopyAnnotationsTo(callStatement)
Return callStatement
End If
Return newExpressionStatement
End Function
Public Overrides Function VisitEqualsValue(node As EqualsValueSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newEqualsValue = DirectCast(MyBase.VisitEqualsValue(node), EqualsValueSyntax)
If node.Value IsNot Nothing AndAlso Not node.Value.IsMissing AndAlso
newEqualsValue.Value IsNot Nothing AndAlso Not newEqualsValue.IsMissing Then
Dim typeInfo = _semanticModel.GetTypeInfo(node.Value)
Dim conversion = _semanticModel.GetConversion(node.Value)
Dim newValue = AddCasts(newEqualsValue.Value, typeInfo, conversion, node.Value)
newEqualsValue = newEqualsValue _
.WithValue(newValue) _
.WithAdditionalAnnotations(Simplifier.Annotation)
End If
Return newEqualsValue
End Function
Public Overrides Function VisitInvocationExpression(node As InvocationExpressionSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newInvocationExpression = DirectCast(MyBase.VisitInvocationExpression(node), InvocationExpressionSyntax)
' the argument for redim needs to be an LValue and therefore cannot be a cast or parenthesized expression.
If node.IsParentKind(SyntaxKind.ReDimKeyword) Then
Return newInvocationExpression
End If
If newInvocationExpression.ArgumentList Is Nothing Then
Dim trailingTrivia = newInvocationExpression.GetTrailingTrivia()
newInvocationExpression = newInvocationExpression _
.WithTrailingTrivia(SyntaxTriviaList.Empty) _
.WithArgumentList(SyntaxFactory.ArgumentList().WithTrailingTrivia(trailingTrivia)) _
.WithAdditionalAnnotations(Simplifier.Annotation)
End If
If node.Expression.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
Dim memberAccess = DirectCast(node.Expression, MemberAccessExpressionSyntax)
Dim targetSymbol = SimplificationHelpers.GetOriginalSymbolInfo(_semanticModel, memberAccess.Name)
If Not targetSymbol Is Nothing And targetSymbol.IsReducedExtension() AndAlso memberAccess.Expression IsNot Nothing Then
newInvocationExpression = RewriteExtensionMethodInvocation(node, newInvocationExpression, memberAccess.Expression, DirectCast(newInvocationExpression.Expression, MemberAccessExpressionSyntax).Expression, DirectCast(targetSymbol, IMethodSymbol))
End If
End If
Return newInvocationExpression
End Function
Private Function RewriteExtensionMethodInvocation(
originalNode As InvocationExpressionSyntax,
rewrittenNode As InvocationExpressionSyntax,
oldThisExpression As ExpressionSyntax,
thisExpression As ExpressionSyntax,
reducedExtensionMethod As IMethodSymbol) As InvocationExpressionSyntax
Dim originalMemberAccess = DirectCast(originalNode.Expression, MemberAccessExpressionSyntax)
If originalMemberAccess.GetRootConditionalAccessExpression() IsNot Nothing Then
' Bail out on extension method invocations in conditional access expression.
' Note that this is a temporary workaround for https://github.com/dotnet/roslyn/issues/2593.
' Issue https//github.com/dotnet/roslyn/issues/3260 tracks fixing this workaround.
Return rewrittenNode
End If
Dim expression = RewriteExtensionMethodInvocation(rewrittenNode, oldThisExpression, thisExpression, reducedExtensionMethod, typeNameFormatWithoutGenerics)
Dim binding = _semanticModel.GetSpeculativeSymbolInfo(originalNode.SpanStart, expression, SpeculativeBindingOption.BindAsExpression)
If (Not binding.Symbol Is Nothing) Then
Return expression
End If
' The previous binding did not work. So we are going to include the type arguments as well
Return RewriteExtensionMethodInvocation(rewrittenNode, oldThisExpression, thisExpression, reducedExtensionMethod, typeNameFormatWithGenerics)
End Function
Private Function RewriteExtensionMethodInvocation(
originalNode As InvocationExpressionSyntax,
oldThisExpression As ExpressionSyntax,
thisExpression As ExpressionSyntax,
reducedExtensionMethod As IMethodSymbol,
symbolDisplayFormat As SymbolDisplayFormat) As InvocationExpressionSyntax
Dim containingType = reducedExtensionMethod.ContainingType.ToDisplayString(symbolDisplayFormat)
Dim oldMemberAccess = DirectCast(originalNode.Expression, MemberAccessExpressionSyntax)
Dim newMemberAccess = SyntaxFactory.SimpleMemberAccessExpression(SyntaxFactory.ParseExpression(containingType), oldMemberAccess.OperatorToken, oldMemberAccess.Name).WithLeadingTrivia(thisExpression.GetFirstToken().LeadingTrivia)
' Copies the annotation for the member access expression
newMemberAccess = originalNode.Expression.CopyAnnotationsTo(newMemberAccess).WithAdditionalAnnotations(Simplifier.Annotation)
Dim typeInfo = _semanticModel.GetTypeInfo(oldThisExpression)
Dim conversion = _semanticModel.GetConversion(oldThisExpression)
Dim castedThisExpression = AddCasts(thisExpression, typeInfo, conversion, oldThisExpression)
Dim thisArgument = SyntaxFactory.SimpleArgument(castedThisExpression) _
.WithLeadingTrivia(SyntaxTriviaList.Empty) _
.WithAdditionalAnnotations(Simplifier.Annotation)
thisArgument = DirectCast(originalNode.Expression, MemberAccessExpressionSyntax).Expression.CopyAnnotationsTo(thisArgument)
Dim arguments = originalNode.ArgumentList.Arguments.Insert(0, thisArgument)
Dim replacementNode = SyntaxFactory.InvocationExpression(
newMemberAccess,
originalNode.ArgumentList.WithArguments(arguments))
Return originalNode.CopyAnnotationsTo(replacementNode).WithAdditionalAnnotations(Simplifier.Annotation)
End Function
Public Overrides Function VisitObjectCreationExpression(node As ObjectCreationExpressionSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newObjectCreationExpression = DirectCast(MyBase.VisitObjectCreationExpression(node), ObjectCreationExpressionSyntax)
If newObjectCreationExpression.ArgumentList Is Nothing Then
Dim trailingTrivia = newObjectCreationExpression.Type.GetTrailingTrivia()
newObjectCreationExpression = newObjectCreationExpression _
.WithType(newObjectCreationExpression.Type.WithTrailingTrivia(SyntaxTriviaList.Empty)) _
.WithArgumentList(SyntaxFactory.ArgumentList().WithTrailingTrivia(trailingTrivia)) _
.WithAdditionalAnnotations(Simplifier.Annotation)
End If
Return newObjectCreationExpression
End Function
Public Overrides Function VisitSimpleArgument(node As SimpleArgumentSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newSimpleArgument = DirectCast(MyBase.VisitSimpleArgument(node), SimpleArgumentSyntax)
If node.NameColonEquals Is Nothing Then
Dim tuple = TryCast(node.Parent, TupleExpressionSyntax)
If tuple IsNot Nothing Then
Dim inferredName = node.Expression.TryGetInferredMemberName()
If CanMakeNameExplicitInTuple(tuple, inferredName) Then
Dim identifier = SyntaxFactory.Identifier(inferredName)
identifier = TryEscapeIdentifierToken(identifier)
newSimpleArgument = newSimpleArgument.
WithLeadingTrivia().
WithNameColonEquals(SyntaxFactory.NameColonEquals(SyntaxFactory.IdentifierName(identifier))).
WithAdditionalAnnotations(Simplifier.Annotation).
WithLeadingTrivia(node.GetLeadingTrivia())
End If
End If
End If
' We need to be careful here. if this is a local, field or property passed to a ByRef argument, we shouldn't
' parenthesize to avoid breaking copy-back semantics.
Dim symbol = _semanticModel.GetSymbolInfo(node.Expression, _cancellationToken).Symbol
If symbol IsNot Nothing Then
If symbol.MatchesKind(SymbolKind.Local, SymbolKind.Field, SymbolKind.Property) Then
Dim parameter = node.DetermineParameter(_semanticModel, cancellationToken:=_cancellationToken)
If parameter IsNot Nothing AndAlso
parameter.RefKind <> RefKind.None Then
Return newSimpleArgument
End If
End If
End If
If newSimpleArgument.Expression.Kind = SyntaxKind.AddressOfExpression Then
Return newSimpleArgument
End If
Dim typeInfo = _semanticModel.GetTypeInfo(node.Expression)
Dim conversion = _semanticModel.GetConversion(node.Expression)
Dim newExpression = AddCasts(newSimpleArgument.Expression, typeInfo, conversion, node.Expression)
newSimpleArgument = newSimpleArgument _
.WithExpression(newExpression) _
.WithAdditionalAnnotations(Simplifier.Annotation)
Return newSimpleArgument
End Function
Private Shared Function CanMakeNameExplicitInTuple(tuple As TupleExpressionSyntax, name As String) As Boolean
If name Is Nothing OrElse SyntaxFacts.IsReservedTupleElementName(name) Then
Return False
End If
Dim found = False
For Each argument In tuple.Arguments
Dim elementName As Object
If argument.NameColonEquals IsNot Nothing Then
elementName = argument.NameColonEquals.Name.Identifier.ValueText
Else
elementName = argument.Expression?.TryGetInferredMemberName()
End If
If CaseInsensitiveComparison.Equals(elementName, name) Then
If found Then
' No duplicate names allowed
Return False
End If
found = True
End If
Next
Return True
End Function
Public Overrides Function VisitInferredFieldInitializer(node As InferredFieldInitializerSyntax) As SyntaxNode
Dim newInitializer = TryCast(MyBase.VisitInferredFieldInitializer(node), InferredFieldInitializerSyntax)
If newInitializer IsNot Nothing Then
Dim inferredName = node.Expression.TryGetInferredMemberName()
If inferredName IsNot Nothing Then
Dim identifier = SyntaxFactory.Identifier(inferredName)
identifier = TryEscapeIdentifierToken(identifier)
Return SyntaxFactory.NamedFieldInitializer(SyntaxFactory.IdentifierName(identifier), newInitializer.Expression.WithoutLeadingTrivia()).
WithLeadingTrivia(node.GetLeadingTrivia()).
WithAdditionalAnnotations(Simplifier.Annotation)
End If
End If
Return newInitializer
End Function
Public Overrides Function VisitGenericName(node As GenericNameSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newNode = DirectCast(MyBase.VisitGenericName(node), SimpleNameSyntax)
Return VisitSimpleName(newNode, node)
End Function
Public Overrides Function VisitSingleLineLambdaExpression(node As SingleLineLambdaExpressionSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim baseSingleLineLambda = DirectCast(MyBase.VisitSingleLineLambdaExpression(node), SingleLineLambdaExpressionSyntax)
Dim newSingleLineLambda = baseSingleLineLambda _
.Parenthesize()
Return newSingleLineLambda
End Function
Public Overrides Function VisitQualifiedName(node As QualifiedNameSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim rewrittenQualifiedName = MyBase.VisitQualifiedName(node)
Dim symbolForQualifiedName = _semanticModel.GetSymbolInfo(node).Symbol
If symbolForQualifiedName.IsConstructor Then
symbolForQualifiedName = symbolForQualifiedName.ContainingSymbol
End If
If symbolForQualifiedName.IsModuleMember Then
Dim symbolForLeftPart = _semanticModel.GetSymbolInfo(node.Left).Symbol
If Not symbolForQualifiedName.ContainingType.Equals(symbolForLeftPart) Then
' <rewritten_left>.<module_name>.<rewritten_right>
Dim moduleIdentifierToken = SyntaxFactory.Identifier(symbolForQualifiedName.ContainingType.Name)
moduleIdentifierToken = TryEscapeIdentifierToken(moduleIdentifierToken)
Dim qualifiedNameWithModuleName = rewrittenQualifiedName.CopyAnnotationsTo(SyntaxFactory.QualifiedName(
SyntaxFactory.QualifiedName(DirectCast(rewrittenQualifiedName, QualifiedNameSyntax).Left, SyntaxFactory.IdentifierName(moduleIdentifierToken)) _
.WithAdditionalAnnotations(Simplifier.Annotation, SimplificationHelpers.SimplifyModuleNameAnnotation),
DirectCast(rewrittenQualifiedName, QualifiedNameSyntax).Right))
If symbolForQualifiedName.Equals(_semanticModel.GetSpeculativeSymbolInfo(node.SpanStart, qualifiedNameWithModuleName, SpeculativeBindingOption.BindAsExpression).Symbol) Then
rewrittenQualifiedName = qualifiedNameWithModuleName
End If
End If
End If
Return rewrittenQualifiedName
End Function
Public Overrides Function VisitMemberAccessExpression(node As MemberAccessExpressionSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim rewrittenMemberAccess = MyBase.VisitMemberAccessExpression(node)
Dim symbolForMemberAccess = _semanticModel.GetSymbolInfo(node).Symbol
If node.Expression IsNot Nothing AndAlso symbolForMemberAccess.IsModuleMember Then
Dim symbolForLeftPart = _semanticModel.GetSymbolInfo(node.Expression).Symbol
If Not symbolForMemberAccess.ContainingType.Equals(symbolForLeftPart) Then
' <rewritten_left>.<module_name>.<rewritten_right>
Dim moduleIdentifierToken = SyntaxFactory.Identifier(symbolForMemberAccess.ContainingType.Name)
moduleIdentifierToken = TryEscapeIdentifierToken(moduleIdentifierToken)
Dim memberAccessWithModuleName = rewrittenMemberAccess.CopyAnnotationsTo(
SyntaxFactory.SimpleMemberAccessExpression(
SyntaxFactory.SimpleMemberAccessExpression(
DirectCast(rewrittenMemberAccess, MemberAccessExpressionSyntax).Expression,
node.OperatorToken,
SyntaxFactory.IdentifierName(moduleIdentifierToken)) _
.WithAdditionalAnnotations(Simplifier.Annotation, SimplificationHelpers.SimplifyModuleNameAnnotation),
node.OperatorToken,
DirectCast(rewrittenMemberAccess, MemberAccessExpressionSyntax).Name))
If symbolForMemberAccess.Equals(_semanticModel.GetSpeculativeSymbolInfo(node.SpanStart, memberAccessWithModuleName, SpeculativeBindingOption.BindAsExpression).Symbol) Then
rewrittenMemberAccess = memberAccessWithModuleName
End If
End If
End If
Return rewrittenMemberAccess
End Function
Public Overrides Function VisitIdentifierName(node As IdentifierNameSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newNode = DirectCast(MyBase.VisitIdentifierName(node), SimpleNameSyntax)
Return VisitSimpleName(newNode, node)
End Function
Private Function VisitSimpleName(rewrittenSimpleName As SimpleNameSyntax, originalSimpleName As SimpleNameSyntax) As ExpressionSyntax
_cancellationToken.ThrowIfCancellationRequested()
Dim identifier = rewrittenSimpleName.Identifier
Dim newNode As ExpressionSyntax = rewrittenSimpleName
'
' 1. if this identifier is an alias, we'll expand it here and replace the node completely.
'
If originalSimpleName.Kind = SyntaxKind.IdentifierName Then
Dim aliasInfo = _semanticModel.GetAliasInfo(DirectCast(originalSimpleName, IdentifierNameSyntax))
If aliasInfo IsNot Nothing Then
Dim aliasTarget = aliasInfo.Target
If aliasTarget.IsNamespace() AndAlso DirectCast(aliasTarget, INamespaceSymbol).IsGlobalNamespace Then
Return rewrittenSimpleName
End If
' if the enclosing expression is a typeof expression that already contains open type we cannot
' we need to insert an open type as well.
Dim typeOfExpression = originalSimpleName.GetAncestor(Of TypeOfExpressionSyntax)()
If typeOfExpression IsNot Nothing AndAlso IsTypeOfUnboundGenericType(_semanticModel, typeOfExpression) Then
aliasTarget = DirectCast(aliasTarget, INamedTypeSymbol).ConstructUnboundGenericType()
End If
' the expanded form replaces the current identifier name.
Dim replacement = FullyQualifyIdentifierName(
aliasTarget,
newNode,
originalSimpleName,
replaceNode:=True) _
.WithAdditionalAnnotations(Simplifier.Annotation)
If replacement.Kind = SyntaxKind.QualifiedName Then
Dim qualifiedReplacement = DirectCast(replacement, QualifiedNameSyntax)
Dim newIdentifier = identifier.CopyAnnotationsTo(qualifiedReplacement.Right.Identifier)
If Me._annotationForReplacedAliasIdentifier IsNot Nothing Then
newIdentifier = newIdentifier.WithAdditionalAnnotations(Me._annotationForReplacedAliasIdentifier)
End If
Dim aliasAnnotationInfo = AliasAnnotation.Create(aliasInfo.Name)
newIdentifier = newIdentifier.WithAdditionalAnnotations(aliasAnnotationInfo)
replacement = replacement.ReplaceNode(
qualifiedReplacement.Right,
qualifiedReplacement.Right.WithIdentifier(newIdentifier))
replacement = newNode.CopyAnnotationsTo(replacement)
Return replacement
End If
If replacement.IsKind(SyntaxKind.IdentifierName) Then
Dim identifierReplacement = DirectCast(replacement, IdentifierNameSyntax)
Dim newIdentifier = identifier.CopyAnnotationsTo(identifierReplacement.Identifier)
If Me._annotationForReplacedAliasIdentifier IsNot Nothing Then
newIdentifier = newIdentifier.WithAdditionalAnnotations(Me._annotationForReplacedAliasIdentifier)
End If
Dim aliasAnnotationInfo = AliasAnnotation.Create(aliasInfo.Name)
newIdentifier = newIdentifier.WithAdditionalAnnotations(aliasAnnotationInfo)
replacement = replacement.ReplaceToken(identifier, newIdentifier)
replacement = newNode.CopyAnnotationsTo(replacement)
Return replacement
End If
Throw New NotImplementedException()
End If
End If
Dim symbol = _semanticModel.GetSymbolInfo(originalSimpleName.Identifier, _cancellationToken).Symbol
If symbol Is Nothing Then
Return newNode
End If
'
' 2. If it's an attribute, make sure the identifier matches the attribute's class name without the attribute suffix.
'
If originalSimpleName.GetAncestor(Of AttributeSyntax)() IsNot Nothing Then
If symbol.IsConstructor() AndAlso symbol.ContainingType?.IsAttribute() Then
symbol = symbol.ContainingType
Dim name = symbol.Name
Debug.Assert(name.StartsWith(originalSimpleName.Identifier.ValueText, StringComparison.Ordinal))
' Note, VB can't escape attribute names like C#, so we actually need to expand to the symbol name
' without a suffix, see http://msdn.microsoft.com/en-us/library/aa711866(v=vs.71).aspx
Dim newName = String.Empty
If name.TryGetWithoutAttributeSuffix(isCaseSensitive:=False, result:=newName) Then
If identifier.ValueText <> newName Then
identifier = If(identifier.IsBracketed(),
identifier.CopyAnnotationsTo(SyntaxFactory.BracketedIdentifier(identifier.LeadingTrivia, newName, identifier.TrailingTrivia)),
identifier.CopyAnnotationsTo(SyntaxFactory.Identifier(identifier.LeadingTrivia, newName, identifier.TrailingTrivia)))
End If
' if the user already used the Attribute suffix in the attribute, we'll maintain it.
If identifier.ValueText = name Then
identifier = identifier.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation)
End If
End If
End If
End If
'
' 3. Always try to escape keyword identifiers
'
identifier = TryEscapeIdentifierToken(identifier)
If identifier <> rewrittenSimpleName.Identifier Then
Select Case newNode.Kind
Case SyntaxKind.IdentifierName,
SyntaxKind.GenericName
newNode = DirectCast(newNode, SimpleNameSyntax).WithIdentifier(identifier).WithAdditionalAnnotations(Simplifier.Annotation)
Case Else
Throw New NotImplementedException()
End Select
End If
Dim parent = originalSimpleName.Parent
' do not complexify further for location where only simple names are allowed
If (TypeOf (parent) Is FieldInitializerSyntax) OrElse
((TypeOf (parent) Is DeclarationStatementSyntax) AndAlso Not TypeOf (parent) Is InheritsOrImplementsStatementSyntax) OrElse
(TypeOf (parent) Is MemberAccessExpressionSyntax AndAlso parent.Kind <> SyntaxKind.SimpleMemberAccessExpression) OrElse
(parent.Kind = SyntaxKind.SimpleMemberAccessExpression AndAlso originalSimpleName.IsRightSideOfDot()) OrElse
(parent.Kind = SyntaxKind.QualifiedName AndAlso originalSimpleName.IsRightSideOfQualifiedName()) Then
Return TryAddTypeArgumentToIdentifierName(newNode, symbol)
End If
'
' 4. If this is a standalone identifier or the left side of a qualified name or member access try to fully qualify it
'
' we need to treat the constructor as type name, so just get the containing type.
If symbol.IsConstructor() AndAlso parent.Kind = SyntaxKind.ObjectCreationExpression Then
symbol = symbol.ContainingType
End If
' if it's a namespace or type name, fully qualify it.
If symbol.Kind = SymbolKind.NamedType OrElse symbol.Kind = SymbolKind.Namespace Then
Return FullyQualifyIdentifierName(
DirectCast(symbol, INamespaceOrTypeSymbol),
newNode,
originalSimpleName,
replaceNode:=False) _
.WithAdditionalAnnotations(Simplifier.Annotation)
End If
' if it's a member access, we're fully qualifying the left side and make it a member access.
If symbol.Kind = SymbolKind.Method OrElse
symbol.Kind = SymbolKind.Field OrElse
symbol.Kind = SymbolKind.Property Then
If symbol.IsStatic OrElse
(TypeOf (parent) Is CrefReferenceSyntax) OrElse
_semanticModel.SyntaxTree.IsNameOfContext(originalSimpleName.SpanStart, _cancellationToken) Then
newNode = FullyQualifyIdentifierName(
symbol,
newNode,
originalSimpleName,
replaceNode:=False)
Else
Dim left As ExpressionSyntax
If Not Equals(_semanticModel.GetEnclosingNamedType(originalSimpleName.SpanStart, _cancellationToken), symbol.ContainingType) Then
left = SyntaxFactory.MyBaseExpression()
Else
left = SyntaxFactory.MeExpression()
End If
Dim identifiersLeadingTrivia = newNode.GetLeadingTrivia()
newNode = TryAddTypeArgumentToIdentifierName(newNode, symbol)
newNode = SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
left,
SyntaxFactory.Token(SyntaxKind.DotToken),
DirectCast(newNode, SimpleNameSyntax).WithoutLeadingTrivia()) _
.WithLeadingTrivia(identifiersLeadingTrivia)
End If
newNode = newNode.WithAdditionalAnnotations(Simplifier.Annotation)
End If
Return newNode
End Function
Private Shared Function TryAddTypeArgumentToIdentifierName(
newNode As ExpressionSyntax,
symbol As ISymbol) As ExpressionSyntax
If newNode.Kind = SyntaxKind.IdentifierName AndAlso symbol.Kind = SymbolKind.Method Then
If DirectCast(symbol, IMethodSymbol).TypeArguments.Length <> 0 Then
Dim typeArguments = DirectCast(symbol, IMethodSymbol).TypeArguments
Dim genericName = SyntaxFactory.GenericName(
DirectCast(newNode, IdentifierNameSyntax).Identifier,
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList(typeArguments.Select(Function(p) SyntaxFactory.ParseTypeName(p.ToDisplayString(typeNameFormatWithGenerics)))))) _
.WithLeadingTrivia(newNode.GetLeadingTrivia()) _
.WithTrailingTrivia(newNode.GetTrailingTrivia()) _
.WithAdditionalAnnotations(Simplifier.Annotation)
genericName = newNode.CopyAnnotationsTo(genericName)
Return genericName
End If
End If
Return newNode
End Function
Private Function FullyQualifyIdentifierName(
symbol As ISymbol,
rewrittenNode As ExpressionSyntax,
originalNode As ExpressionSyntax,
replaceNode As Boolean
) As ExpressionSyntax
Debug.Assert(Not replaceNode OrElse rewrittenNode.Kind = SyntaxKind.IdentifierName)
' TODO: use and expand Generate*Syntax(isymbol) to not depend on symbol display any more.
' See GenerateExpressionSyntax();
Dim result = rewrittenNode
' only if this symbol has a containing type or namespace there is work for us to do.
If replaceNode OrElse symbol.ContainingType IsNot Nothing OrElse symbol.ContainingNamespace IsNot Nothing Then
Dim symbolForQualification = If(replaceNode, symbol, symbol.ContainingSymbol)
rewrittenNode = TryAddTypeArgumentToIdentifierName(rewrittenNode, symbol)
Dim displayParts = symbolForQualification.ToDisplayParts(typeNameFormatWithGenerics)
Dim left As ExpressionSyntax = SyntaxFactory.ParseTypeName(displayParts.ToDisplayString())
' symbol display always includes module names in the qualification, but this can sometimes break code
' (see bug 529837).
' if we don't get back the same symbol for the full qualification, then we'll omit the module name.
If symbol.IsModuleMember Then
Dim newSymbol = _semanticModel.GetSpeculativeSymbolInfo(originalNode.SpanStart, left, SpeculativeBindingOption.BindAsExpression).Symbol
If Not symbolForQualification.Equals(newSymbol) Then
displayParts = symbolForQualification.ContainingSymbol.ToDisplayParts(typeNameFormatWithGenerics)
left = SyntaxFactory.ParseTypeName(displayParts.ToDisplayString())
End If
End If
If replaceNode Then
result = left _
.WithLeadingTrivia(rewrittenNode.GetLeadingTrivia()) _
.WithTrailingTrivia(rewrittenNode.GetTrailingTrivia())
Debug.Assert(
symbol.Equals(_semanticModel.GetSpeculativeSymbolInfo(originalNode.SpanStart, result, SpeculativeBindingOption.BindAsExpression).Symbol))
Return result
End If
' now create syntax for the combination of left and right syntax, or a simple replacement in case of an identifier
Dim parent = originalNode.Parent
Dim leadingTrivia = rewrittenNode.GetLeadingTrivia()
rewrittenNode = rewrittenNode.WithoutLeadingTrivia()
Select Case parent.Kind
Case SyntaxKind.QualifiedName
result = rewrittenNode.CopyAnnotationsTo(
SyntaxFactory.QualifiedName(
DirectCast(left, NameSyntax),
DirectCast(rewrittenNode, SimpleNameSyntax)))
Case SyntaxKind.SimpleMemberAccessExpression
Dim memberAccessParent = DirectCast(parent, MemberAccessExpressionSyntax)
result = rewrittenNode.CopyAnnotationsTo(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
left,
memberAccessParent.OperatorToken,
DirectCast(rewrittenNode, SimpleNameSyntax)))
Case Else
Debug.Assert(TypeOf (rewrittenNode) Is SimpleNameSyntax)
If SyntaxFacts.IsInNamespaceOrTypeContext(originalNode) OrElse TypeOf (parent) Is CrefReferenceSyntax Then
Dim right = DirectCast(rewrittenNode, SimpleNameSyntax)
result = rewrittenNode.CopyAnnotationsTo(SyntaxFactory.QualifiedName(DirectCast(left, NameSyntax), right.WithAdditionalAnnotations(Simplifier.SpecialTypeAnnotation)))
Else
result = rewrittenNode.CopyAnnotationsTo(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
DirectCast(left, ExpressionSyntax),
SyntaxFactory.Token(SyntaxKind.DotToken),
DirectCast(rewrittenNode, SimpleNameSyntax)))
End If
End Select
result = result.WithLeadingTrivia(leadingTrivia)
End If
Return result
End Function
Private Function IsTypeOfUnboundGenericType(semanticModel As SemanticModel, typeOfExpression As TypeOfExpressionSyntax) As Boolean
If typeOfExpression IsNot Nothing Then
Dim type = TryCast(semanticModel.GetTypeInfo(typeOfExpression.Type, _cancellationToken).Type, INamedTypeSymbol)
' It's possible the immediate type might not be an unbound type, such as typeof(A<>.B). So walk through
' parent types too
Do While type IsNot Nothing
If type.IsUnboundGenericType Then
Return True
End If
type = type.ContainingType
Loop
End If
Return False
End Function
Public Overrides Function VisitLabelStatement(node As LabelStatementSyntax) As SyntaxNode
_cancellationToken.ThrowIfCancellationRequested()
Dim newLabelStatement = DirectCast(MyBase.VisitLabelStatement(node), LabelStatementSyntax)
Dim escapedLabelToken = TryEscapeIdentifierToken(newLabelStatement.LabelToken)
If newLabelStatement.LabelToken <> escapedLabelToken Then
newLabelStatement = newLabelStatement.WithLabelToken(escapedLabelToken)
End If
Return newLabelStatement
End Function
End Class
End Class
End Namespace
|