/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb
2 424 строки
116 KB
Todd Grunke
Fix ArgumentNullException in VB Edit and Continue (#83250)
20 апр 2026, 22:53
Не верифицирован
20 апр 2026, 22:53
68ce286
Код
Авторство
О чём код?
' 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.Collections Imports Microsoft.CodeAnalysis.Differencing Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue <ExportLanguageService(GetType(IEditAndContinueAnalyzer), LanguageNames.VisualBasic), [Shared]> Friend NotInheritable Class VisualBasicEditAndContinueAnalyzer Inherits AbstractEditAndContinueAnalyzer <ImportingConstructor> <Obsolete(MefConstruction.ImportingConstructorMessage, True)> Public Sub New() 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 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 ' Const field initializers do not contribute to constructors Dim declarator = DirectCast(declaration.Parent, VariableDeclaratorSyntax) If declarator.Parent.IsKind(SyntaxKind.FieldDeclaration) AndAlso DirectCast(declarator.Parent, FieldDeclarationSyntax).Modifiers.Any(SyntaxKind.ConstKeyword) Then Return False End If 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 DocumentSemanticModel, newModel As DocumentSemanticModel, 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.RequiredModel, oldSymbols, cancellationToken) OrElse newNode IsNot Nothing AndAlso Not TryGetSyntaxNodesForEdit(editKind, newNode, newModel.RequiredModel, 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 DocumentSemanticModel, newModel As DocumentSemanticModel, 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.RequiredModel, topMatch.ReverseMatches, cancellationToken) Dim newContainingMemberOrType = GetParameterContainingMemberOrType(newNode, oldNode, newModel.RequiredModel, topMatch.Matches, cancellationToken) Dim matchingNewContainingMemberOrType = GetSemanticallyMatchingNewSymbol(oldContainingMemberOrType, newContainingMemberOrType, newModel.Compilation, 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 ReadOnly Property DescendantTokensIgnoringLambdaBodies As Func(Of SyntaxNode, IEnumerable(Of SyntaxToken)) Get Return AddressOf LambdaUtilities.DescendantTokensIgnoringLambdaBodies End Get End Property Friend Overrides ReadOnly Property AreTokensEquivalent As Func(Of SyntaxToken, SyntaxToken, Boolean) Get Return AddressOf SyntaxFactory.AreEquivalent 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 RudeEditDiagnosticsBuilder 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 RudeEditDiagnosticsBuilder, 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 RudeEditDiagnosticsBuilder, 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 RudeEditDiagnosticsBuilder, 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 RudeEditDiagnosticsBuilder, 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 RudeEditDiagnosticsBuilder, forwardMap As IReadOnlyDictionary(Of SyntaxNode, SyntaxNode), oldActiveStatement As SyntaxNode, oldBody As DeclarationBody, oldModel As DocumentSemanticModel, newActiveStatement As SyntaxNode, newBody As DeclarationBody, newModel As DocumentSemanticModel, 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 RudeEditDiagnosticsBuilder, forwardMap As IReadOnlyDictionary(Of SyntaxNode, SyntaxNode), oldActiveStatement As SyntaxNode, oldEncompassingAncestor As SyntaxNode, oldModel As DocumentSemanticModel, newActiveStatement As SyntaxNode, newEncompassingAncestor As SyntaxNode, newModel As DocumentSemanticModel, 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) ' Using statements with variable declarations do not introduce compiler generated temporary. ReportUnmatchedStatements(Of UsingBlockSyntax)(diagnostics, forwardMap, oldActiveStatement, oldEncompassingAncestor, oldModel, newActiveStatement, newEncompassingAncestor, newModel, nodeSelector:=Function(node) node.IsKind(SyntaxKind.UsingBlock) AndAlso DirectCast(node, UsingBlockSyntax).UsingStatement.Expression IsNot Nothing, 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 Protected Overrides Iterator Function GetParseOptionsRudeEdits(oldOptions As ParseOptions, newOptions As ParseOptions) As IEnumerable(Of Diagnostic) For Each rudeEdit In MyBase.GetParseOptionsRudeEdits(oldOptions, newOptions) Yield rudeEdit Next Dim oldVBOptions = DirectCast(oldOptions, VisualBasicParseOptions) Dim newVBOptions = DirectCast(newOptions, VisualBasicParseOptions) If oldVBOptions.LanguageVersion <> newVBOptions.LanguageVersion Then Yield CreateProjectRudeEdit(ProjectSettingKind.LangVersion, oldVBOptions.SpecifiedLanguageVersion.ToDisplayString(), newVBOptions.SpecifiedLanguageVersion.ToDisplayString()) End If If Not oldVBOptions.PreprocessorSymbols.SequenceEqual(newVBOptions.PreprocessorSymbols) Then Yield CreateProjectRudeEdit(ProjectSettingKind.DefineConstants, GetPreprocessorSymbolsDisplay(oldVBOptions.PreprocessorSymbols), GetPreprocessorSymbolsDisplay(newVBOptions.PreprocessorSymbols)) End If End Function Private Shared Function GetPreprocessorSymbolsDisplay(symbols As ImmutableArray(Of KeyValuePair(Of String, Object))) As String Return String.Join(",", symbols.Select(Function(kvp) kvp.Key & "=" & kvp.Value.ToString())) End Function Protected Overrides Iterator Function GetCompilationOptionsRudeEdits(oldOptions As CompilationOptions, newOptions As CompilationOptions) As IEnumerable(Of Diagnostic) For Each rudeEdit In MyBase.GetCompilationOptionsRudeEdits(oldOptions, newOptions) Yield rudeEdit Next Dim oldVBOptions = DirectCast(oldOptions, VisualBasicCompilationOptions) Dim newVBOptions = DirectCast(newOptions, VisualBasicCompilationOptions) If oldVBOptions.RootNamespace <> newVBOptions.RootNamespace Then Yield CreateProjectRudeEdit(ProjectSettingKind.RootNamespace, oldVBOptions.RootNamespace, newVBOptions.RootNamespace) End If If oldVBOptions.OptionStrict <> newVBOptions.OptionStrict Then Yield CreateProjectRudeEdit(ProjectSettingKind.OptionStrict, oldVBOptions.OptionStrict.ToString(), newVBOptions.OptionStrict.ToString()) End If If oldVBOptions.OptionInfer <> newVBOptions.OptionInfer Then Yield CreateProjectRudeEdit(ProjectSettingKind.OptionInfer, GetOptionDisplay(oldVBOptions.OptionInfer), GetOptionDisplay(newVBOptions.OptionInfer)) End If If oldVBOptions.OptionExplicit <> newVBOptions.OptionExplicit Then Yield CreateProjectRudeEdit(ProjectSettingKind.OptionExplicit, GetOptionDisplay(oldVBOptions.OptionExplicit), GetOptionDisplay(newVBOptions.OptionExplicit)) End If If oldVBOptions.OptionCompareText <> newVBOptions.OptionCompareText Then Yield CreateProjectRudeEdit(ProjectSettingKind.OptionCompare, GetOptionCompareTextDisplay(oldVBOptions.OptionCompareText), GetOptionCompareTextDisplay(newVBOptions.OptionCompareText)) End If End Function Private Shared Function GetOptionDisplay(value As Boolean) As String Return If(value, "On", "Off") End Function Private Shared Function GetOptionCompareTextDisplay(value As Boolean) As String Return If(value, "Text", "Binary") End Function End Class End Namespace