|
' 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 System.Composition
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Differencing
Imports Microsoft.CodeAnalysis.EditAndContinue
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Shared.Collections
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Friend NotInheritable Class VisualBasicEditAndContinueAnalyzer
Inherits AbstractEditAndContinueAnalyzer
<ExportLanguageServiceFactory(GetType(IEditAndContinueAnalyzer), LanguageNames.VisualBasic), [Shared]>
Private NotInheritable Class Factory
Implements ILanguageServiceFactory
<ImportingConstructor>
<Obsolete(MefConstruction.ImportingConstructorMessage, True)>
Public Sub New()
End Sub
Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService
Return New VisualBasicEditAndContinueAnalyzer(testFaultInjector:=Nothing)
End Function
End Class
' Public for testing purposes
Public Sub New(Optional testFaultInjector As Action(Of SyntaxNode) = Nothing)
MyBase.New(testFaultInjector)
End Sub
#Region "Syntax Analysis"
Friend Overrides Function TryFindMemberDeclaration(rootOpt As SyntaxNode, node As SyntaxNode, activeSpan As TextSpan, <Out> ByRef declarations As OneOrMany(Of SyntaxNode)) As Boolean
Dim current = node
While current IsNot rootOpt
Select Case current.Kind
Case SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.ConstructorBlock,
SyntaxKind.OperatorBlock,
SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock
declarations = OneOrMany.Create(current)
Return True
Case SyntaxKind.PropertyStatement
' Property a As Integer = 1
' Property a As New T
If Not current.Parent.IsKind(SyntaxKind.PropertyBlock) Then
declarations = OneOrMany.Create(current)
Return True
End If
Case SyntaxKind.VariableDeclarator
If current.Parent.IsKind(SyntaxKind.FieldDeclaration) Then
Dim variableDeclarator = CType(current, VariableDeclaratorSyntax)
If variableDeclarator.Names.Count = 1 Then
declarations = OneOrMany.Create(Of SyntaxNode)(variableDeclarator.Names(0))
Else
declarations = OneOrMany.Create(variableDeclarator.Names.SelectAsArray(Function(n) CType(n, SyntaxNode)))
End If
Return True
End If
Case SyntaxKind.ModifiedIdentifier
If current.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then
declarations = OneOrMany.Create(current)
Return True
End If
End Select
current = current.Parent
End While
declarations = Nothing
Return False
End Function
''' <returns>
''' Given a node representing a declaration or a top-level edit node returns:
''' - <see cref="MethodBlockBaseSyntax"/> for methods, constructors, operators and accessors.
''' - <see cref="ExpressionSyntax"/> for auto-properties and fields with initializer or AsNew clause.
''' - <see cref="ArgumentListSyntax"/> for fields with array initializer, e.g. "Dim a(1) As Integer".
''' A null reference otherwise.
''' </returns>
Friend Overrides Function TryGetDeclarationBody(node As SyntaxNode, symbol As ISymbol) As MemberBody
Return SyntaxUtilities.TryGetDeclarationBody(node)
End Function
Friend Overrides Function IsDeclarationWithSharedBody(declaration As SyntaxNode, member As ISymbol) As Boolean
If declaration.Kind = SyntaxKind.ModifiedIdentifier AndAlso declaration.Parent.Kind = SyntaxKind.VariableDeclarator Then
Dim variableDeclarator = CType(declaration.Parent, VariableDeclaratorSyntax)
Return variableDeclarator.Names.Count > 1 AndAlso variableDeclarator.Initializer IsNot Nothing OrElse SyntaxUtilities.HasAsNewClause(variableDeclarator)
End If
Return False
End Function
Protected Overrides Function GetVariableUseSites(roots As IEnumerable(Of SyntaxNode), localOrParameter As ISymbol, model As SemanticModel, cancellationToken As CancellationToken) As IEnumerable(Of SyntaxNode)
Debug.Assert(TypeOf localOrParameter Is IParameterSymbol OrElse TypeOf localOrParameter Is ILocalSymbol OrElse TypeOf localOrParameter Is IRangeVariableSymbol)
' Not supported (it's non trivial to find all places where "this" is used):
Debug.Assert(Not localOrParameter.IsThisParameter())
Return From root In roots
From node In root.DescendantNodesAndSelf()
Where node.IsKind(SyntaxKind.IdentifierName)
Let identifier = DirectCast(node, IdentifierNameSyntax)
Where String.Equals(DirectCast(identifier.Identifier.Value, String), localOrParameter.Name, StringComparison.OrdinalIgnoreCase) AndAlso
If(model.GetSymbolInfo(identifier, cancellationToken).Symbol?.Equals(localOrParameter), False)
Select node
End Function
Friend Shared Function FindStatementAndPartner(
span As TextSpan,
body As SyntaxNode,
partnerBody As SyntaxNode,
<Out> ByRef partnerStatement As SyntaxNode,
<Out> ByRef statementPart As Integer) As SyntaxNode
Dim position = span.Start
If Not body.FullSpan.Contains(position) Then
' invalid position, let's find a labeled node that encompasses the body:
position = body.SpanStart
End If
Dim node As SyntaxNode = Nothing
If partnerBody IsNot Nothing Then
FindLeafNodeAndPartner(body, position, partnerBody, node, partnerStatement)
Else
node = body.FindToken(position).Parent
partnerStatement = Nothing
End If
' In some cases active statements may start at the same position.
' Consider a nested lambda:
' Function(a) [|[|Function(b)|] a + b|]
' There are 2 active statements, one spanning the the body of the outer lambda and
' the other on the nested lambda's header.
' Find the parent whose span starts at the same position but it's length is at least as long as the active span's length.
While node.Span.Length < span.Length AndAlso node.Parent.SpanStart = position
node = node.Parent
partnerStatement = partnerStatement?.Parent
End While
Debug.Assert(node IsNot Nothing)
While node IsNot body AndAlso
Not SyntaxComparer.Statement.HasLabel(node) AndAlso
Not LambdaUtilities.IsLambdaBodyStatementOrExpression(node)
node = node.Parent
If partnerStatement IsNot Nothing Then
partnerStatement = partnerStatement.Parent
End If
End While
' In case of local variable declaration an active statement may start with a modified identifier.
' If it is a declaration with a simple initializer we want the resulting statement to be the declaration,
' not the identifier.
If node.IsKind(SyntaxKind.ModifiedIdentifier) AndAlso
node.Parent.IsKind(SyntaxKind.VariableDeclarator) AndAlso
DirectCast(node.Parent, VariableDeclaratorSyntax).Names.Count = 1 Then
node = node.Parent
End If
Return node
End Function
Friend Overrides Function IsClosureScope(node As SyntaxNode) As Boolean
Return LambdaUtilities.IsClosureScope(node)
End Function
Friend Overrides Function GetCapturedParameterScope(methodOrLambda As SyntaxNode) As SyntaxNode
Return methodOrLambda
End Function
Protected Overrides Function FindEnclosingLambdaBody(encompassingAncestor As SyntaxNode, node As SyntaxNode) As LambdaBody
While node IsNot encompassingAncestor And node IsNot Nothing
Dim body As SyntaxNode = Nothing
If LambdaUtilities.IsLambdaBodyStatementOrExpression(node, body) Then
Return SyntaxUtilities.CreateLambdaBody(body)
End If
node = node.Parent
End While
Return Nothing
End Function
Protected Overrides Function ComputeTopLevelMatch(oldCompilationUnit As SyntaxNode, newCompilationUnit As SyntaxNode) As Match(Of SyntaxNode)
Return SyntaxComparer.TopLevel.ComputeMatch(oldCompilationUnit, newCompilationUnit)
End Function
Protected Overrides Function ComputeParameterMap(oldDeclaration As SyntaxNode, newDeclaration As SyntaxNode) As BidirectionalMap(Of SyntaxNode)?
Dim oldParameterLists = GetDeclarationParameterLists(oldDeclaration)
Dim newParameterLists = GetDeclarationParameterLists(newDeclaration)
Dim primaryMatch = GetTopLevelMatch(oldParameterLists.Primary, newParameterLists.Primary)
Dim secondaryMatch = GetTopLevelMatch(oldParameterLists.Secondary, newParameterLists.Secondary)
If primaryMatch Is Nothing AndAlso secondaryMatch Is Nothing Then
Return Nothing
End If
Dim map = BidirectionalMap(Of SyntaxNode).FromMatch(If(primaryMatch, secondaryMatch))
If primaryMatch IsNot Nothing AndAlso secondaryMatch IsNot Nothing Then
map = map.WithMatch(secondaryMatch)
End If
Return map
End Function
Private Shared Function GetTopLevelMatch(oldNode As SyntaxNode, newNode As SyntaxNode) As Match(Of SyntaxNode)
Return If(oldNode IsNot Nothing AndAlso newNode IsNot Nothing, SyntaxComparer.TopLevel.ComputeMatch(oldNode, newNode), Nothing)
End Function
Private Shared Function GetDeclarationParameterLists(declaration As SyntaxNode) As (Primary As SyntaxNode, Secondary As SyntaxNode)
Select Case declaration.Kind
' Indexer accessor may have two parameter lists: one on the property and ther other on the accessor
Case SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock
Return (DirectCast(declaration.Parent, PropertyBlockSyntax).PropertyStatement.ParameterList,
DirectCast(declaration, AccessorBlockSyntax).AccessorStatement.ParameterList)
Case SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock
Return (DirectCast(declaration, AccessorBlockSyntax).AccessorStatement.ParameterList, Nothing)
End Select
Return (declaration.GetParameterList(), Nothing)
End Function
#End Region
#Region "Syntax And Semantic Utils"
Protected Overrides Function IsNamespaceDeclaration(node As SyntaxNode) As Boolean
' An edit can operate on either just the statement (update) or the whole block (move, insert, delete).
Return node.IsKind(SyntaxKind.NamespaceStatement, SyntaxKind.NamespaceBlock)
End Function
Private Shared Function IsTypeDeclaration(node As SyntaxNode) As Boolean
Return TypeOf node Is TypeBlockSyntax OrElse TypeOf node Is DelegateStatementSyntax OrElse TypeOf node Is EnumBlockSyntax
End Function
Protected Overrides Function IsCompilationUnitWithGlobalStatements(node As SyntaxNode) As Boolean
Return False
End Function
Protected Overrides Function IsGlobalStatement(node As SyntaxNode) As Boolean
Return False
End Function
Protected Overrides Iterator Function GetTopLevelTypeDeclarations(compilationUnit As SyntaxNode) As IEnumerable(Of SyntaxNode)
Dim stack = ArrayBuilder(Of SyntaxList(Of StatementSyntax)).GetInstance()
stack.Add(DirectCast(compilationUnit, CompilationUnitSyntax).Members)
While stack.Count > 0
Dim members = stack.Last()
stack.RemoveLast()
For Each member In members
If IsTypeDeclaration(member) Then
Yield member
End If
Dim namespaceBlock = TryCast(member, NamespaceBlockSyntax)
If namespaceBlock IsNot Nothing Then
stack.Add(namespaceBlock.Members)
End If
Next
End While
End Function
Protected Overrides ReadOnly Property LineDirectiveKeyword As String
Get
Return "ExternalSource"
End Get
End Property
Protected Overrides ReadOnly Property LineDirectiveSyntaxKind As UShort
Get
Return SyntaxKind.ExternalSourceDirectiveTrivia
End Get
End Property
Protected Overrides Function GetSyntaxSequenceEdits(oldNodes As ImmutableArray(Of SyntaxNode), newNodes As ImmutableArray(Of SyntaxNode)) As IEnumerable(Of SequenceEdit)
Return SyntaxComparer.GetSequenceEdits(oldNodes, newNodes)
End Function
Friend Overrides ReadOnly Property EmptyCompilationUnit As SyntaxNode
Get
Return SyntaxFactory.CompilationUnit()
End Get
End Property
Friend Overrides Function ExperimentalFeaturesEnabled(tree As SyntaxTree) As Boolean
' There are no experimental features at this time.
Return False
End Function
Protected Overrides Function StatementLabelEquals(node1 As SyntaxNode, node2 As SyntaxNode) As Boolean
Return SyntaxComparer.Statement.GetLabelImpl(node1) = SyntaxComparer.Statement.GetLabelImpl(node2)
End Function
Private Shared Function ChildrenCompiledInBody(node As SyntaxNode) As Boolean
Return Not node.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) AndAlso
Not node.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) AndAlso
Not node.IsKind(SyntaxKind.MultiLineSubLambdaExpression) AndAlso
Not node.IsKind(SyntaxKind.SingleLineSubLambdaExpression)
End Function
Protected Overrides Function TryGetEnclosingBreakpointSpan(token As SyntaxToken, <Out> ByRef span As TextSpan) As Boolean
Return BreakpointSpans.TryGetClosestBreakpointSpan(token.Parent, token.SpanStart, minLength:=token.Span.Length, span)
End Function
Protected Overrides Function TryGetActiveSpan(node As SyntaxNode, statementPart As Integer, minLength As Integer, <Out> ByRef span As TextSpan) As Boolean
Return BreakpointSpans.TryGetClosestBreakpointSpan(node, node.SpanStart, minLength, span)
End Function
Protected Overrides Iterator Function EnumerateNearStatements(statement As SyntaxNode) As IEnumerable(Of ValueTuple(Of SyntaxNode, Integer))
Dim direction As Integer = +1
Dim nodeOrToken As SyntaxNodeOrToken = statement
Dim propertyOrFieldModifiers As SyntaxTokenList? = GetFieldOrPropertyModifiers(statement)
While True
' If the current statement is the last statement of if-block or try-block statements
' pretend there are no siblings following it.
Dim lastBlockStatement As SyntaxNode = Nothing
If nodeOrToken.Parent IsNot Nothing Then
If nodeOrToken.Parent.IsKind(SyntaxKind.MultiLineIfBlock) Then
lastBlockStatement = DirectCast(nodeOrToken.Parent, MultiLineIfBlockSyntax).Statements.LastOrDefault()
ElseIf nodeOrToken.Parent.IsKind(SyntaxKind.SingleLineIfStatement) Then
lastBlockStatement = DirectCast(nodeOrToken.Parent, SingleLineIfStatementSyntax).Statements.LastOrDefault()
ElseIf nodeOrToken.Parent.IsKind(SyntaxKind.TryBlock) Then
lastBlockStatement = DirectCast(nodeOrToken.Parent, TryBlockSyntax).Statements.LastOrDefault()
End If
End If
If direction > 0 Then
If lastBlockStatement IsNot Nothing AndAlso nodeOrToken.AsNode() Is lastBlockStatement Then
nodeOrToken = Nothing
Else
nodeOrToken = nodeOrToken.GetNextSibling()
End If
Else
nodeOrToken = nodeOrToken.GetPreviousSibling()
If lastBlockStatement IsNot Nothing AndAlso nodeOrToken.AsNode() Is lastBlockStatement Then
nodeOrToken = Nothing
End If
End If
If nodeOrToken.RawKind = 0 Then
Dim parent = statement.Parent
If parent Is Nothing Then
Return
End If
If direction > 0 Then
nodeOrToken = statement
direction = -1
Continue While
End If
If propertyOrFieldModifiers.HasValue Then
Yield (statement, -1)
End If
nodeOrToken = parent
statement = parent
propertyOrFieldModifiers = GetFieldOrPropertyModifiers(statement)
direction = +1
End If
Dim node = nodeOrToken.AsNode()
If node Is Nothing Then
Continue While
End If
If propertyOrFieldModifiers.HasValue Then
Dim nodeModifiers = GetFieldOrPropertyModifiers(node)
If Not nodeModifiers.HasValue OrElse
propertyOrFieldModifiers.Value.Any(SyntaxKind.SharedKeyword) <> nodeModifiers.Value.Any(SyntaxKind.SharedKeyword) Then
Continue While
End If
End If
Yield (node, DefaultStatementPart)
End While
End Function
Private Shared Function GetFieldOrPropertyModifiers(node As SyntaxNode) As SyntaxTokenList?
If node.IsKind(SyntaxKind.FieldDeclaration) Then
Return DirectCast(node, FieldDeclarationSyntax).Modifiers
ElseIf node.IsKind(SyntaxKind.PropertyStatement) Then
Return DirectCast(node, PropertyStatementSyntax).Modifiers
Else
Return Nothing
End If
End Function
Private Shared Function AreEquivalentIgnoringLambdaBodies(left As SyntaxNode, right As SyntaxNode) As Boolean
' usual case
If SyntaxFactory.AreEquivalent(left, right) Then
Return True
End If
Return LambdaUtilities.AreEquivalentIgnoringLambdaBodies(left, right)
End Function
Protected Overrides Function AreEquivalentActiveStatements(oldStatement As SyntaxNode, newStatement As SyntaxNode, statementPart As Integer) As Boolean
If oldStatement.RawKind <> newStatement.RawKind Then
Return False
End If
' Dim a,b,c As <NewExpression>
' We need to check the actual initializer expression in addition to the identifier.
If HasMultiInitializer(oldStatement) Then
Return AreEquivalentIgnoringLambdaBodies(oldStatement, newStatement) AndAlso
AreEquivalentIgnoringLambdaBodies(DirectCast(oldStatement.Parent, VariableDeclaratorSyntax).AsClause,
DirectCast(newStatement.Parent, VariableDeclaratorSyntax).AsClause)
End If
Select Case oldStatement.Kind
Case SyntaxKind.SubNewStatement,
SyntaxKind.SubStatement,
SyntaxKind.SubNewStatement,
SyntaxKind.FunctionStatement,
SyntaxKind.OperatorStatement,
SyntaxKind.GetAccessorStatement,
SyntaxKind.SetAccessorStatement,
SyntaxKind.AddHandlerAccessorStatement,
SyntaxKind.RemoveHandlerAccessorStatement,
SyntaxKind.RaiseEventAccessorStatement
' Header statements are nops. Changes in the header statements are changes in top-level surface
' which should not be reported as active statement rude edits.
Return True
Case Else
Return AreEquivalentIgnoringLambdaBodies(oldStatement, newStatement)
End Select
End Function
Private Shared Function HasMultiInitializer(modifiedIdentifier As SyntaxNode) As Boolean
Return modifiedIdentifier.Parent.IsKind(SyntaxKind.VariableDeclarator) AndAlso
DirectCast(modifiedIdentifier.Parent, VariableDeclaratorSyntax).Names.Count > 1
End Function
Protected Overrides Function AreEquivalentImpl(oldToken As SyntaxToken, newToken As SyntaxToken) As Boolean
Return SyntaxFactory.AreEquivalent(oldToken, newToken)
End Function
Friend Overrides Function IsInterfaceDeclaration(node As SyntaxNode) As Boolean
Return node.IsKind(SyntaxKind.InterfaceBlock)
End Function
Friend Overrides Function IsRecordDeclaration(node As SyntaxNode) As Boolean
' No records in VB
Return False
End Function
Friend Overrides Function TryGetContainingTypeDeclaration(node As SyntaxNode) As SyntaxNode
Return node.Parent.FirstAncestorOrSelf(Of TypeBlockSyntax)() ' TODO: EnbumBlock?
End Function
Friend Overrides Function IsDeclarationWithInitializer(declaration As SyntaxNode) As Boolean
Select Case declaration.Kind
Case SyntaxKind.ModifiedIdentifier
Debug.Assert(declaration.Parent.IsKind(SyntaxKind.VariableDeclarator) OrElse
declaration.Parent.IsKind(SyntaxKind.Parameter))
If Not declaration.Parent.IsKind(SyntaxKind.VariableDeclarator) Then
Return False
End If
Dim declarator = DirectCast(declaration.Parent, VariableDeclaratorSyntax)
Dim identifier = DirectCast(declaration, ModifiedIdentifierSyntax)
Return identifier.ArrayBounds IsNot Nothing OrElse
GetInitializerExpression(declarator.Initializer, declarator.AsClause) IsNot Nothing
Case SyntaxKind.PropertyStatement
Dim propertyStatement = DirectCast(declaration, PropertyStatementSyntax)
Return GetInitializerExpression(propertyStatement.Initializer, propertyStatement.AsClause) IsNot Nothing
Case Else
Return False
End Select
End Function
Friend Overrides Function IsPrimaryConstructorDeclaration(declaration As SyntaxNode) As Boolean
Return False
End Function
Private Shared Function GetInitializerExpression(equalsValue As EqualsValueSyntax, asClause As AsClauseSyntax) As ExpressionSyntax
If equalsValue IsNot Nothing Then
Return equalsValue.Value
End If
If asClause IsNot Nothing AndAlso asClause.IsKind(SyntaxKind.AsNewClause) Then
Return DirectCast(asClause, AsNewClauseSyntax).NewExpression
End If
Return Nothing
End Function
''' <summary>
''' VB symbols return references that represent the declaration statement.
''' The node that represents the whole declaration (the block) is the parent node if it exists.
''' For example, a method with a body is represented by a SubBlock/FunctionBlock while a method without a body
''' is represented by its declaration statement.
''' </summary>
Protected Overrides Function GetSymbolDeclarationSyntax(symbol As ISymbol, selector As Func(Of ImmutableArray(Of SyntaxReference), SyntaxReference), cancellationToken As CancellationToken) As SyntaxNode
' Invoke method of a delegate type doesn't have DeclaringSyntaxReferences
Dim syntaxRefs As ImmutableArray(Of SyntaxReference)
If symbol Is symbol.ContainingType?.DelegateInvokeMethod Then
syntaxRefs = symbol.ContainingType.DeclaringSyntaxReferences
If syntaxRefs.IsEmpty Then
Dim parameter = DirectCast(symbol, IMethodSymbol).Parameters.First()
Return parameter.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken).Parent.Parent
End If
Else
syntaxRefs = symbol.DeclaringSyntaxReferences
End If
Dim syntax = selector(syntaxRefs)?.GetSyntax(cancellationToken)
If syntax Is Nothing Then
Return Nothing
End If
Dim parent = syntax.Parent
Select Case syntax.Kind
' declarations that always have block
Case SyntaxKind.NamespaceStatement
Debug.Assert(parent.Kind = SyntaxKind.NamespaceBlock)
Return parent
Case SyntaxKind.ClassStatement
Debug.Assert(parent.Kind = SyntaxKind.ClassBlock)
Return parent
Case SyntaxKind.StructureStatement
Debug.Assert(parent.Kind = SyntaxKind.StructureBlock)
Return parent
Case SyntaxKind.InterfaceStatement
Debug.Assert(parent.Kind = SyntaxKind.InterfaceBlock)
Return parent
Case SyntaxKind.ModuleStatement
Debug.Assert(parent.Kind = SyntaxKind.ModuleBlock)
Return parent
Case SyntaxKind.EnumStatement
Debug.Assert(parent.Kind = SyntaxKind.EnumBlock)
Return parent
Case SyntaxKind.SubNewStatement
Debug.Assert(parent.Kind = SyntaxKind.ConstructorBlock)
Return parent
Case SyntaxKind.OperatorStatement
Debug.Assert(parent.Kind = SyntaxKind.OperatorBlock)
Return parent
Case SyntaxKind.GetAccessorStatement
Debug.Assert(parent.Kind = SyntaxKind.GetAccessorBlock)
Return parent
Case SyntaxKind.SetAccessorStatement
Debug.Assert(parent.Kind = SyntaxKind.SetAccessorBlock)
Return parent
Case SyntaxKind.AddHandlerAccessorStatement
Debug.Assert(parent.Kind = SyntaxKind.AddHandlerAccessorBlock)
Return parent
Case SyntaxKind.RemoveHandlerAccessorStatement
Debug.Assert(parent.Kind = SyntaxKind.RemoveHandlerAccessorBlock)
Return parent
Case SyntaxKind.RaiseEventAccessorStatement
Debug.Assert(parent.Kind = SyntaxKind.RaiseEventAccessorBlock)
Return parent
' declarations that may or may not have block
Case SyntaxKind.SubStatement
Return If(parent.Kind = SyntaxKind.SubBlock, parent, syntax)
Case SyntaxKind.FunctionStatement
Return If(parent.Kind = SyntaxKind.FunctionBlock, parent, syntax)
Case SyntaxKind.PropertyStatement
Return If(parent.Kind = SyntaxKind.PropertyBlock, parent, syntax)
Case SyntaxKind.EventStatement
Return If(parent.Kind = SyntaxKind.EventBlock, parent, syntax)
' declarations that never have a block
Case SyntaxKind.ModifiedIdentifier
' Field defined in a field declaration, or a locla variable defined in local declaration, For Each, For, Using, etc.
Return syntax
Case SyntaxKind.VariableDeclarator
' fields are represented by ModifiedIdentifier:
Throw ExceptionUtilities.UnexpectedValue(syntax.Kind)
Case Else
Return syntax
End Select
End Function
Protected Overrides Function GetDeclaredSymbol(model As SemanticModel, declaration As SyntaxNode, cancellationToken As CancellationToken) As ISymbol
Return model.GetDeclaredSymbol(declaration, cancellationToken)
End Function
Friend Overrides Function IsConstructorWithMemberInitializers(symbol As ISymbol, cancellationToken As CancellationToken) As Boolean
Dim method = TryCast(symbol, IMethodSymbol)
If method Is Nothing OrElse (method.MethodKind <> MethodKind.Constructor AndAlso method.MethodKind <> MethodKind.SharedConstructor) Then
Return False
End If
' static constructor has initializers:
If method.IsStatic Then
Return True
End If
' Default constructor has initializers unless the type is a struct.
' Instance member initializers in a struct are not supported in VB.
If method.IsImplicitlyDeclared Then
Return method.ContainingType.TypeKind <> TypeKind.Struct
End If
Dim ctor = TryCast(symbol.DeclaringSyntaxReferences(0).GetSyntax(cancellationToken).Parent, ConstructorBlockSyntax)
If ctor Is Nothing Then
Return False
End If
' Constructor includes field initializers if the first statement
' isn't a call to another constructor of the declaring class or module.
If ctor.Statements.Count = 0 Then
Return True
End If
Dim firstStatement = ctor.Statements.First
If Not firstStatement.IsKind(SyntaxKind.ExpressionStatement) Then
Return True
End If
Dim expressionStatement = DirectCast(firstStatement, ExpressionStatementSyntax)
If Not expressionStatement.Expression.IsKind(SyntaxKind.InvocationExpression) Then
Return True
End If
Dim invocation = DirectCast(expressionStatement.Expression, InvocationExpressionSyntax)
If Not invocation.Expression.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
Return True
End If
Dim memberAccess = DirectCast(invocation.Expression, MemberAccessExpressionSyntax)
If Not memberAccess.Name.IsKind(SyntaxKind.IdentifierName) OrElse
Not memberAccess.Name.Identifier.IsKind(SyntaxKind.IdentifierToken) Then
Return True
End If
' Note that ValueText returns "New" for both New and [New]
If Not String.Equals(memberAccess.Name.Identifier.ToString(), "New", StringComparison.OrdinalIgnoreCase) Then
Return True
End If
Return memberAccess.Expression.IsKind(SyntaxKind.MyBaseKeyword)
End Function
Friend Overrides Function IsPartial(type As INamedTypeSymbol) As Boolean
Dim syntaxRefs = type.DeclaringSyntaxReferences
Return syntaxRefs.Length > 1 OrElse
DirectCast(syntaxRefs.Single().GetSyntax(), TypeStatementSyntax).Modifiers.Any(SyntaxKind.PartialKeyword)
End Function
Protected Overrides Function GetEditedSymbols(
editKind As EditKind,
oldNode As SyntaxNode,
newNode As SyntaxNode,
oldModel As SemanticModel,
newModel As SemanticModel,
cancellationToken As CancellationToken) As OneOrMany(Of (oldSymbol As ISymbol, newSymbol As ISymbol))
Dim oldSymbols = OneOrMany(Of ISymbol).Empty
Dim newSymbols = OneOrMany(Of ISymbol).Empty
If oldNode IsNot Nothing AndAlso Not TryGetSyntaxNodesForEdit(editKind, oldNode, oldModel, oldSymbols, cancellationToken) OrElse
newNode IsNot Nothing AndAlso Not TryGetSyntaxNodesForEdit(editKind, newNode, newModel, newSymbols, cancellationToken) Then
Return OneOrMany(Of (ISymbol, ISymbol)).Empty
End If
Debug.Assert(Not oldSymbols.IsEmpty OrElse Not newSymbols.IsEmpty)
If oldSymbols.Count <= 1 AndAlso newSymbols.Count <= 1 Then
Return OneOrMany.Create((oldSymbols.FirstOrDefault(), newSymbols.FirstOrDefault()))
End If
' This only occurs when field identifiers are deleted/inserted/reordered from/to/within their variable declarator list,
' or their shared initializer is updated. The particular inserted and deleted fields will be represented by separate edits,
' but the AsNew clause of the declarator may have been updated as well, which needs to update the remaining (matching) fields.
Return OneOrMany.Create(PairSymbols(oldSymbols, newSymbols).ToImmutableArray())
End Function
Private Shared Iterator Function PairSymbols(
oldSymbols As OneOrMany(Of ISymbol),
newSymbols As OneOrMany(Of ISymbol)) As IEnumerable(Of (ISymbol, ISymbol))
For Each oldSymbol In oldSymbols
Dim newSymbol = newSymbols.FirstOrDefault(Function(s, o) CaseInsensitiveComparison.Equals(s.Name, o.Name), oldSymbol)
If newSymbol IsNot Nothing Then
Yield (oldSymbol, newSymbol)
End If
Next
End Function
Protected Overrides Sub AddSymbolEdits(
ByRef result As TemporaryArray(Of (ISymbol, ISymbol, EditKind)),
editKind As EditKind,
oldNode As SyntaxNode,
oldSymbol As ISymbol,
newNode As SyntaxNode,
newSymbol As ISymbol,
oldModel As SemanticModel,
newModel As SemanticModel,
topMatch As Match(Of SyntaxNode),
editMap As IReadOnlyDictionary(Of SyntaxNode, EditKind),
symbolCache As SymbolInfoCache,
cancellationToken As CancellationToken)
Debug.Assert(oldSymbol IsNot Nothing OrElse newSymbol IsNot Nothing)
If oldNode.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter) OrElse
oldNode.IsKind(SyntaxKind.ModifiedIdentifier) AndAlso oldNode.IsParentKind(SyntaxKind.Parameter) OrElse
newNode.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter) OrElse
newNode.IsKind(SyntaxKind.ModifiedIdentifier) AndAlso newNode.IsParentKind(SyntaxKind.Parameter) Then
' parameter list, member, Or type declaration
Dim oldContainingMemberOrType = GetParameterContainingMemberOrType(oldNode, newNode, oldModel, topMatch.ReverseMatches, cancellationToken)
Dim newContainingMemberOrType = GetParameterContainingMemberOrType(newNode, oldNode, newModel, topMatch.Matches, cancellationToken)
Dim matchingNewContainingMemberOrType = GetSemanticallyMatchingNewSymbol(oldContainingMemberOrType, newContainingMemberOrType, newModel, symbolCache, cancellationToken)
' Any change to a constraint should be analyzed as an update of the type parameter
Dim isTypeConstraint = TypeOf oldNode Is TypeParameterConstraintClauseSyntax OrElse
TypeOf newNode Is TypeParameterConstraintClauseSyntax
' If the signature of a property changed or its parameter has been renamed we need to update all its accessors
Dim oldPropertySymbol = TryCast(oldContainingMemberOrType, IPropertySymbol)
Dim newPropertySymbol = TryCast(newContainingMemberOrType, IPropertySymbol)
If oldPropertySymbol IsNot Nothing AndAlso
newPropertySymbol IsNot Nothing AndAlso
(IsMemberOrDelegateReplaced(oldPropertySymbol, newPropertySymbol) OrElse
oldSymbol IsNot Nothing AndAlso newSymbol IsNot Nothing AndAlso oldSymbol.Name <> newSymbol.Name) Then
AddMemberUpdate(result, oldPropertySymbol.GetMethod, newPropertySymbol.GetMethod, matchingNewContainingMemberOrType)
AddMemberUpdate(result, oldPropertySymbol.SetMethod, newPropertySymbol.SetMethod, matchingNewContainingMemberOrType)
End If
AddMemberUpdate(result, oldContainingMemberOrType, newContainingMemberOrType, matchingNewContainingMemberOrType)
If matchingNewContainingMemberOrType IsNot Nothing Then
' Map parameter to the corresponding semantically matching member.
' Since the signature of the member matches we can direcly map by parameter ordinal.
If oldSymbol.Kind = SymbolKind.Parameter Then
newSymbol = matchingNewContainingMemberOrType.GetParameters()(DirectCast(oldSymbol, IParameterSymbol).Ordinal)
ElseIf oldSymbol.Kind = SymbolKind.TypeParameter Then
newSymbol = matchingNewContainingMemberOrType.GetTypeParameters()(DirectCast(oldSymbol, ITypeParameterSymbol).Ordinal)
End If
End If
result.Add((oldSymbol, newSymbol, If(isTypeConstraint, EditKind.Update, editKind)))
Return
End If
Select Case editKind
Case EditKind.Reorder
If oldSymbol Is Nothing OrElse newSymbol Is Nothing Then
Return
End If
' Reordering of data members is only allowed if the layout of the type doesn't change.
' Reordering of other members is a no-op, although the new order won't be reflected in metadata (Reflection will report original order).
result.Add((oldSymbol, newSymbol, EditKind.Reorder))
Case EditKind.Delete
result.Add((oldSymbol, Nothing, editKind))
Case EditKind.Insert
result.Add((Nothing, newSymbol, editKind))
Case EditKind.Update
' Updates of a property/indexer/event node might affect its accessors.
' Return all affected symbols for these updates so that the changes in the accessor bodies get analyzed.
Dim oldPropertySymbol = TryCast(oldSymbol, IPropertySymbol)
Dim newPropertySymbol = TryCast(newSymbol, IPropertySymbol)
If oldPropertySymbol IsNot Nothing AndAlso newPropertySymbol IsNot Nothing Then
' Note: a signature change does not affect the property itself.
result.Add((oldPropertySymbol, newPropertySymbol, EditKind.Update))
If oldPropertySymbol.GetMethod IsNot Nothing OrElse newPropertySymbol.GetMethod IsNot Nothing Then
If IsMemberOrDelegateReplaced(oldPropertySymbol, newPropertySymbol) Then
result.Add((oldPropertySymbol.GetMethod, newPropertySymbol.GetMethod, editKind))
End If
End If
If oldPropertySymbol.SetMethod IsNot Nothing OrElse newPropertySymbol.SetMethod IsNot Nothing Then
If IsMemberOrDelegateReplaced(oldPropertySymbol, newPropertySymbol) Then
result.Add((oldPropertySymbol.SetMethod, newPropertySymbol.SetMethod, editKind))
End If
End If
Return
End If
Dim oldEventSymbol = TryCast(oldSymbol, IEventSymbol)
Dim newEventSymbol = TryCast(newSymbol, IEventSymbol)
If oldEventSymbol IsNot Nothing AndAlso newEventSymbol IsNot Nothing Then
result.Add((oldEventSymbol, newEventSymbol, EditKind.Update))
If oldEventSymbol.AddMethod IsNot Nothing OrElse newEventSymbol.AddMethod IsNot Nothing Then
If IsMemberOrDelegateReplaced(oldEventSymbol, newEventSymbol) Then
result.Add((oldEventSymbol.AddMethod, newEventSymbol.AddMethod, editKind))
End If
End If
If oldEventSymbol.RemoveMethod IsNot Nothing OrElse newEventSymbol.RemoveMethod IsNot Nothing Then
If IsMemberOrDelegateReplaced(oldEventSymbol, newEventSymbol) Then
result.Add((oldEventSymbol.RemoveMethod, newEventSymbol.RemoveMethod, editKind))
End If
End If
If oldEventSymbol.RaiseMethod IsNot Nothing OrElse newEventSymbol.RaiseMethod IsNot Nothing Then
' change in event type does not affect Raise method, but rename does
If oldEventSymbol.Name <> newEventSymbol.Name Then
result.Add((oldEventSymbol.RaiseMethod, newEventSymbol.RaiseMethod, editKind))
End If
End If
Return
End If
result.Add((oldSymbol, newSymbol, editKind))
Case EditKind.Move
Contract.ThrowIfNull(oldNode)
Contract.ThrowIfNull(newNode)
Contract.ThrowIfNull(oldModel)
Debug.Assert(oldNode.RawKind = newNode.RawKind)
If Not IsTypeDeclaration(oldNode) Then
Return
End If
result.Add((oldSymbol, newSymbol, editKind))
Case Else
Throw ExceptionUtilities.UnexpectedValue(editKind)
End Select
End Sub
Private Function TryGetSyntaxNodesForEdit(
editKind As EditKind,
node As SyntaxNode,
model As SemanticModel,
<Out> ByRef symbols As OneOrMany(Of ISymbol),
cancellationToken As CancellationToken) As Boolean
Select Case node.Kind()
Case SyntaxKind.ImportsStatement,
SyntaxKind.NamespaceBlock,
SyntaxKind.NamespaceStatement
Return False
Case SyntaxKind.VariableDeclarator
' Initializer or As clause update
Dim variableDeclarator = CType(node, VariableDeclaratorSyntax)
If variableDeclarator.Names.Count > 1 Then
symbols = OneOrMany.Create(variableDeclarator.Names.SelectAsArray(Function(n) GetDeclaredSymbol(model, n, cancellationToken)))
Return True
End If
node = variableDeclarator.Names(0)
Case SyntaxKind.FieldDeclaration
' Attribute or modifier update
If editKind = EditKind.Update OrElse editKind = EditKind.Reorder Then
Dim field = CType(node, FieldDeclarationSyntax)
If field.Declarators.Count = 1 AndAlso field.Declarators(0).Names.Count = 1 Then
node = field.Declarators(0).Names(0)
Else
symbols = OneOrMany.Create(
(From declarator In field.Declarators
From name In declarator.Names
Select GetDeclaredSymbol(model, name, cancellationToken)).ToImmutableArray())
Return True
End If
End If
End Select
Dim symbol = GetDeclaredSymbol(model, node, cancellationToken)
If symbol Is Nothing Then
Return False
End If
symbols = OneOrMany.Create(symbol)
Return True
End Function
Private Function GetParameterContainingMemberOrType(node As SyntaxNode, otherNode As SyntaxNode, model As SemanticModel, fromOtherMap As IReadOnlyDictionary(Of SyntaxNode, SyntaxNode), cancellationToken As CancellationToken) As ISymbol
Debug.Assert(node Is Nothing OrElse
node.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter) OrElse
node.IsKind(SyntaxKind.ModifiedIdentifier) AndAlso node.IsParentKind(SyntaxKind.Parameter) OrElse
TypeOf node Is TypeParameterConstraintClauseSyntax)
' parameter list, member, or type declaration
Dim declaration As SyntaxNode = Nothing
If node Is Nothing Then
fromOtherMap.TryGetValue(GetContainingDeclaration(otherNode), declaration)
Else
declaration = GetContainingDeclaration(node)
End If
Return If(declaration IsNot Nothing, GetDeclaredSymbol(model, declaration, cancellationToken), Nothing)
End Function
Private Shared Function GetContainingDeclaration(node As SyntaxNode) As SyntaxNode
Return If(node.IsKind(SyntaxKind.ModifiedIdentifier), node.Parent.Parent.Parent, node.Parent.Parent)
End Function
Friend Overrides ReadOnly Property IsLambda As Func(Of SyntaxNode, Boolean)
Get
Return AddressOf LambdaUtilities.IsLambda
End Get
End Property
Friend Overrides ReadOnly Property IsNotLambda As Func(Of SyntaxNode, Boolean)
Get
Return AddressOf LambdaUtilities.IsNotLambda
End Get
End Property
Friend Overrides Function IsNestedFunction(node As SyntaxNode) As Boolean
Return TypeOf node Is LambdaExpressionSyntax
End Function
Friend Overrides Function IsLocalFunction(node As SyntaxNode) As Boolean
Return False
End Function
Friend Overrides Function IsGenericLocalFunction(node As SyntaxNode) As Boolean
Return False
End Function
Friend Overrides Function TryGetLambdaBodies(node As SyntaxNode, <Out> ByRef body1 As LambdaBody, <Out> ByRef body2 As LambdaBody) As Boolean
Dim bodyNode1 As SyntaxNode = Nothing
Dim bodyNode2 As SyntaxNode = Nothing
If LambdaUtilities.TryGetLambdaBodies(node, bodyNode1, bodyNode2) Then
body1 = SyntaxUtilities.CreateLambdaBody(bodyNode1)
body2 = If(bodyNode2 IsNot Nothing, SyntaxUtilities.CreateLambdaBody(bodyNode2), Nothing)
Return True
End If
Return False
End Function
Friend Overrides Function GetLambdaExpressionSymbol(model As SemanticModel, lambdaExpression As SyntaxNode, cancellationToken As CancellationToken) As IMethodSymbol
Dim lambdaExpressionSyntax = DirectCast(lambdaExpression, LambdaExpressionSyntax)
' The semantic model only returns the lambda symbol for positions that are within the body of the lambda (not the header)
Return DirectCast(model.GetEnclosingSymbol(lambdaExpressionSyntax.SubOrFunctionHeader.Span.End, cancellationToken), IMethodSymbol)
End Function
Friend Overrides Function GetContainingQueryExpression(node As SyntaxNode) As SyntaxNode
Return node.FirstAncestorOrSelf(Of QueryExpressionSyntax)
End Function
Friend Overrides Function QueryClauseLambdasTypeEquivalent(oldModel As SemanticModel, oldNode As SyntaxNode, newModel As SemanticModel, newNode As SyntaxNode, cancellationToken As CancellationToken) As Boolean
Select Case oldNode.Kind
Case SyntaxKind.AggregateClause
Dim oldInfo = oldModel.GetAggregateClauseSymbolInfo(DirectCast(oldNode, AggregateClauseSyntax), cancellationToken)
Dim newInfo = newModel.GetAggregateClauseSymbolInfo(DirectCast(newNode, AggregateClauseSyntax), cancellationToken)
Return MemberOrDelegateSignaturesEquivalent(oldInfo.Select1.Symbol, newInfo.Select1.Symbol) AndAlso
MemberOrDelegateSignaturesEquivalent(oldInfo.Select2.Symbol, newInfo.Select2.Symbol)
Case SyntaxKind.CollectionRangeVariable
Dim oldInfo = oldModel.GetCollectionRangeVariableSymbolInfo(DirectCast(oldNode, CollectionRangeVariableSyntax), cancellationToken)
Dim newInfo = newModel.GetCollectionRangeVariableSymbolInfo(DirectCast(newNode, CollectionRangeVariableSyntax), cancellationToken)
Return MemberOrDelegateSignaturesEquivalent(oldInfo.AsClauseConversion.Symbol, newInfo.AsClauseConversion.Symbol) AndAlso
MemberOrDelegateSignaturesEquivalent(oldInfo.SelectMany.Symbol, newInfo.SelectMany.Symbol) AndAlso
MemberOrDelegateSignaturesEquivalent(oldInfo.ToQueryableCollectionConversion.Symbol, newInfo.ToQueryableCollectionConversion.Symbol)
Case SyntaxKind.FunctionAggregation
Dim oldInfo = oldModel.GetSymbolInfo(DirectCast(oldNode, FunctionAggregationSyntax), cancellationToken)
Dim newInfo = newModel.GetSymbolInfo(DirectCast(newNode, FunctionAggregationSyntax), cancellationToken)
Return MemberOrDelegateSignaturesEquivalent(oldInfo.Symbol, newInfo.Symbol)
Case SyntaxKind.ExpressionRangeVariable
Dim oldInfo = oldModel.GetSymbolInfo(DirectCast(oldNode, ExpressionRangeVariableSyntax), cancellationToken)
Dim newInfo = newModel.GetSymbolInfo(DirectCast(newNode, ExpressionRangeVariableSyntax), cancellationToken)
Return MemberOrDelegateSignaturesEquivalent(oldInfo.Symbol, newInfo.Symbol)
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Dim oldInfo = oldModel.GetSymbolInfo(DirectCast(oldNode, OrderingSyntax), cancellationToken)
Dim newInfo = newModel.GetSymbolInfo(DirectCast(newNode, OrderingSyntax), cancellationToken)
Return MemberOrDelegateSignaturesEquivalent(oldInfo.Symbol, newInfo.Symbol)
Case SyntaxKind.FromClause,
SyntaxKind.WhereClause,
SyntaxKind.SkipClause,
SyntaxKind.TakeClause,
SyntaxKind.SkipWhileClause,
SyntaxKind.TakeWhileClause,
SyntaxKind.GroupByClause,
SyntaxKind.SimpleJoinClause,
SyntaxKind.GroupJoinClause,
SyntaxKind.SelectClause
Dim oldInfo = oldModel.GetSymbolInfo(DirectCast(oldNode, QueryClauseSyntax), cancellationToken)
Dim newInfo = newModel.GetSymbolInfo(DirectCast(newNode, QueryClauseSyntax), cancellationToken)
Return MemberOrDelegateSignaturesEquivalent(oldInfo.Symbol, newInfo.Symbol)
Case Else
Return True
End Select
End Function
#End Region
#Region "Diagnostic Info"
Protected Overrides ReadOnly Property ErrorDisplayFormat As SymbolDisplayFormat
Get
Return SymbolDisplayFormat.VisualBasicShortErrorMessageFormat
End Get
End Property
Protected Overrides Function TryGetDiagnosticSpan(node As SyntaxNode, editKind As EditKind) As TextSpan?
Return TryGetDiagnosticSpanImpl(node, editKind)
End Function
Protected Overloads Shared Function GetDiagnosticSpan(node As SyntaxNode, editKind As EditKind) As TextSpan
Return If(TryGetDiagnosticSpanImpl(node, editKind), node.Span)
End Function
Private Shared Function TryGetDiagnosticSpanImpl(node As SyntaxNode, editKind As EditKind) As TextSpan?
Return TryGetDiagnosticSpanImpl(node.Kind, node, editKind)
End Function
Protected Overrides Function GetBodyDiagnosticSpan(node As SyntaxNode, editKind As EditKind) As TextSpan
Return GetDiagnosticSpan(node, editKind)
End Function
' internal for testing; kind is passed explicitly for testing as well
Friend Shared Function TryGetDiagnosticSpanImpl(kind As SyntaxKind, node As SyntaxNode, editKind As EditKind) As TextSpan?
Select Case kind
Case SyntaxKind.CompilationUnit
Dim unit = DirectCast(node, CompilationUnitSyntax)
Dim globalNode = unit.ChildNodes().FirstOrDefault()
If globalNode Is Nothing Then
Return Nothing
End If
Return GetDiagnosticSpan(globalNode, editKind)
Case SyntaxKind.OptionStatement,
SyntaxKind.ImportsStatement
Return node.Span
Case SyntaxKind.NamespaceBlock
Return GetDiagnosticSpan(DirectCast(node, NamespaceBlockSyntax).NamespaceStatement)
Case SyntaxKind.NamespaceStatement
Return GetDiagnosticSpan(DirectCast(node, NamespaceStatementSyntax))
Case SyntaxKind.ClassBlock,
SyntaxKind.StructureBlock,
SyntaxKind.InterfaceBlock,
SyntaxKind.ModuleBlock
Return GetDiagnosticSpan(DirectCast(node, TypeBlockSyntax).BlockStatement)
Case SyntaxKind.ClassStatement,
SyntaxKind.StructureStatement,
SyntaxKind.InterfaceStatement,
SyntaxKind.ModuleStatement
Return GetDiagnosticSpan(DirectCast(node, TypeStatementSyntax))
Case SyntaxKind.EnumBlock
Return TryGetDiagnosticSpanImpl(DirectCast(node, EnumBlockSyntax).EnumStatement, editKind)
Case SyntaxKind.EnumStatement
Dim enumStatement = DirectCast(node, EnumStatementSyntax)
Return GetDiagnosticSpan(enumStatement.Modifiers, enumStatement.EnumKeyword, enumStatement.Identifier)
Case SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.OperatorBlock,
SyntaxKind.ConstructorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.GetAccessorBlock,
SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock
Return GetDiagnosticSpan(DirectCast(node, MethodBlockBaseSyntax).BlockStatement)
Case SyntaxKind.EventBlock
Return GetDiagnosticSpan(DirectCast(node, EventBlockSyntax).EventStatement)
Case SyntaxKind.SubStatement,
SyntaxKind.FunctionStatement,
SyntaxKind.OperatorStatement,
SyntaxKind.SubNewStatement,
SyntaxKind.EventStatement,
SyntaxKind.SetAccessorStatement,
SyntaxKind.GetAccessorStatement,
SyntaxKind.AddHandlerAccessorStatement,
SyntaxKind.RemoveHandlerAccessorStatement,
SyntaxKind.RaiseEventAccessorStatement,
SyntaxKind.DeclareSubStatement,
SyntaxKind.DeclareFunctionStatement,
SyntaxKind.DelegateSubStatement,
SyntaxKind.DelegateFunctionStatement
Return GetDiagnosticSpan(DirectCast(node, MethodBaseSyntax))
Case SyntaxKind.PropertyBlock
Return GetDiagnosticSpan(DirectCast(node, PropertyBlockSyntax).PropertyStatement)
Case SyntaxKind.PropertyStatement
Return GetDiagnosticSpan(DirectCast(node, PropertyStatementSyntax))
Case SyntaxKind.FieldDeclaration
Dim fieldDeclaration = DirectCast(node, FieldDeclarationSyntax)
Return GetDiagnosticSpan(fieldDeclaration.Modifiers, fieldDeclaration.Declarators.First, fieldDeclaration.Declarators.Last)
Case SyntaxKind.VariableDeclarator,
SyntaxKind.ModifiedIdentifier,
SyntaxKind.EnumMemberDeclaration,
SyntaxKind.TypeParameterSingleConstraintClause,
SyntaxKind.TypeParameterMultipleConstraintClause,
SyntaxKind.ClassConstraint,
SyntaxKind.StructureConstraint,
SyntaxKind.NewConstraint,
SyntaxKind.TypeConstraint
Return node.Span
Case SyntaxKind.TypeParameter
Return DirectCast(node, TypeParameterSyntax).Identifier.Span
Case SyntaxKind.TypeParameterList,
SyntaxKind.ParameterList,
SyntaxKind.AttributeList,
SyntaxKind.SimpleAsClause
If editKind = EditKind.Delete Then
Return TryGetDiagnosticSpanImpl(node.Parent, editKind)
Else
Return node.Span
End If
Case SyntaxKind.AttributesStatement,
SyntaxKind.Attribute
Return node.Span
Case SyntaxKind.Parameter
Dim parameter = DirectCast(node, ParameterSyntax)
Return GetDiagnosticSpan(parameter.Modifiers, parameter.Identifier, parameter)
Case SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression
Return GetDiagnosticSpan(DirectCast(node, LambdaExpressionSyntax).SubOrFunctionHeader)
Case SyntaxKind.MultiLineIfBlock
Dim ifStatement = DirectCast(node, MultiLineIfBlockSyntax).IfStatement
Return GetDiagnosticSpan(ifStatement.IfKeyword, ifStatement.Condition, ifStatement.ThenKeyword)
Case SyntaxKind.ElseIfBlock
Dim elseIfStatement = DirectCast(node, ElseIfBlockSyntax).ElseIfStatement
Return GetDiagnosticSpan(elseIfStatement.ElseIfKeyword, elseIfStatement.Condition, elseIfStatement.ThenKeyword)
Case SyntaxKind.SingleLineIfStatement
Dim ifStatement = DirectCast(node, SingleLineIfStatementSyntax)
Return GetDiagnosticSpan(ifStatement.IfKeyword, ifStatement.Condition, ifStatement.ThenKeyword)
Case SyntaxKind.SingleLineElseClause
Return DirectCast(node, SingleLineElseClauseSyntax).ElseKeyword.Span
Case SyntaxKind.TryBlock
Return DirectCast(node, TryBlockSyntax).TryStatement.TryKeyword.Span
Case SyntaxKind.CatchBlock
Return DirectCast(node, CatchBlockSyntax).CatchStatement.CatchKeyword.Span
Case SyntaxKind.FinallyBlock
Return DirectCast(node, FinallyBlockSyntax).FinallyStatement.FinallyKeyword.Span
Case SyntaxKind.SyncLockBlock
Return DirectCast(node, SyncLockBlockSyntax).SyncLockStatement.Span
Case SyntaxKind.WithBlock
Return DirectCast(node, WithBlockSyntax).WithStatement.Span
Case SyntaxKind.UsingBlock
Return DirectCast(node, UsingBlockSyntax).UsingStatement.Span
Case SyntaxKind.SimpleDoLoopBlock,
SyntaxKind.DoWhileLoopBlock,
SyntaxKind.DoUntilLoopBlock,
SyntaxKind.DoLoopWhileBlock,
SyntaxKind.DoLoopUntilBlock
Return DirectCast(node, DoLoopBlockSyntax).DoStatement.Span
Case SyntaxKind.WhileBlock
Return DirectCast(node, WhileBlockSyntax).WhileStatement.Span
Case SyntaxKind.ForEachBlock,
SyntaxKind.ForBlock
Return DirectCast(node, ForOrForEachBlockSyntax).ForOrForEachStatement.Span
Case SyntaxKind.AwaitExpression
Return DirectCast(node, AwaitExpressionSyntax).AwaitKeyword.Span
Case SyntaxKind.AnonymousObjectCreationExpression
Dim newWith = DirectCast(node, AnonymousObjectCreationExpressionSyntax)
Return TextSpan.FromBounds(newWith.NewKeyword.Span.Start,
newWith.Initializer.WithKeyword.Span.End)
Case SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression,
SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression
Return DirectCast(node, LambdaExpressionSyntax).SubOrFunctionHeader.Span
Case SyntaxKind.QueryExpression
Return TryGetDiagnosticSpanImpl(DirectCast(node, QueryExpressionSyntax).Clauses.First(), editKind)
Case SyntaxKind.WhereClause
Return DirectCast(node, WhereClauseSyntax).WhereKeyword.Span
Case SyntaxKind.SelectClause
Return DirectCast(node, SelectClauseSyntax).SelectKeyword.Span
Case SyntaxKind.FromClause
Return DirectCast(node, FromClauseSyntax).FromKeyword.Span
Case SyntaxKind.AggregateClause
Return DirectCast(node, AggregateClauseSyntax).AggregateKeyword.Span
Case SyntaxKind.LetClause
Return DirectCast(node, LetClauseSyntax).LetKeyword.Span
Case SyntaxKind.SimpleJoinClause
Return DirectCast(node, SimpleJoinClauseSyntax).JoinKeyword.Span
Case SyntaxKind.GroupJoinClause
Dim groupJoin = DirectCast(node, GroupJoinClauseSyntax)
Return TextSpan.FromBounds(groupJoin.GroupKeyword.SpanStart, groupJoin.JoinKeyword.Span.End)
Case SyntaxKind.GroupByClause
Return DirectCast(node, GroupByClauseSyntax).GroupKeyword.Span
Case SyntaxKind.FunctionAggregation
Return node.Span
Case SyntaxKind.CollectionRangeVariable,
SyntaxKind.ExpressionRangeVariable
Return TryGetDiagnosticSpanImpl(node.Parent, editKind)
Case SyntaxKind.TakeWhileClause,
SyntaxKind.SkipWhileClause
Dim partition = DirectCast(node, PartitionWhileClauseSyntax)
Return TextSpan.FromBounds(partition.SkipOrTakeKeyword.SpanStart, partition.WhileKeyword.Span.End)
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Return node.Span
Case SyntaxKind.JoinCondition
Return DirectCast(node, JoinConditionSyntax).EqualsKeyword.Span
Case Else
Return node.Span
End Select
End Function
Private Overloads Shared Function GetDiagnosticSpan(ifKeyword As SyntaxToken, condition As SyntaxNode, thenKeywordOpt As SyntaxToken) As TextSpan
Return TextSpan.FromBounds(ifKeyword.Span.Start,
If(thenKeywordOpt.RawKind <> 0, thenKeywordOpt.Span.End, condition.Span.End))
End Function
Private Overloads Shared Function GetDiagnosticSpan(node As NamespaceStatementSyntax) As TextSpan
Return TextSpan.FromBounds(node.NamespaceKeyword.SpanStart, node.Name.Span.End)
End Function
Private Overloads Shared Function GetDiagnosticSpan(node As TypeStatementSyntax) As TextSpan
Return GetDiagnosticSpan(node.Modifiers,
node.DeclarationKeyword,
If(node.TypeParameterList, CType(node.Identifier, SyntaxNodeOrToken)))
End Function
Private Overloads Shared Function GetDiagnosticSpan(modifiers As SyntaxTokenList, start As SyntaxNodeOrToken, endNode As SyntaxNodeOrToken) As TextSpan
Return TextSpan.FromBounds(If(modifiers.Count <> 0, modifiers.First.SpanStart, start.SpanStart),
endNode.Span.End)
End Function
Private Overloads Shared Function GetDiagnosticSpan(header As MethodBaseSyntax) As TextSpan
Dim startToken As SyntaxToken
Dim endToken As SyntaxToken
If header.Modifiers.Count > 0 Then
startToken = header.Modifiers.First
Else
Select Case header.Kind
Case SyntaxKind.DelegateFunctionStatement,
SyntaxKind.DelegateSubStatement
startToken = DirectCast(header, DelegateStatementSyntax).DelegateKeyword
Case SyntaxKind.DeclareSubStatement,
SyntaxKind.DeclareFunctionStatement
startToken = DirectCast(header, DeclareStatementSyntax).DeclareKeyword
Case Else
startToken = header.DeclarationKeyword
End Select
End If
If header.ParameterList IsNot Nothing Then
endToken = header.ParameterList.CloseParenToken
Else
Select Case header.Kind
Case SyntaxKind.SubStatement,
SyntaxKind.FunctionStatement
endToken = DirectCast(header, MethodStatementSyntax).Identifier
Case SyntaxKind.DeclareSubStatement,
SyntaxKind.DeclareFunctionStatement
endToken = DirectCast(header, DeclareStatementSyntax).LibraryName.Token
Case SyntaxKind.OperatorStatement
endToken = DirectCast(header, OperatorStatementSyntax).OperatorToken
Case SyntaxKind.SubNewStatement
endToken = DirectCast(header, SubNewStatementSyntax).NewKeyword
Case SyntaxKind.PropertyStatement
endToken = DirectCast(header, PropertyStatementSyntax).Identifier
Case SyntaxKind.EventStatement
endToken = DirectCast(header, EventStatementSyntax).Identifier
Case SyntaxKind.DelegateFunctionStatement,
SyntaxKind.DelegateSubStatement
endToken = DirectCast(header, DelegateStatementSyntax).Identifier
Case SyntaxKind.SetAccessorStatement,
SyntaxKind.GetAccessorStatement,
SyntaxKind.AddHandlerAccessorStatement,
SyntaxKind.RemoveHandlerAccessorStatement,
SyntaxKind.RaiseEventAccessorStatement
endToken = header.DeclarationKeyword
Case Else
Throw ExceptionUtilities.UnexpectedValue(header.Kind)
End Select
End If
Return TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End)
End Function
Private Overloads Shared Function GetDiagnosticSpan(lambda As LambdaHeaderSyntax) As TextSpan
Dim startToken = If(lambda.Modifiers.Count <> 0, lambda.Modifiers.First, lambda.DeclarationKeyword)
Dim endToken As SyntaxToken
If lambda.ParameterList IsNot Nothing Then
endToken = lambda.ParameterList.CloseParenToken
Else
endToken = lambda.DeclarationKeyword
End If
Return TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End)
End Function
Friend Overrides Function GetLambdaParameterDiagnosticSpan(lambda As SyntaxNode, ordinal As Integer) As TextSpan
Select Case lambda.Kind
Case SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression
Return DirectCast(lambda, LambdaExpressionSyntax).SubOrFunctionHeader.ParameterList.Parameters(ordinal).Identifier.Span
Case Else
Return lambda.Span
End Select
End Function
Friend Overrides Function GetDisplayName(symbol As INamedTypeSymbol) As String
Select Case symbol.TypeKind
Case TypeKind.Structure
Return VBFeaturesResources.structure_
Case TypeKind.Module
Return VBFeaturesResources.module_
Case Else
Return MyBase.GetDisplayName(symbol)
End Select
End Function
Friend Overrides Function GetDisplayName(symbol As IMethodSymbol) As String
Select Case symbol.MethodKind
Case MethodKind.StaticConstructor
Return VBFeaturesResources.Shared_constructor
Case MethodKind.LambdaMethod
Return VBFeaturesResources.Lambda
Case Else
Return MyBase.GetDisplayName(symbol)
End Select
End Function
Friend Overrides Function GetDisplayName(symbol As IPropertySymbol) As String
If symbol.IsWithEvents Then
Return VBFeaturesResources.WithEvents_field
End If
Return MyBase.GetDisplayName(symbol)
End Function
Protected Overrides Function TryGetDisplayName(node As SyntaxNode, editKind As EditKind) As String
Return TryGetDisplayNameImpl(node, editKind)
End Function
Protected Overloads Shared Function GetDisplayName(node As SyntaxNode, editKind As EditKind) As String
Dim result = TryGetDisplayNameImpl(node, editKind)
If result Is Nothing Then
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End If
Return result
End Function
Protected Overrides Function GetBodyDisplayName(node As SyntaxNode, Optional editKind As EditKind = EditKind.Update) As String
Return GetDisplayName(node, editKind)
End Function
Private Shared Function TryGetDisplayNameImpl(node As SyntaxNode, editKind As EditKind) As String
Select Case node.Kind
' top-level
Case SyntaxKind.OptionStatement
Return VBFeaturesResources.option_
Case SyntaxKind.ImportsStatement
Return VBFeaturesResources.import
Case SyntaxKind.NamespaceBlock,
SyntaxKind.NamespaceStatement
Return FeaturesResources.namespace_
Case SyntaxKind.ClassBlock,
SyntaxKind.ClassStatement
Return FeaturesResources.class_
Case SyntaxKind.StructureBlock,
SyntaxKind.StructureStatement
Return VBFeaturesResources.structure_
Case SyntaxKind.InterfaceBlock,
SyntaxKind.InterfaceStatement
Return FeaturesResources.interface_
Case SyntaxKind.ModuleBlock,
SyntaxKind.ModuleStatement
Return VBFeaturesResources.module_
Case SyntaxKind.EnumBlock,
SyntaxKind.EnumStatement
Return FeaturesResources.enum_
Case SyntaxKind.DelegateSubStatement,
SyntaxKind.DelegateFunctionStatement
Return FeaturesResources.delegate_
Case SyntaxKind.FieldDeclaration
Dim declaration = DirectCast(node, FieldDeclarationSyntax)
Return If(declaration.Modifiers.Any(SyntaxKind.WithEventsKeyword), VBFeaturesResources.WithEvents_field,
If(declaration.Modifiers.Any(SyntaxKind.ConstKeyword), FeaturesResources.const_field, FeaturesResources.field))
Case SyntaxKind.VariableDeclarator,
SyntaxKind.ModifiedIdentifier
Return TryGetDisplayNameImpl(node.Parent, editKind)
Case SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.SubStatement,
SyntaxKind.FunctionStatement,
SyntaxKind.DeclareSubStatement,
SyntaxKind.DeclareFunctionStatement
Return FeaturesResources.method
Case SyntaxKind.OperatorBlock,
SyntaxKind.OperatorStatement
Return FeaturesResources.operator_
Case SyntaxKind.ConstructorBlock
Return If(CType(node, ConstructorBlockSyntax).SubNewStatement.Modifiers.Any(SyntaxKind.SharedKeyword), VBFeaturesResources.Shared_constructor, FeaturesResources.constructor)
Case SyntaxKind.SubNewStatement
Return If(CType(node, SubNewStatementSyntax).Modifiers.Any(SyntaxKind.SharedKeyword), VBFeaturesResources.Shared_constructor, FeaturesResources.constructor)
Case SyntaxKind.PropertyBlock
Return FeaturesResources.property_
Case SyntaxKind.PropertyStatement
Return If(node.IsParentKind(SyntaxKind.PropertyBlock),
FeaturesResources.property_,
FeaturesResources.auto_property)
Case SyntaxKind.EventBlock,
SyntaxKind.EventStatement
Return FeaturesResources.event_
Case SyntaxKind.EnumMemberDeclaration
Return FeaturesResources.enum_value
Case SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.GetAccessorStatement,
SyntaxKind.SetAccessorStatement
Return FeaturesResources.property_accessor
Case SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock,
SyntaxKind.AddHandlerAccessorStatement,
SyntaxKind.RemoveHandlerAccessorStatement,
SyntaxKind.RaiseEventAccessorStatement
Return FeaturesResources.event_accessor
Case SyntaxKind.TypeParameterSingleConstraintClause,
SyntaxKind.TypeParameterMultipleConstraintClause,
SyntaxKind.ClassConstraint,
SyntaxKind.StructureConstraint,
SyntaxKind.NewConstraint,
SyntaxKind.TypeConstraint
Return FeaturesResources.type_constraint
Case SyntaxKind.SimpleAsClause
Return VBFeaturesResources.as_clause
Case SyntaxKind.TypeParameterList
Return VBFeaturesResources.type_parameters
Case SyntaxKind.TypeParameter
Return FeaturesResources.type_parameter
Case SyntaxKind.ParameterList
Return VBFeaturesResources.parameters
Case SyntaxKind.Parameter
Return FeaturesResources.parameter
Case SyntaxKind.AttributeList,
SyntaxKind.AttributesStatement
Return VBFeaturesResources.attributes
Case SyntaxKind.Attribute
Return FeaturesResources.attribute
' statement-level
Case SyntaxKind.TryBlock
Return VBFeaturesResources.Try_block
Case SyntaxKind.CatchBlock
Return VBFeaturesResources.Catch_clause
Case SyntaxKind.FinallyBlock
Return VBFeaturesResources.Finally_clause
Case SyntaxKind.UsingBlock
Return If(editKind = EditKind.Update, VBFeaturesResources.Using_statement, VBFeaturesResources.Using_block)
Case SyntaxKind.WithBlock
Return If(editKind = EditKind.Update, VBFeaturesResources.With_statement, VBFeaturesResources.With_block)
Case SyntaxKind.SyncLockBlock
Return If(editKind = EditKind.Update, VBFeaturesResources.SyncLock_statement, VBFeaturesResources.SyncLock_block)
Case SyntaxKind.ForEachBlock
Return If(editKind = EditKind.Update, VBFeaturesResources.For_Each_statement, VBFeaturesResources.For_Each_block)
Case SyntaxKind.OnErrorGoToMinusOneStatement,
SyntaxKind.OnErrorGoToZeroStatement,
SyntaxKind.OnErrorResumeNextStatement,
SyntaxKind.OnErrorGoToLabelStatement
Return VBFeaturesResources.On_Error_statement
Case SyntaxKind.ResumeStatement,
SyntaxKind.ResumeNextStatement,
SyntaxKind.ResumeLabelStatement
Return VBFeaturesResources.Resume_statement
Case SyntaxKind.YieldStatement
Return VBFeaturesResources.Yield_statement
Case SyntaxKind.AwaitExpression
Return VBFeaturesResources.Await_expression
Case SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression,
SyntaxKind.FunctionLambdaHeader,
SyntaxKind.SubLambdaHeader
Return VBFeaturesResources.Lambda
Case SyntaxKind.WhereClause
Return VBFeaturesResources.Where_clause
Case SyntaxKind.SelectClause
Return VBFeaturesResources.Select_clause
Case SyntaxKind.FromClause
Return VBFeaturesResources.From_clause
Case SyntaxKind.AggregateClause
Return VBFeaturesResources.Aggregate_clause
Case SyntaxKind.LetClause
Return VBFeaturesResources.Let_clause
Case SyntaxKind.SimpleJoinClause
Return VBFeaturesResources.Join_clause
Case SyntaxKind.GroupJoinClause
Return VBFeaturesResources.Group_Join_clause
Case SyntaxKind.GroupByClause
Return VBFeaturesResources.Group_By_clause
Case SyntaxKind.FunctionAggregation
Return VBFeaturesResources.Function_aggregation
Case SyntaxKind.CollectionRangeVariable,
SyntaxKind.ExpressionRangeVariable
Return TryGetDisplayNameImpl(node.Parent, editKind)
Case SyntaxKind.TakeWhileClause
Return VBFeaturesResources.Take_While_clause
Case SyntaxKind.SkipWhileClause
Return VBFeaturesResources.Skip_While_clause
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Return VBFeaturesResources.Ordering_clause
Case SyntaxKind.JoinCondition
Return VBFeaturesResources.Join_condition
Case Else
Return Nothing
End Select
End Function
#End Region
#Region "Top-level Syntactic Rude Edits"
Private Structure EditClassifier
Private ReadOnly _analyzer As VisualBasicEditAndContinueAnalyzer
Private ReadOnly _diagnostics As ArrayBuilder(Of RudeEditDiagnostic)
Private ReadOnly _match As Match(Of SyntaxNode)
Private ReadOnly _oldNode As SyntaxNode
Private ReadOnly _newNode As SyntaxNode
Private ReadOnly _kind As EditKind
Private ReadOnly _span As TextSpan?
Public Sub New(analyzer As VisualBasicEditAndContinueAnalyzer,
diagnostics As ArrayBuilder(Of RudeEditDiagnostic),
oldNode As SyntaxNode,
newNode As SyntaxNode,
kind As EditKind,
Optional match As Match(Of SyntaxNode) = Nothing,
Optional span As TextSpan? = Nothing)
_analyzer = analyzer
_diagnostics = diagnostics
_oldNode = oldNode
_newNode = newNode
_kind = kind
_span = span
_match = match
End Sub
Private Sub ReportError(kind As RudeEditKind)
_diagnostics.Add(New RudeEditDiagnostic(
kind,
span:=GetSpan(),
node:=If(_newNode, _oldNode),
arguments:={GetDisplayName(If(_newNode, _oldNode), EditKind.Update)}))
End Sub
Private Function GetSpan() As TextSpan
If _span.HasValue Then
Return _span.Value
End If
If _newNode Is Nothing Then
Return _analyzer.GetDeletedNodeDiagnosticSpan(_match.Matches, _oldNode)
Else
Return GetDiagnosticSpan(_newNode, _kind)
End If
End Function
Public Sub ClassifyEdit()
Select Case _kind
Case EditKind.Delete
ClassifyDelete(_oldNode)
Return
Case EditKind.Update
ClassifyUpdate(_newNode)
Return
Case EditKind.Move
ClassifyMove(_newNode)
Return
Case EditKind.Insert
ClassifyInsert(_newNode)
Return
Case EditKind.Reorder
ClassifyReorder(_oldNode, _newNode)
Return
Case Else
Throw ExceptionUtilities.UnexpectedValue(_kind)
End Select
End Sub
#Region "Move and Reorder"
Private Sub ClassifyMove(newNode As SyntaxNode)
Select Case newNode.Kind
Case SyntaxKind.ModifiedIdentifier,
SyntaxKind.VariableDeclarator
' Identifier can be moved within the same type declaration.
' Determine validity of such change in semantic analysis.
Return
Case SyntaxKind.NamespaceBlock,
SyntaxKind.ClassBlock,
SyntaxKind.StructureBlock,
SyntaxKind.InterfaceBlock,
SyntaxKind.ModuleBlock,
SyntaxKind.EnumBlock,
SyntaxKind.DelegateFunctionStatement,
SyntaxKind.DelegateSubStatement
Return
Case Else
ReportError(RudeEditKind.Move)
End Select
End Sub
Private Sub ClassifyReorder(oldNode As SyntaxNode, newNode As SyntaxNode)
Select Case newNode.Kind
Case SyntaxKind.EnumMemberDeclaration
' To allow this change we would need to check that values of all fields of the enum
' are preserved, or make sure we can update all method bodies that accessed those that changed.
ReportError(RudeEditKind.Move)
Return
Case SyntaxKind.TypeParameter
ReportError(RudeEditKind.Move)
Return
Case SyntaxKind.ModifiedIdentifier,
SyntaxKind.VariableDeclarator
' Identifier can be moved within the same type declaration.
' Determine validity of such change in semantic analysis.
Return
End Select
End Sub
#End Region
#Region "Insert"
Private Sub ClassifyInsert(node As SyntaxNode)
Select Case node.Kind
Case SyntaxKind.OptionStatement
ReportError(RudeEditKind.Insert)
Return
Case SyntaxKind.AttributesStatement
' Module/assembly attribute
ReportError(RudeEditKind.Insert)
Return
Case SyntaxKind.Attribute
' Only module/assembly attributes are rude
If node.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then
ReportError(RudeEditKind.Insert)
End If
Return
Case SyntaxKind.AttributeList
' Only module/assembly attributes are rude
If node.IsParentKind(SyntaxKind.AttributesStatement) Then
ReportError(RudeEditKind.Insert)
End If
Return
End Select
End Sub
#End Region
#Region "Delete"
Private Sub ClassifyDelete(oldNode As SyntaxNode)
Select Case oldNode.Kind
Case SyntaxKind.OptionStatement,
SyntaxKind.AttributesStatement
ReportError(RudeEditKind.Delete)
Return
Case SyntaxKind.AttributeList
' Only module/assembly attributes are rude edits
If oldNode.IsParentKind(SyntaxKind.AttributesStatement) Then
ReportError(RudeEditKind.Insert)
End If
Return
Case SyntaxKind.Attribute
' Only module/assembly attributes are rude edits
If oldNode.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then
ReportError(RudeEditKind.Insert)
End If
Return
End Select
End Sub
#End Region
#Region "Update"
Private Sub ClassifyUpdate(newNode As SyntaxNode)
Select Case newNode.Kind
Case SyntaxKind.OptionStatement
ReportError(RudeEditKind.Update)
Return
Case SyntaxKind.AttributesStatement
ReportError(RudeEditKind.Update)
Return
Case SyntaxKind.Attribute
' Only module/assembly attributes are rude edits
If newNode.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then
ReportError(RudeEditKind.Update)
End If
Return
End Select
End Sub
#End Region
End Structure
Friend Overrides Sub ReportTopLevelSyntacticRudeEdits(
diagnostics As ArrayBuilder(Of RudeEditDiagnostic),
match As Match(Of SyntaxNode),
edit As Edit(Of SyntaxNode),
editMap As Dictionary(Of SyntaxNode, EditKind))
' For most nodes we ignore Insert and Delete edits if their parent was also inserted or deleted, respectively.
' For ModifiedIdentifiers though we check the grandparent instead because variables can move across
' VariableDeclarators. Moving a variable from a VariableDeclarator that only has a single variable results in
' deletion of that declarator. We don't want to report that delete. Similarly for moving to a new VariableDeclarator.
If edit.Kind = EditKind.Delete AndAlso
edit.OldNode.IsKind(SyntaxKind.ModifiedIdentifier) AndAlso
edit.OldNode.Parent.IsKind(SyntaxKind.VariableDeclarator) Then
If HasEdit(editMap, edit.OldNode.Parent.Parent, EditKind.Delete) Then
Return
End If
ElseIf edit.Kind = EditKind.Insert AndAlso
edit.NewNode.IsKind(SyntaxKind.ModifiedIdentifier) AndAlso
edit.NewNode.Parent.IsKind(SyntaxKind.VariableDeclarator) Then
If HasEdit(editMap, edit.NewNode.Parent.Parent, EditKind.Insert) Then
Return
End If
ElseIf HasParentEdit(editMap, edit) Then
Return
End If
Dim classifier = New EditClassifier(Me, diagnostics, edit.OldNode, edit.NewNode, edit.Kind, match)
classifier.ClassifyEdit()
End Sub
Friend Overrides Function HasUnsupportedOperation(nodes As IEnumerable(Of SyntaxNode), <Out> ByRef unsupportedNode As SyntaxNode, <Out> ByRef rudeEdit As RudeEditKind) As Boolean
' Disallow editing the body even if the change is only in trivia.
' The compiler might not emit equivallent IL for these constructs (e.g. different names of backing fields for static locals).
For Each node In nodes
Select Case node.Kind()
Case SyntaxKind.AggregateClause,
SyntaxKind.GroupByClause,
SyntaxKind.SimpleJoinClause,
SyntaxKind.GroupJoinClause
unsupportedNode = node
rudeEdit = RudeEditKind.ComplexQueryExpression
Return True
Case SyntaxKind.LocalDeclarationStatement
Dim declaration = DirectCast(node, LocalDeclarationStatementSyntax)
If declaration.Modifiers.Any(SyntaxKind.StaticKeyword) Then
unsupportedNode = node
rudeEdit = RudeEditKind.UpdateStaticLocal
Return True
End If
End Select
Next
unsupportedNode = Nothing
rudeEdit = RudeEditKind.None
Return False
End Function
#End Region
#Region "Semantic Rude Edits"
Protected Overrides Function AreHandledEventsEqual(oldMethod As IMethodSymbol, newMethod As IMethodSymbol) As Boolean
Return oldMethod.HandledEvents.SequenceEqual(
newMethod.HandledEvents,
Function(x, y)
Return x.HandlesKind = y.HandlesKind AndAlso
SymbolsEquivalent(x.EventContainer, y.EventContainer) AndAlso
SymbolsEquivalent(x.EventSymbol, y.EventSymbol) AndAlso
SymbolsEquivalent(x.WithEventsSourceProperty, y.WithEventsSourceProperty)
End Function)
End Function
Friend Overrides Sub ReportInsertedMemberSymbolRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), newSymbol As ISymbol, newNode As SyntaxNode, insertingIntoExistingContainingType As Boolean)
Dim kind = GetInsertedMemberSymbolRudeEditKind(newSymbol, insertingIntoExistingContainingType)
If kind <> RudeEditKind.None Then
diagnostics.Add(New RudeEditDiagnostic(
kind,
GetDiagnosticSpan(newNode, EditKind.Insert),
newNode,
arguments:={GetDisplayName(newNode, EditKind.Insert)}))
End If
End Sub
Private Shared Function GetInsertedMemberSymbolRudeEditKind(newSymbol As ISymbol, insertingIntoExistingContainingType As Boolean) As RudeEditKind
Select Case newSymbol.Kind
Case SymbolKind.Method
Dim method = DirectCast(newSymbol, IMethodSymbol)
' Inserting P/Invoke into a new or existing type is not allowed.
If method.GetDllImportData() IsNot Nothing Then
Return RudeEditKind.InsertDllImport
End If
' Inserting method with handles clause into a new or existing type is not allowed.
If Not method.HandledEvents.IsEmpty Then
Return RudeEditKind.InsertHandlesClause
End If
Case SymbolKind.NamedType
Dim type = CType(newSymbol, INamedTypeSymbol)
' Inserting module is not allowed.
If type.TypeKind = TypeKind.Module Then
Return RudeEditKind.Insert
End If
Return RudeEditKind.None
End Select
' All rude edits below only apply when inserting into an existing type (not when the type itself is inserted):
If Not insertingIntoExistingContainingType Then
Return RudeEditKind.None
End If
' Inserting virtual or interface member is not allowed.
If newSymbol.IsVirtual Or newSymbol.IsOverride Or newSymbol.IsAbstract Then
Return RudeEditKind.InsertVirtual
End If
Select Case newSymbol.Kind
Case SymbolKind.Method
Dim method = DirectCast(newSymbol, IMethodSymbol)
' Inserting operator to an existing type is not allowed.
If method.MethodKind = MethodKind.Conversion OrElse method.MethodKind = MethodKind.UserDefinedOperator Then
Return RudeEditKind.InsertOperator
End If
Return RudeEditKind.None
Case SymbolKind.Field
' Inserting a field into an enum is not allowed.
If newSymbol.ContainingType.TypeKind = TypeKind.Enum Then
Return RudeEditKind.Insert
End If
Return RudeEditKind.None
End Select
Return RudeEditKind.None
End Function
#End Region
#Region "Exception Handling Rude Edits"
Protected Overrides Function GetExceptionHandlingAncestors(node As SyntaxNode, root As SyntaxNode, isNonLeaf As Boolean) As List(Of SyntaxNode)
Dim result = New List(Of SyntaxNode)()
While node IsNot root
Dim kind = node.Kind
Select Case kind
Case SyntaxKind.TryBlock
If isNonLeaf Then
result.Add(node)
End If
Case SyntaxKind.CatchBlock,
SyntaxKind.FinallyBlock
result.Add(node)
Debug.Assert(node.Parent.Kind = SyntaxKind.TryBlock)
node = node.Parent
Case SyntaxKind.ClassBlock,
SyntaxKind.StructureBlock
' stop at type declaration
Exit While
End Select
' stop at lambda
If LambdaUtilities.IsLambda(node) Then
Exit While
End If
Debug.Assert(node.Parent IsNot Nothing)
node = node.Parent
End While
Return result
End Function
Friend Overrides Sub ReportEnclosingExceptionHandlingRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic),
exceptionHandlingEdits As IEnumerable(Of Edit(Of SyntaxNode)),
oldStatement As SyntaxNode,
newStatementSpan As TextSpan)
For Each edit In exceptionHandlingEdits
Debug.Assert(edit.Kind <> EditKind.Update OrElse edit.OldNode.RawKind = edit.NewNode.RawKind)
If edit.Kind <> EditKind.Update OrElse Not AreExceptionHandlingPartsEquivalent(edit.OldNode, edit.NewNode) Then
AddAroundActiveStatementRudeDiagnostic(diagnostics, edit.OldNode, edit.NewNode, newStatementSpan)
End If
Next
End Sub
Private Shared Function AreExceptionHandlingPartsEquivalent(oldNode As SyntaxNode, newNode As SyntaxNode) As Boolean
Select Case oldNode.Kind
Case SyntaxKind.TryBlock
Dim oldTryBlock = DirectCast(oldNode, TryBlockSyntax)
Dim newTryBlock = DirectCast(newNode, TryBlockSyntax)
Return SyntaxFactory.AreEquivalent(oldTryBlock.FinallyBlock, newTryBlock.FinallyBlock) AndAlso
SyntaxFactory.AreEquivalent(oldTryBlock.CatchBlocks, newTryBlock.CatchBlocks)
Case SyntaxKind.CatchBlock,
SyntaxKind.FinallyBlock
Return SyntaxFactory.AreEquivalent(oldNode, newNode)
Case Else
Throw ExceptionUtilities.UnexpectedValue(oldNode.Kind)
End Select
End Function
''' <summary>
''' An active statement (leaf or not) inside a "Catch" makes the Catch part readonly.
''' An active statement (leaf or not) inside a "Finally" makes the whole Try/Catch/Finally part read-only.
''' An active statement (non leaf) inside a "Try" makes the Catch/Finally part read-only.
''' </summary>
Protected Overrides Function GetExceptionHandlingRegion(node As SyntaxNode, <Out> ByRef coversAllChildren As Boolean) As TextSpan
Select Case node.Kind
Case SyntaxKind.TryBlock
Dim tryBlock = DirectCast(node, TryBlockSyntax)
coversAllChildren = False
If tryBlock.CatchBlocks.Count = 0 Then
Debug.Assert(tryBlock.FinallyBlock IsNot Nothing)
Return TextSpan.FromBounds(tryBlock.FinallyBlock.SpanStart, tryBlock.EndTryStatement.Span.End)
End If
Return TextSpan.FromBounds(tryBlock.CatchBlocks.First().SpanStart, tryBlock.EndTryStatement.Span.End)
Case SyntaxKind.CatchBlock
coversAllChildren = True
Return node.Span
Case SyntaxKind.FinallyBlock
coversAllChildren = True
Return DirectCast(node.Parent, TryBlockSyntax).Span
Case Else
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
End Function
#End Region
#Region "State Machines"
Friend Overrides Function IsStateMachineMethod(declaration As SyntaxNode) As Boolean
Return SyntaxUtilities.IsAsyncMethodOrLambda(declaration) OrElse
SyntaxUtilities.IsIteratorMethodOrLambda(declaration)
End Function
Friend Shared Function GetStateMachineInfo(body As SyntaxNode) As StateMachineInfo
' In VB declaration and body are represented by the same node for both lambdas and methods (unlike C#)
If SyntaxUtilities.IsAsyncMethodOrLambda(body) Then
Return New StateMachineInfo(IsAsync:=True, IsIterator:=False, HasSuspensionPoints:=SyntaxUtilities.GetAwaitExpressions(body).Any())
ElseIf SyntaxUtilities.IsIteratorMethodOrLambda(body) Then
Return New StateMachineInfo(IsAsync:=False, IsIterator:=True, HasSuspensionPoints:=SyntaxUtilities.GetYieldStatements(body).Any())
Else
Return StateMachineInfo.None
End If
End Function
Friend Overrides Sub ReportStateMachineSuspensionPointRudeEdits(diagnosticContext As DiagnosticContext, oldNode As SyntaxNode, newNode As SyntaxNode)
' TODO: changes around suspension points (foreach, lock, using, etc.)
If newNode.IsKind(SyntaxKind.AwaitExpression) AndAlso oldNode.IsKind(SyntaxKind.AwaitExpression) Then
Dim oldContainingStatementPart = FindContainingStatementPart(oldNode)
Dim newContainingStatementPart = FindContainingStatementPart(newNode)
' If the old statement has spilled state and the new doesn't, the edit is ok. We'll just not use the spilled state.
If Not SyntaxFactory.AreEquivalent(oldContainingStatementPart, newContainingStatementPart) AndAlso
Not HasNoSpilledState(newNode, newContainingStatementPart) Then
diagnosticContext.Report(RudeEditKind.AwaitStatementUpdate, newContainingStatementPart.Span)
End If
End If
End Sub
Private Shared Function FindContainingStatementPart(node As SyntaxNode) As SyntaxNode
Dim statement = TryCast(node, StatementSyntax)
While statement Is Nothing
Select Case node.Parent.Kind()
Case SyntaxKind.ForStatement,
SyntaxKind.ForEachStatement,
SyntaxKind.IfStatement,
SyntaxKind.WhileStatement,
SyntaxKind.SimpleDoStatement,
SyntaxKind.SelectStatement,
SyntaxKind.UsingStatement
Return node
End Select
If LambdaUtilities.IsLambdaBodyStatementOrExpression(node) Then
Return node
End If
node = node.Parent
statement = TryCast(node, StatementSyntax)
End While
Return statement
End Function
Private Shared Function HasNoSpilledState(awaitExpression As SyntaxNode, containingStatementPart As SyntaxNode) As Boolean
Debug.Assert(awaitExpression.IsKind(SyntaxKind.AwaitExpression))
' There is nothing within the statement part surrounding the await expression.
If containingStatementPart Is awaitExpression Then
Return True
End If
Select Case containingStatementPart.Kind()
Case SyntaxKind.ExpressionStatement,
SyntaxKind.ReturnStatement
Dim expression = GetExpressionFromStatementPart(containingStatementPart)
' Await <expr>
' Return Await <expr>
If expression Is awaitExpression Then
Return True
End If
' <ident> = Await <expr>
' Return <ident> = Await <expr>
Return IsSimpleAwaitAssignment(expression, awaitExpression)
Case SyntaxKind.VariableDeclarator
' <ident> = Await <expr> in using, for, etc
' EqualsValue -> VariableDeclarator
Return awaitExpression.Parent.Parent Is containingStatementPart
Case SyntaxKind.LoopUntilStatement,
SyntaxKind.LoopWhileStatement,
SyntaxKind.DoUntilStatement,
SyntaxKind.DoWhileStatement
' Until Await <expr>
' UntilClause -> LoopUntilStatement
Return awaitExpression.Parent.Parent Is containingStatementPart
Case SyntaxKind.LocalDeclarationStatement
' Dim <ident> = Await <expr>
' EqualsValue -> VariableDeclarator -> LocalDeclarationStatement
Return awaitExpression.Parent.Parent.Parent Is containingStatementPart
End Select
Return IsSimpleAwaitAssignment(containingStatementPart, awaitExpression)
End Function
Private Shared Function GetExpressionFromStatementPart(statement As SyntaxNode) As ExpressionSyntax
Select Case statement.Kind()
Case SyntaxKind.ExpressionStatement
Return DirectCast(statement, ExpressionStatementSyntax).Expression
Case SyntaxKind.ReturnStatement
Return DirectCast(statement, ReturnStatementSyntax).Expression
Case Else
Throw ExceptionUtilities.UnexpectedValue(statement.Kind())
End Select
End Function
Private Shared Function IsSimpleAwaitAssignment(node As SyntaxNode, awaitExpression As SyntaxNode) As Boolean
If node.IsKind(SyntaxKind.SimpleAssignmentStatement) Then
Dim assignment = DirectCast(node, AssignmentStatementSyntax)
Return assignment.Left.IsKind(SyntaxKind.IdentifierName) AndAlso assignment.Right Is awaitExpression
End If
Return False
End Function
#End Region
#Region "Rude Edits around Active Statement"
Friend Overrides Sub ReportOtherRudeEditsAroundActiveStatement(diagnostics As ArrayBuilder(Of RudeEditDiagnostic),
forwardMap As IReadOnlyDictionary(Of SyntaxNode, SyntaxNode),
oldActiveStatement As SyntaxNode,
oldBody As DeclarationBody,
oldModel As SemanticModel,
newActiveStatement As SyntaxNode,
newBody As DeclarationBody,
newModel As SemanticModel,
isNonLeaf As Boolean,
cancellationToken As CancellationToken)
Dim onErrorOrResumeStatement = FindOnErrorOrResumeStatement(newBody)
If onErrorOrResumeStatement IsNot Nothing Then
AddAroundActiveStatementRudeDiagnostic(diagnostics, oldActiveStatement, onErrorOrResumeStatement, newActiveStatement.Span)
End If
ReportRudeEditsForAncestorsDeclaringInterStatementTemps(diagnostics, forwardMap, oldActiveStatement, oldBody.EncompassingAncestor, oldModel, newActiveStatement, newBody.EncompassingAncestor, newModel, cancellationToken)
End Sub
Private Shared Function FindOnErrorOrResumeStatement(newBody As DeclarationBody) As SyntaxNode
For Each newRoot In newBody.RootNodes
For Each node In newRoot.DescendantNodes(AddressOf ChildrenCompiledInBody)
Select Case node.Kind
Case SyntaxKind.OnErrorGoToLabelStatement,
SyntaxKind.OnErrorGoToMinusOneStatement,
SyntaxKind.OnErrorGoToZeroStatement,
SyntaxKind.OnErrorResumeNextStatement,
SyntaxKind.ResumeStatement,
SyntaxKind.ResumeNextStatement,
SyntaxKind.ResumeLabelStatement
Return node
End Select
Next
Next
Return Nothing
End Function
Private Sub ReportRudeEditsForAncestorsDeclaringInterStatementTemps(diagnostics As ArrayBuilder(Of RudeEditDiagnostic),
forwardMap As IReadOnlyDictionary(Of SyntaxNode, SyntaxNode),
oldActiveStatement As SyntaxNode,
oldEncompassingAncestor As SyntaxNode,
oldModel As SemanticModel,
newActiveStatement As SyntaxNode,
newEncompassingAncestor As SyntaxNode,
newModel As SemanticModel,
cancellationToken As CancellationToken)
' Rude Edits for Using/SyncLock/With/ForEach statements that are added/updated around an active statement.
' Although such changes are technically possible, they might lead to confusion since
' the temporary variables these statements generate won't be properly initialized.
'
' We use a simple algorithm to match each New node with its old counterpart.
' If all nodes match this algorithm Is linear, otherwise it's quadratic.
'
' Unlike exception regions matching where we use LCS, we allow reordering of the statements.
ReportUnmatchedStatements(Of SyncLockBlockSyntax)(diagnostics, forwardMap, oldActiveStatement, oldEncompassingAncestor, oldModel, newActiveStatement, newEncompassingAncestor, newModel,
nodeSelector:=Function(node) node.IsKind(SyntaxKind.SyncLockBlock),
getTypedNodes:=Function(n) OneOrMany.Create(Of SyntaxNode)(n.SyncLockStatement.Expression),
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.SyncLockStatement.Expression, n2.SyncLockStatement.Expression),
areSimilar:=Nothing,
cancellationToken)
ReportUnmatchedStatements(Of WithBlockSyntax)(diagnostics, forwardMap, oldActiveStatement, oldEncompassingAncestor, oldModel, newActiveStatement, newEncompassingAncestor, newModel,
nodeSelector:=Function(node) node.IsKind(SyntaxKind.WithBlock),
getTypedNodes:=Function(n) OneOrMany.Create(Of SyntaxNode)(n.WithStatement.Expression),
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.WithStatement.Expression, n2.WithStatement.Expression),
areSimilar:=Nothing,
cancellationToken)
ReportUnmatchedStatements(Of UsingBlockSyntax)(diagnostics, forwardMap, oldActiveStatement, oldEncompassingAncestor, oldModel, newActiveStatement, newEncompassingAncestor, newModel,
nodeSelector:=Function(node) node.IsKind(SyntaxKind.UsingBlock),
getTypedNodes:=Function(n) OneOrMany.Create(Of SyntaxNode)(n.UsingStatement.Expression),
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.UsingStatement.Expression, n2.UsingStatement.Expression),
areSimilar:=Nothing,
cancellationToken)
ReportUnmatchedStatements(Of ForEachBlockSyntax)(diagnostics, forwardMap, oldActiveStatement, oldEncompassingAncestor, oldModel, newActiveStatement, newEncompassingAncestor, newModel,
nodeSelector:=Function(node) node.IsKind(SyntaxKind.ForEachBlock),
getTypedNodes:=Function(n) OneOrMany.Create(Of SyntaxNode)(n.ForEachStatement.Expression),
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.ForOrForEachStatement, n2.ForOrForEachStatement),
areSimilar:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(DirectCast(n1.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable,
DirectCast(n2.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable),
cancellationToken)
End Sub
#End Region
End Class
End Namespace
|