|
' 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 Microsoft.CodeAnalysis.Differencing
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Friend NotInheritable Class SyntaxComparer
Inherits AbstractSyntaxComparer
Friend Shared ReadOnly TopLevel As SyntaxComparer = New SyntaxComparer(Nothing, Nothing, Nothing, Nothing, compareStatementSyntax:=False)
Friend Shared ReadOnly Statement As SyntaxComparer = New SyntaxComparer(Nothing, Nothing, Nothing, Nothing, compareStatementSyntax:=True)
Private ReadOnly _matchingLambdas As Boolean
Public Sub New(oldRoot As SyntaxNode, newRoot As SyntaxNode, oldRootChildren As IEnumerable(Of SyntaxNode), newRootChildren As IEnumerable(Of SyntaxNode), Optional matchingLambdas As Boolean = False, Optional compareStatementSyntax As Boolean = False)
MyBase.New(oldRoot, newRoot, oldRootChildren, newRootChildren, compareStatementSyntax)
_matchingLambdas = matchingLambdas
End Sub
Protected Overrides Function IsLambdaBodyStatementOrExpression(node As SyntaxNode) As Boolean
Return LambdaUtilities.IsLambdaBodyStatementOrExpression(node)
End Function
#Region "Labels"
' Assumptions:
' - Each listed label corresponds to one or more syntax kinds.
' - Nodes with same labels might produce Update edits, nodes with different labels don't.
' - If IsTiedToParent(label) is true for a label then all its possible parent labels must precede the label.
' (i.e. both MethodDeclaration and TypeDeclaration must precede TypeParameter label).
' - All descendants of a node whose kind is listed here will be ignored regardless of their labels
Friend Enum Label
'
' Top Syntax
'
CompilationUnit
[Option] ' tied to parent
Import ' tied to parent
Attributes ' tied to parent
NamespaceDeclaration
TypeDeclaration
EnumDeclaration
DelegateDeclaration
FieldDeclaration ' tied to parent
FieldVariableDeclarator ' tied to parent
PInvokeDeclaration ' tied to parent
MethodDeclaration ' tied to parent
ConstructorDeclaration ' tied to parent
OperatorDeclaration ' tied to parent
PropertyDeclaration ' tied to parent
CustomEventDeclaration ' tied to parent
EnumMemberDeclaration ' tied to parent
AccessorDeclaration ' tied to parent
' Opening statement of a type, method, operator, constructor, property, and accessor.
' We need to represent this node in the graph since attributes, (generic) parameters are its children.
' However, we don't need to have a specialized label for each type of declaration statement since
' they are tied to the parent and each parent has a single declaration statement.
DeclarationStatement ' tied to parent
' Event statement is either a child of a custom event or a stand-alone event field declaration.
EventStatement ' tied to parent
TypeParameterList ' tied to parent
TypeParameter ' tied to parent
FieldOrParameterName ' tied to grand-grandparent (type or method declaration)
AttributeList ' tied to parent
Attribute ' tied to parent
'
' Statement Syntax
'
BodyBlock
' We need to represent sub/function/ctor/accessor/operator declaration
' begin And End statements since they may be active statements.
BodyBegin ' tied to parent
LambdaRoot
TryBlock
TryStatement ' tied to parent
CatchBlock ' tied to parent
CatchStatement ' tied to parent
FinallyBlock ' tied to parent
FinallyStatement ' tied to parent
CatchFilterClause ' tied to parent
EndTryStatement ' tied to parent
ForBlock
ForEachBlock
ForEachStatement ' tied to parent
ForStatement ' tied to parent
ForStepClause ' tied to parent
NextStatement ' tied to parent
UsingBlockWithDeclarations
UsingBlockWithExpression
UsingStatement ' tied to parent
EndUsingStatement ' tied to parent
SyncLockBlock
SyncLockStatement ' tied to parent
EndSyncLockStatement ' tied to parent
WithBlock
WithStatement ' tied to parent
EndWithStatement ' tied to parent
DoWhileBlock
DoWhileStatement ' tied to parent
EndLoop ' tied to parent
WhileOrUntilClause ' tied to parent
IfBlock
IfStatement ' tied to parent
ElseIfBlock ' tied to parent
ElseIfStatement ' tied to parent
ElseBlock ' tied to parent
ElseStatement ' tied to parent
EndIfStatement ' tied to parent
SelectBlock
SelectStatement ' tied to parent
CaseBlock ' tied to parent
CaseStatement ' tied to parent
CaseClause ' tied to parent
EndSelectStatement ' tied to parent
ReDimStatement
ReDimClause ' tied to parent
' Exit Sub|Function|Operator|Property|For|While|Do|Select|Try
ExitStatement
' Continue While|Do|For
ContinueStatement
' Throw, Throw expr
ThrowStatement
ErrorStatement
' Return, Return expr
ReturnStatement
OnErrorStatement
ResumeStatement
' GoTo, Stop, End
GoToStatement
LabelStatement
EraseStatement
ExpressionStatement
AssignmentStatement
EventHandlerStatement
YieldStatement
LocalDeclarationStatement ' tied to parent
LocalVariableDeclarator ' tied to parent
AwaitExpression
Lambda
LambdaBodyBegin ' tied to parent
QueryExpression
AggregateClause ' tied to parent
JoinClause ' tied to parent
FromClause ' tied to parent
WhereClauseLambda ' tied to parent
LetClause ' tied to parent
SelectClauseLambda ' tied to parent
PartitionWhileLambda ' tied to parent
PartitionClause ' tied to parent
GroupByClause ' tied to parent
OrderByClause ' tied to parent
CollectionRangeVariable ' tied to parent (FromClause, JoinClause, AggregateClause)
ExpressionRangeVariable ' tied to parent (LetClause, SelectClause, GroupByClause keys)
ExpressionRangeVariableItems ' tied to parent (GroupByClause items)
FunctionAggregationLambda ' tied to parent (JoinClause, GroupByClause, AggregateClause)
OrderingLambda ' tied to parent (OrderByClause)
JoinConditionLambda ' tied to parent (JoinClause)
ParameterList ' tied to parent
Parameter ' tied to parent
LocalVariableName ' tied to parent
BodyEnd ' tied to parent
' helpers
Count
Ignored = IgnoredNode
End Enum
''' <summary>
''' Return true if it is desirable to report two edits (delete and insert) rather than a move edit
''' when the node changes its parent.
''' </summary>
Private Overloads Shared Function TiedToAncestor(label As Label) As Integer
Select Case label
' Top level syntax
Case Label.Option,
Label.Import,
Label.Attributes,
Label.FieldDeclaration,
Label.FieldVariableDeclarator,
Label.PInvokeDeclaration,
Label.MethodDeclaration,
Label.OperatorDeclaration,
Label.ConstructorDeclaration,
Label.PropertyDeclaration,
Label.CustomEventDeclaration,
Label.EnumMemberDeclaration,
Label.AccessorDeclaration,
Label.DeclarationStatement,
Label.EventStatement,
Label.TypeParameterList,
Label.TypeParameter,
Label.ParameterList,
Label.Parameter,
Label.AttributeList,
Label.Attribute
Return 1
Case Label.FieldOrParameterName
Return 3 ' type or method declaration
' Statement syntax
Case Label.BodyBegin,
Label.LambdaBodyBegin,
Label.BodyEnd,
Label.TryStatement,
Label.CatchBlock,
Label.CatchStatement,
Label.FinallyBlock,
Label.FinallyStatement,
Label.CatchFilterClause,
Label.EndTryStatement,
Label.ForEachStatement,
Label.ForStatement,
Label.ForStepClause,
Label.NextStatement,
Label.UsingStatement,
Label.EndUsingStatement,
Label.SyncLockStatement,
Label.EndSyncLockStatement,
Label.WithStatement,
Label.EndWithStatement,
Label.DoWhileStatement,
Label.WhileOrUntilClause,
Label.EndLoop,
Label.IfStatement,
Label.ElseIfBlock,
Label.ElseIfStatement,
Label.ElseBlock,
Label.ElseStatement,
Label.EndIfStatement,
Label.SelectStatement,
Label.CaseBlock,
Label.CaseStatement,
Label.CaseClause,
Label.EndSelectStatement,
Label.ReDimClause,
Label.AggregateClause,
Label.JoinClause,
Label.FromClause,
Label.WhereClauseLambda,
Label.LetClause,
Label.SelectClauseLambda,
Label.PartitionWhileLambda,
Label.PartitionClause,
Label.GroupByClause,
Label.OrderByClause,
Label.CollectionRangeVariable,
Label.ExpressionRangeVariable,
Label.ExpressionRangeVariableItems,
Label.FunctionAggregationLambda,
Label.OrderingLambda,
Label.JoinConditionLambda,
Label.LocalDeclarationStatement,
Label.LocalVariableDeclarator,
Label.LocalVariableName
Return 1
Case Else
Return 0
End Select
Throw New NotImplementedException()
End Function
Friend Overrides Function Classify(kind As Integer, node As SyntaxNode, ByRef isLeaf As Boolean) As Integer
Return Classify(CType(kind, SyntaxKind), node, isLeaf, ignoreVariableDeclarations:=False)
End Function
' internal for testing
Friend Overloads Function Classify(kind As SyntaxKind, nodeOpt As SyntaxNode, ByRef isLeaf As Boolean, ignoreVariableDeclarations As Boolean) As Label
If _compareStatementSyntax Then
Return ClassifyStatementSyntax(kind, nodeOpt, isLeaf)
End If
Return ClassifyTopSyntax(kind, nodeOpt, isLeaf, ignoreVariableDeclarations)
End Function
Friend Shared Function ClassifyStatementSyntax(kind As SyntaxKind, nodeOpt As SyntaxNode, ByRef isLeaf As Boolean) As Label
isLeaf = False
Select Case kind
Case SyntaxKind.SubBlock,
SyntaxKind.ConstructorBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.OperatorBlock,
SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock
Return Label.BodyBlock
Case SyntaxKind.SubStatement,
SyntaxKind.SubNewStatement,
SyntaxKind.FunctionStatement,
SyntaxKind.OperatorStatement,
SyntaxKind.GetAccessorStatement,
SyntaxKind.SetAccessorStatement,
SyntaxKind.AddHandlerAccessorStatement,
SyntaxKind.RemoveHandlerAccessorStatement,
SyntaxKind.RaiseEventAccessorStatement
isLeaf = True
Return Label.BodyBegin
Case SyntaxKind.SubLambdaHeader,
SyntaxKind.FunctionLambdaHeader
Return Label.LambdaBodyBegin
Case SyntaxKind.EndSubStatement,
SyntaxKind.EndFunctionStatement,
SyntaxKind.EndOperatorStatement,
SyntaxKind.EndGetStatement,
SyntaxKind.EndSetStatement,
SyntaxKind.EndAddHandlerStatement,
SyntaxKind.EndRemoveHandlerStatement,
SyntaxKind.EndRaiseEventStatement
isLeaf = True
Return Label.BodyEnd
Case SyntaxKind.SimpleDoLoopBlock,
SyntaxKind.DoWhileLoopBlock,
SyntaxKind.DoUntilLoopBlock,
SyntaxKind.DoLoopWhileBlock,
SyntaxKind.DoLoopUntilBlock,
SyntaxKind.WhileBlock
Return Label.DoWhileBlock
Case SyntaxKind.SimpleDoStatement, SyntaxKind.DoWhileStatement, SyntaxKind.DoUntilStatement,
SyntaxKind.WhileStatement
Return Label.DoWhileStatement
Case SyntaxKind.WhileClause,
SyntaxKind.UntilClause
Return Label.WhileOrUntilClause
Case SyntaxKind.SimpleLoopStatement, SyntaxKind.LoopWhileStatement, SyntaxKind.LoopUntilStatement,
SyntaxKind.EndWhileStatement
Return Label.EndLoop
Case SyntaxKind.ForBlock
Return Label.ForBlock
Case SyntaxKind.ForEachBlock
Return Label.ForEachBlock
Case SyntaxKind.ForStatement
Return Label.ForStatement
Case SyntaxKind.ForEachStatement
Return Label.ForEachStatement
Case SyntaxKind.ForStepClause
Return Label.ForStepClause
Case SyntaxKind.NextStatement
isLeaf = True
Return Label.NextStatement
Case SyntaxKind.UsingBlock
' We need to distinguish using statements with expression or single variable declaration from ones with multiple variable declarations.
' The former generate a single try-finally block, the latter one for each variable. The finally blocks need to match since they
' affect state machine state matching. For simplicity we do not match single-declaration to expression, we just treat usings
' with declarations entirely separately from usings with expressions.
'
' The parent is not available only when comparing nodes for value equality.
' In that case it doesn't matter what label the node has as long as it has some.
Return If(TryCast(nodeOpt, UsingBlockSyntax)?.UsingStatement.Variables IsNot Nothing, Label.UsingBlockWithDeclarations, Label.UsingBlockWithExpression)
Case SyntaxKind.UsingStatement
Return Label.UsingStatement
Case SyntaxKind.EndUsingStatement
isLeaf = True
Return Label.EndUsingStatement
Case SyntaxKind.SyncLockBlock
Return Label.SyncLockBlock
Case SyntaxKind.SyncLockStatement
Return Label.SyncLockStatement
Case SyntaxKind.EndSyncLockStatement
isLeaf = True
Return Label.EndSyncLockStatement
Case SyntaxKind.WithBlock
Return Label.WithBlock
Case SyntaxKind.WithStatement
Return Label.WithStatement
Case SyntaxKind.EndWithStatement
isLeaf = True
Return Label.EndWithStatement
Case SyntaxKind.LocalDeclarationStatement
Return Label.LocalDeclarationStatement
Case SyntaxKind.VariableDeclarator
Return Label.LocalVariableDeclarator
Case SyntaxKind.ModifiedIdentifier
Return Label.LocalVariableName
Case SyntaxKind.MultiLineIfBlock,
SyntaxKind.SingleLineIfStatement
Return Label.IfBlock
Case SyntaxKind.MultiLineIfBlock,
SyntaxKind.SingleLineIfStatement
Return Label.IfBlock
Case SyntaxKind.IfStatement
Return Label.IfStatement
Case SyntaxKind.ElseIfBlock
Return Label.ElseIfBlock
Case SyntaxKind.ElseIfStatement
Return Label.ElseIfStatement
Case SyntaxKind.ElseBlock,
SyntaxKind.SingleLineElseClause
Return Label.ElseBlock
Case SyntaxKind.ElseStatement
isLeaf = True
Return Label.ElseStatement
Case SyntaxKind.EndIfStatement
isLeaf = True
Return Label.EndIfStatement
Case SyntaxKind.TryBlock
Return Label.TryBlock
Case SyntaxKind.TryBlock
Return Label.TryBlock
Case SyntaxKind.TryStatement
Return Label.TryStatement
Case SyntaxKind.CatchBlock
Return Label.CatchBlock
Case SyntaxKind.CatchStatement
Return Label.CatchStatement
Case SyntaxKind.FinallyBlock
Return Label.FinallyBlock
Case SyntaxKind.FinallyStatement
Return Label.FinallyStatement
Case SyntaxKind.CatchFilterClause
Return Label.CatchFilterClause
Case SyntaxKind.EndTryStatement
isLeaf = True
Return Label.EndTryStatement
Case SyntaxKind.ErrorStatement
isLeaf = True
Return Label.ErrorStatement
Case SyntaxKind.ThrowStatement
Return Label.ThrowStatement
Case SyntaxKind.OnErrorGoToZeroStatement,
SyntaxKind.OnErrorGoToMinusOneStatement,
SyntaxKind.OnErrorGoToLabelStatement,
SyntaxKind.OnErrorResumeNextStatement
Return Label.OnErrorStatement
Case SyntaxKind.ResumeStatement,
SyntaxKind.ResumeLabelStatement,
SyntaxKind.ResumeNextStatement
Return Label.ResumeStatement
Case SyntaxKind.SelectBlock
Return Label.SelectBlock
Case SyntaxKind.SelectStatement
Return Label.SelectStatement
Case SyntaxKind.CaseBlock,
SyntaxKind.CaseElseBlock
Return Label.CaseBlock
Case SyntaxKind.CaseStatement,
SyntaxKind.CaseElseStatement
Return Label.CaseStatement
Case SyntaxKind.ElseCaseClause,
SyntaxKind.SimpleCaseClause,
SyntaxKind.RangeCaseClause,
SyntaxKind.CaseEqualsClause,
SyntaxKind.CaseNotEqualsClause,
SyntaxKind.CaseLessThanClause,
SyntaxKind.CaseLessThanOrEqualClause,
SyntaxKind.CaseGreaterThanOrEqualClause,
SyntaxKind.CaseGreaterThanClause
Return Label.CaseClause
Case SyntaxKind.EndSelectStatement
isLeaf = True
Return Label.EndSelectStatement
Case SyntaxKind.ExitForStatement,
SyntaxKind.ExitDoStatement,
SyntaxKind.ExitWhileStatement,
SyntaxKind.ExitSelectStatement,
SyntaxKind.ExitTryStatement,
SyntaxKind.ExitSubStatement,
SyntaxKind.ExitFunctionStatement,
SyntaxKind.ExitOperatorStatement,
SyntaxKind.ExitPropertyStatement
isLeaf = True
Return Label.ExitStatement
Case SyntaxKind.ContinueWhileStatement,
SyntaxKind.ContinueDoStatement,
SyntaxKind.ContinueForStatement
isLeaf = True
Return Label.ContinueStatement
Case SyntaxKind.ReturnStatement
Return Label.ReturnStatement
Case SyntaxKind.GoToStatement,
SyntaxKind.StopStatement,
SyntaxKind.EndStatement
isLeaf = True
Return Label.GoToStatement
Case SyntaxKind.LabelStatement
isLeaf = True
Return Label.LabelStatement
Case SyntaxKind.EraseStatement
isLeaf = True
Return Label.EraseStatement
Case SyntaxKind.ExpressionStatement,
SyntaxKind.CallStatement
Return Label.ExpressionStatement
Case SyntaxKind.MidAssignmentStatement,
SyntaxKind.SimpleAssignmentStatement,
SyntaxKind.AddAssignmentStatement,
SyntaxKind.SubtractAssignmentStatement,
SyntaxKind.MultiplyAssignmentStatement,
SyntaxKind.DivideAssignmentStatement,
SyntaxKind.IntegerDivideAssignmentStatement,
SyntaxKind.ExponentiateAssignmentStatement,
SyntaxKind.LeftShiftAssignmentStatement,
SyntaxKind.RightShiftAssignmentStatement,
SyntaxKind.ConcatenateAssignmentStatement
Return Label.AssignmentStatement
Case SyntaxKind.AddHandlerStatement,
SyntaxKind.RemoveHandlerStatement,
SyntaxKind.RaiseEventStatement
Return Label.EventHandlerStatement
Case SyntaxKind.ReDimStatement,
SyntaxKind.ReDimPreserveStatement
Return Label.ReDimStatement
Case SyntaxKind.RedimClause
Return Label.ReDimClause
Case SyntaxKind.YieldStatement
Return Label.YieldStatement
Case SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression,
SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression
Return Label.Lambda
Case SyntaxKind.FunctionLambdaHeader,
SyntaxKind.SubLambdaHeader
Return Label.LambdaBodyBegin
Case SyntaxKind.ParameterList
Return Label.ParameterList
Case SyntaxKind.Parameter
Return Label.Parameter
Case SyntaxKind.QueryExpression
Return Label.QueryExpression
Case SyntaxKind.WhereClause
Return Label.WhereClauseLambda
Case SyntaxKind.LetClause
Return Label.LetClause
Case SyntaxKind.SkipClause,
SyntaxKind.TakeClause
Return Label.PartitionClause
Case SyntaxKind.TakeWhileClause,
SyntaxKind.SkipWhileClause
Return Label.PartitionWhileLambda
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Return Label.OrderingLambda
Case SyntaxKind.FunctionAggregation
Return Label.FunctionAggregationLambda
Case SyntaxKind.SelectClause
Return Label.SelectClauseLambda
Case SyntaxKind.GroupByClause
Return Label.GroupByClause
Case SyntaxKind.OrderByClause
Return Label.OrderByClause
Case SyntaxKind.SimpleJoinClause,
SyntaxKind.GroupJoinClause
Return Label.JoinClause
Case SyntaxKind.AggregateClause
Return Label.AggregateClause
Case SyntaxKind.FromClause
Return Label.FromClause
Case SyntaxKind.ExpressionRangeVariable
' Select, Let, GroupBy
'
' All ERVs need to be included in the map,
' so that we are able to map range variable declarations.
'
' Since we don't distinguish between ERVs that represent a lambda and non-lambda ERVs,
' we need to be handle cases when one maps to the other (and vice versa).
' This is handled in GetCorresondingLambdaBody.
'
' On the other hand we don't want map across lambda body boundaries.
' Hence we use a label for ERVs in Items distinct from one for Keys of a GroupBy clause.
'
' Node that the nodeOpt is Nothing only when comparing nodes for value equality.
' In that case it doesn't matter what label the node has as long as it has some.
If nodeOpt IsNot Nothing AndAlso
nodeOpt.Parent.IsKind(SyntaxKind.GroupByClause) AndAlso
nodeOpt.SpanStart < DirectCast(nodeOpt.Parent, GroupByClauseSyntax).ByKeyword.SpanStart Then
Return Label.ExpressionRangeVariableItems
Else
Return Label.ExpressionRangeVariable
End If
Case SyntaxKind.CollectionRangeVariable
' From, Aggregate
'
' All CRVs need to be included in the map,
' so that we are able to map range variable declarations.
Return Label.CollectionRangeVariable
Case SyntaxKind.JoinCondition
' TODO:
Return Label.JoinConditionLambda
Case SyntaxKind.AwaitExpression
Return Label.AwaitExpression
Case SyntaxKind.GenericName
' optimization - no need to dig into type instantiations
isLeaf = True
Return Label.Ignored
Case Else
Return Label.Ignored
End Select
End Function
Private Shared Function ClassifyTopSyntax(kind As SyntaxKind, nodeOpt As SyntaxNode, ByRef isLeaf As Boolean, ignoreVariableDeclarations As Boolean) As Label
Select Case kind
Case SyntaxKind.CompilationUnit
isLeaf = False
Return Label.CompilationUnit
Case SyntaxKind.OptionStatement
isLeaf = True
Return Label.Option
Case SyntaxKind.ImportsStatement
isLeaf = True
Return Label.Import
Case SyntaxKind.AttributesStatement
isLeaf = False
Return Label.Attributes
Case SyntaxKind.NamespaceBlock
isLeaf = False
Return Label.NamespaceDeclaration
Case SyntaxKind.ClassBlock, SyntaxKind.StructureBlock, SyntaxKind.InterfaceBlock, SyntaxKind.ModuleBlock
isLeaf = False
Return Label.TypeDeclaration
Case SyntaxKind.EnumBlock
isLeaf = False
Return Label.EnumDeclaration
Case SyntaxKind.DelegateFunctionStatement, SyntaxKind.DelegateSubStatement
isLeaf = False
Return Label.DelegateDeclaration
Case SyntaxKind.FieldDeclaration
isLeaf = False
Return Label.FieldDeclaration
Case SyntaxKind.VariableDeclarator
isLeaf = ignoreVariableDeclarations
Return If(ignoreVariableDeclarations, Label.Ignored, Label.FieldVariableDeclarator)
Case SyntaxKind.ModifiedIdentifier
isLeaf = True
Return If(ignoreVariableDeclarations, Label.Ignored, Label.FieldOrParameterName)
Case SyntaxKind.SubBlock, SyntaxKind.FunctionBlock
isLeaf = False
Return Label.MethodDeclaration
Case SyntaxKind.DeclareSubStatement, SyntaxKind.DeclareFunctionStatement
isLeaf = False
Return Label.PInvokeDeclaration
Case SyntaxKind.ConstructorBlock
isLeaf = False
Return Label.ConstructorDeclaration
Case SyntaxKind.OperatorBlock
isLeaf = False
Return Label.OperatorDeclaration
Case SyntaxKind.PropertyBlock
isLeaf = False
Return Label.PropertyDeclaration
Case SyntaxKind.EventBlock
isLeaf = False
Return Label.CustomEventDeclaration
Case SyntaxKind.EnumMemberDeclaration
isLeaf = False
Return Label.EnumMemberDeclaration
Case SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock
isLeaf = False
Return Label.AccessorDeclaration
Case SyntaxKind.ClassStatement,
SyntaxKind.StructureStatement,
SyntaxKind.InterfaceStatement,
SyntaxKind.ModuleStatement,
SyntaxKind.NamespaceStatement,
SyntaxKind.EnumStatement,
SyntaxKind.SubStatement,
SyntaxKind.SubNewStatement,
SyntaxKind.FunctionStatement,
SyntaxKind.OperatorStatement,
SyntaxKind.PropertyStatement,
SyntaxKind.GetAccessorStatement,
SyntaxKind.SetAccessorStatement,
SyntaxKind.AddHandlerAccessorStatement,
SyntaxKind.RemoveHandlerAccessorStatement,
SyntaxKind.RaiseEventAccessorStatement
isLeaf = False
Return Label.DeclarationStatement
Case SyntaxKind.EventStatement
isLeaf = False
Return Label.EventStatement
Case SyntaxKind.TypeParameterList
isLeaf = False
Return Label.TypeParameterList
Case SyntaxKind.TypeParameter
isLeaf = False
Return Label.TypeParameter
Case SyntaxKind.ParameterList
isLeaf = False
Return Label.ParameterList
Case SyntaxKind.Parameter
isLeaf = False
Return Label.Parameter
Case SyntaxKind.AttributeList
If nodeOpt IsNot Nothing AndAlso nodeOpt.IsParentKind(SyntaxKind.AttributesStatement) Then
isLeaf = False
Return Label.AttributeList
End If
isLeaf = True
Return Label.Ignored
Case SyntaxKind.Attribute
isLeaf = True
If nodeOpt IsNot Nothing AndAlso nodeOpt.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then
Return Label.Attribute
End If
Return Label.Ignored
Case Else
isLeaf = True
Return Label.Ignored
End Select
End Function
Protected Overrides Function GetLabel(node As SyntaxNode) As Integer
If _matchingLambdas AndAlso (node Is _newRoot OrElse node Is _oldRoot) Then
Return Label.LambdaRoot
End If
Return GetLabelImpl(node)
End Function
Friend Function GetLabelImpl(node As SyntaxNode) As Label
Dim isLeaf As Boolean
Return Classify(node.Kind, node, isLeaf, ignoreVariableDeclarations:=False)
End Function
'' internal for testing
Friend Overloads Function HasLabel(kind As SyntaxKind, ignoreVariableDeclarations As Boolean) As Boolean
Dim isLeaf As Boolean
Return Classify(kind, Nothing, isLeaf, ignoreVariableDeclarations) <> Label.Ignored
End Function
Protected Overrides ReadOnly Property LabelCount As Integer
Get
Return Label.Count
End Get
End Property
Protected Overrides Function TiedToAncestor(label As Integer) As Integer
Return TiedToAncestor(CType(label, Label))
End Function
#End Region
#Region "Comparisons"
Public Overrides Function ValuesEqual(left As SyntaxNode, right As SyntaxNode) As Boolean
Dim ignoreChildFunction As Func(Of SyntaxKind, Boolean)
Select Case left.Kind()
Case SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.ConstructorBlock,
SyntaxKind.OperatorBlock,
SyntaxKind.PropertyBlock,
SyntaxKind.EventBlock,
SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock
' When comparing a block containing method body statements we need to not ignore
' VariableDeclaration, ModifiedIdentifier, and AsClause children.
' But when comparing field definitions we should ignore VariableDeclaration children.
ignoreChildFunction = Function(childKind) HasLabel(childKind, ignoreVariableDeclarations:=True)
Case SyntaxKind.AttributesStatement
' Normally attributes and attribute lists are ignored, but for attribute statements
' we need to include them, so just assume they're labelled
ignoreChildFunction = Function(childKind) True
Case Else
If HasChildren(left) Then
ignoreChildFunction = Function(childKind) HasLabel(childKind, ignoreVariableDeclarations:=False)
Else
ignoreChildFunction = Nothing
End If
End Select
Return SyntaxFactory.AreEquivalent(left, right, ignoreChildFunction)
End Function
Protected Overrides Function TryComputeWeightedDistance(leftNode As SyntaxNode, rightNode As SyntaxNode, ByRef distance As Double) As Boolean
Select Case leftNode.Kind
Case SyntaxKind.SimpleDoLoopBlock,
SyntaxKind.DoWhileLoopBlock,
SyntaxKind.DoUntilLoopBlock,
SyntaxKind.DoLoopWhileBlock,
SyntaxKind.DoLoopUntilBlock,
SyntaxKind.WhileBlock
Dim getParts = Function(node As SyntaxNode)
Select Case node.Kind
Case SyntaxKind.DoLoopWhileBlock, SyntaxKind.DoLoopUntilBlock
Dim block = DirectCast(node, DoLoopBlockSyntax)
Return New With {.Begin = CType(block.LoopStatement, StatementSyntax), block.Statements}
Case SyntaxKind.WhileBlock
Dim block = DirectCast(node, WhileBlockSyntax)
Return New With {.Begin = CType(block.WhileStatement, StatementSyntax), block.Statements}
Case Else
Dim block = DirectCast(node, DoLoopBlockSyntax)
Return New With {.Begin = CType(block.DoStatement, StatementSyntax), block.Statements}
End Select
End Function
Dim leftParts = getParts(leftNode)
Dim rightParts = getParts(rightNode)
distance = ComputeWeightedDistance(leftParts.Begin, leftParts.Statements, rightParts.Begin, rightParts.Statements)
Return True
Case SyntaxKind.ForBlock
Dim leftFor = DirectCast(leftNode, ForOrForEachBlockSyntax)
Dim rightFor = DirectCast(rightNode, ForOrForEachBlockSyntax)
Dim leftStatement = DirectCast(leftFor.ForOrForEachStatement, ForStatementSyntax)
Dim rightStatement = DirectCast(rightFor.ForOrForEachStatement, ForStatementSyntax)
distance = ComputeWeightedDistance(leftStatement.ControlVariable,
leftFor.ForOrForEachStatement,
leftFor.Statements,
rightStatement.ControlVariable,
rightFor.ForOrForEachStatement,
rightFor.Statements)
Return True
Case SyntaxKind.ForEachBlock
Dim leftFor = DirectCast(leftNode, ForOrForEachBlockSyntax)
Dim rightFor = DirectCast(rightNode, ForOrForEachBlockSyntax)
Dim leftStatement = DirectCast(leftFor.ForOrForEachStatement, ForEachStatementSyntax)
Dim rightStatement = DirectCast(rightFor.ForOrForEachStatement, ForEachStatementSyntax)
distance = ComputeWeightedDistance(leftStatement.ControlVariable,
leftFor.ForOrForEachStatement,
leftFor.Statements,
rightStatement.ControlVariable,
rightFor.ForOrForEachStatement,
rightFor.Statements)
Return True
Case SyntaxKind.UsingBlock
Dim leftUsing = DirectCast(leftNode, UsingBlockSyntax)
Dim rightUsing = DirectCast(rightNode, UsingBlockSyntax)
distance = ComputeWeightedDistance(leftUsing.UsingStatement.Expression, leftUsing.Statements, rightUsing.UsingStatement.Expression, rightUsing.Statements)
Return True
Case SyntaxKind.WithBlock
Dim leftWith = DirectCast(leftNode, WithBlockSyntax)
Dim rightWith = DirectCast(rightNode, WithBlockSyntax)
distance = ComputeWeightedDistance(leftWith.WithStatement.Expression, leftWith.Statements, rightWith.WithStatement.Expression, rightWith.Statements)
Return True
Case SyntaxKind.SyncLockBlock
Dim leftLock = DirectCast(leftNode, SyncLockBlockSyntax)
Dim rightLock = DirectCast(rightNode, SyncLockBlockSyntax)
distance = ComputeWeightedDistance(leftLock.SyncLockStatement.Expression, leftLock.Statements, rightLock.SyncLockStatement.Expression, rightLock.Statements)
Return True
Case SyntaxKind.VariableDeclarator
' For top level syntax a variable declarator is seen for field declarations as we need to treat
' them differently to local declarations, which is what is seen in statement syntax
If Not _compareStatementSyntax Then
Dim leftIdentifiers = DirectCast(leftNode, VariableDeclaratorSyntax).Names.Select(Function(n) n.Identifier)
Dim rightIdentifiers = DirectCast(rightNode, VariableDeclaratorSyntax).Names.Select(Function(n) n.Identifier)
distance = ComputeDistance(leftIdentifiers, rightIdentifiers)
Return True
End If
distance = ComputeDistance(DirectCast(leftNode, VariableDeclaratorSyntax).Names, DirectCast(rightNode, VariableDeclaratorSyntax).Names)
Return True
Case SyntaxKind.MultiLineIfBlock,
SyntaxKind.SingleLineIfStatement
Dim ifParts = Function(node As SyntaxNode)
If node.IsKind(SyntaxKind.MultiLineIfBlock) Then
Dim part = DirectCast(node, MultiLineIfBlockSyntax)
Return New With {part.IfStatement.Condition, part.Statements}
Else
Dim part = DirectCast(node, SingleLineIfStatementSyntax)
Return New With {part.Condition, part.Statements}
End If
End Function
Dim leftIf = ifParts(leftNode)
Dim rightIf = ifParts(rightNode)
distance = ComputeWeightedDistance(leftIf.Condition, leftIf.Statements, rightIf.Condition, rightIf.Statements)
Return True
Case SyntaxKind.ElseIfBlock
Dim leftElseIf = DirectCast(leftNode, ElseIfBlockSyntax)
Dim rightElseIf = DirectCast(rightNode, ElseIfBlockSyntax)
distance = ComputeWeightedDistance(leftElseIf.ElseIfStatement.Condition, leftElseIf.Statements, rightElseIf.ElseIfStatement.Condition, rightElseIf.Statements)
Return True
Case SyntaxKind.ElseBlock,
SyntaxKind.SingleLineElseClause
Dim elseStatements = Function(node As SyntaxNode)
If node.IsKind(SyntaxKind.ElseBlock) Then
Return DirectCast(node, ElseBlockSyntax).Statements
Else
Return DirectCast(node, SingleLineElseClauseSyntax).Statements
End If
End Function
Dim leftStatements = elseStatements(leftNode)
Dim rightStatements = elseStatements(rightNode)
distance = ComputeWeightedDistance(Nothing, leftStatements, Nothing, rightStatements)
Return True
Case SyntaxKind.CatchBlock
Dim leftCatch = DirectCast(leftNode, CatchBlockSyntax)
Dim rightCatch = DirectCast(rightNode, CatchBlockSyntax)
distance = ComputeWeightedDistance(leftCatch.CatchStatement, leftCatch.Statements, rightCatch.CatchStatement, rightCatch.Statements)
Return True
Case SyntaxKind.SingleLineSubLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression
Dim getParts = Function(node As SyntaxNode)
Select Case node.Kind
Case SyntaxKind.SingleLineSubLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression
Dim lambda = DirectCast(node, SingleLineLambdaExpressionSyntax)
Return New With {lambda.SubOrFunctionHeader, .Body = lambda.Body.DescendantTokens()}
Case Else
Dim lambda = DirectCast(node, MultiLineLambdaExpressionSyntax)
Return New With {lambda.SubOrFunctionHeader, .Body = GetDescendantTokensIgnoringSeparators(lambda.Statements)}
End Select
End Function
Dim leftParts = getParts(leftNode)
Dim rightParts = getParts(rightNode)
distance = ComputeWeightedDistanceOfLambdas(leftParts.SubOrFunctionHeader, rightParts.SubOrFunctionHeader, leftParts.Body, rightParts.Body)
Return True
Case SyntaxKind.VariableDeclarator
Dim leftIdentifiers = DirectCast(leftNode, VariableDeclaratorSyntax).Names.Select(Function(n) n.Identifier)
Dim rightIdentifiers = DirectCast(rightNode, VariableDeclaratorSyntax).Names.Select(Function(n) n.Identifier)
distance = ComputeDistance(leftIdentifiers, rightIdentifiers)
Return True
Case Else
Dim leftName As SyntaxNodeOrToken? = TryGetName(leftNode)
Dim rightName As SyntaxNodeOrToken? = TryGetName(rightNode)
If leftName.HasValue AndAlso rightName.HasValue Then
distance = ComputeDistance(leftName.Value, rightName.Value)
Return True
End If
distance = 0
Return False
End Select
End Function
Private Shared Function ComputeWeightedDistanceOfLambdas(leftHeader As LambdaHeaderSyntax,
rightHeader As LambdaHeaderSyntax,
leftBody As IEnumerable(Of SyntaxToken),
rightBody As IEnumerable(Of SyntaxToken)) As Double
If leftHeader.Modifiers.Any(SyntaxKind.AsyncKeyword) <> rightHeader.Modifiers.Any(SyntaxKind.AsyncKeyword) Then
Return 1.0
End If
Dim parameterDistance = ComputeDistance(leftHeader.ParameterList, rightHeader.ParameterList)
If leftHeader.AsClause IsNot Nothing OrElse rightHeader.AsClause IsNot Nothing Then
Dim asClauseDistance As Double = ComputeDistance(leftHeader.AsClause, rightHeader.AsClause)
parameterDistance = parameterDistance * 0.8 + asClauseDistance * 0.2
End If
Dim bodyDistance = ComputeDistance(leftBody, rightBody)
Return parameterDistance * 0.6 + bodyDistance * 0.4
End Function
Private Shared Function ComputeWeightedDistance(leftHeader As SyntaxNode,
leftStatements As SyntaxList(Of StatementSyntax),
rightHeader As SyntaxNode,
rightStatements As SyntaxList(Of StatementSyntax)) As Double
Return ComputeWeightedDistance(Nothing, leftHeader, leftStatements, Nothing, rightHeader, rightStatements)
End Function
Private Shared Function ComputeWeightedDistance(leftControlVariable As SyntaxNode,
leftHeader As SyntaxNode,
leftStatements As SyntaxList(Of StatementSyntax),
rightControlVariable As SyntaxNode,
rightHeader As SyntaxNode,
rightStatements As SyntaxList(Of StatementSyntax)) As Double
Debug.Assert((leftControlVariable Is Nothing) = (rightControlVariable Is Nothing))
Dim headerDistance As Double = ComputeDistance(leftHeader, rightHeader)
If leftControlVariable IsNot Nothing Then
Dim controlVariableDistance = ComputeDistance(leftControlVariable, rightControlVariable)
headerDistance = controlVariableDistance * 0.9 + headerDistance * 0.1
End If
Dim statementDistance As Double = ComputeDistance(leftStatements, rightStatements)
Dim distance As Double = headerDistance * 0.6 + statementDistance * 0.4
Dim localsDistance As Double
If TryComputeLocalsDistance(leftStatements, rightStatements, localsDistance) Then
distance = localsDistance * 0.5 + distance * 0.5
End If
Return distance
End Function
Private Shared Function TryComputeLocalsDistance(left As SyntaxList(Of StatementSyntax),
right As SyntaxList(Of StatementSyntax),
ByRef distance As Double) As Boolean
Dim leftLocals As List(Of SyntaxToken) = Nothing
Dim rightLocals As List(Of SyntaxToken) = Nothing
GetLocalNames(left, leftLocals)
GetLocalNames(right, rightLocals)
If leftLocals Is Nothing OrElse rightLocals Is Nothing Then
distance = 0
Return False
End If
distance = ComputeDistance(leftLocals, rightLocals)
Return True
End Function
Private Shared Sub GetLocalNames(statements As SyntaxList(Of StatementSyntax), ByRef result As List(Of SyntaxToken))
For Each s In statements
If s.IsKind(SyntaxKind.LocalDeclarationStatement) Then
For Each declarator In DirectCast(s, LocalDeclarationStatementSyntax).Declarators
GetLocalNames(declarator, result)
Next
End If
Next
End Sub
Private Shared Sub GetLocalNames(localDecl As VariableDeclaratorSyntax, ByRef result As List(Of SyntaxToken))
For Each local In localDecl.Names
If result Is Nothing Then
result = New List(Of SyntaxToken)()
End If
result.Add(local.Identifier)
Next
End Sub
Private Shared Function TryGetName(node As SyntaxNode) As SyntaxNodeOrToken?
Select Case node.Kind()
Case SyntaxKind.OptionStatement
Return DirectCast(node, OptionStatementSyntax).OptionKeyword
Case SyntaxKind.NamespaceBlock
Return DirectCast(node, NamespaceBlockSyntax).NamespaceStatement.Name
Case SyntaxKind.ClassBlock,
SyntaxKind.StructureBlock,
SyntaxKind.InterfaceBlock,
SyntaxKind.ModuleBlock
Return DirectCast(node, TypeBlockSyntax).BlockStatement.Identifier
Case SyntaxKind.EnumBlock
Return DirectCast(node, EnumBlockSyntax).EnumStatement.Identifier
Case SyntaxKind.DelegateFunctionStatement,
SyntaxKind.DelegateSubStatement
Return DirectCast(node, DelegateStatementSyntax).Identifier
Case SyntaxKind.ModifiedIdentifier
Return DirectCast(node, ModifiedIdentifierSyntax).Identifier
Case SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock
Return DirectCast(node, MethodBlockSyntax).SubOrFunctionStatement.Identifier
Case SyntaxKind.SubStatement, ' interface methods
SyntaxKind.FunctionStatement
Return DirectCast(node, MethodStatementSyntax).Identifier
Case SyntaxKind.DeclareSubStatement,
SyntaxKind.DeclareFunctionStatement
Return DirectCast(node, DeclareStatementSyntax).Identifier
Case SyntaxKind.ConstructorBlock
Return DirectCast(node, ConstructorBlockSyntax).SubNewStatement.NewKeyword
Case SyntaxKind.OperatorBlock
Return DirectCast(node, OperatorBlockSyntax).OperatorStatement.OperatorToken
Case SyntaxKind.PropertyBlock
Return DirectCast(node, PropertyBlockSyntax).PropertyStatement.Identifier
Case SyntaxKind.PropertyStatement ' interface properties
Return DirectCast(node, PropertyStatementSyntax).Identifier
Case SyntaxKind.EventBlock
Return DirectCast(node, EventBlockSyntax).EventStatement.Identifier
Case SyntaxKind.EnumMemberDeclaration
Return DirectCast(node, EnumMemberDeclarationSyntax).Identifier
Case SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock
Return DirectCast(node, AccessorBlockSyntax).BlockStatement.DeclarationKeyword
Case SyntaxKind.EventStatement
Return DirectCast(node, EventStatementSyntax).Identifier
Case SyntaxKind.TypeParameter
Return DirectCast(node, TypeParameterSyntax).Identifier
Case SyntaxKind.StructureConstraint,
SyntaxKind.ClassConstraint,
SyntaxKind.NewConstraint
Return DirectCast(node, SpecialConstraintSyntax).ConstraintKeyword
Case SyntaxKind.Parameter
Return DirectCast(node, ParameterSyntax).Identifier.Identifier
Case SyntaxKind.Attribute
Return DirectCast(node, AttributeSyntax).Name
Case Else
Return Nothing
End Select
End Function
Public Overrides Function GetDistance(oldNode As SyntaxNode, newNode As SyntaxNode) As Double
Debug.Assert(GetLabel(oldNode) = GetLabel(newNode) AndAlso GetLabel(oldNode) <> IgnoredNode)
If oldNode Is newNode Then
Return ExactMatchDist
End If
Dim weightedDistance As Double
If TryComputeWeightedDistance(oldNode, newNode, weightedDistance) Then
If weightedDistance = ExactMatchDist AndAlso Not SyntaxFactory.AreEquivalent(oldNode, newNode) Then
weightedDistance = EpsilonDist
End If
Return weightedDistance
End If
Return ComputeValueDistance(oldNode, newNode)
End Function
Friend Shared Function ComputeValueDistance(leftNode As SyntaxNode, rightNode As SyntaxNode) As Double
If SyntaxFactory.AreEquivalent(leftNode, rightNode) Then
Return ExactMatchDist
End If
Dim distance As Double = ComputeDistance(leftNode, rightNode)
Return If(distance = ExactMatchDist, EpsilonDist, distance)
End Function
Friend Overloads Shared Function ComputeDistance(oldNodeOrToken As SyntaxNodeOrToken, newNodeOrToken As SyntaxNodeOrToken) As Double
Debug.Assert(newNodeOrToken.IsToken = oldNodeOrToken.IsToken)
Dim distance As Double
If oldNodeOrToken.IsToken Then
Dim leftToken = oldNodeOrToken.AsToken()
Dim rightToken = newNodeOrToken.AsToken()
distance = ComputeDistance(leftToken, rightToken)
Debug.Assert(Not SyntaxFactory.AreEquivalent(leftToken, rightToken) OrElse distance = ExactMatchDist)
Else
Dim leftNode = oldNodeOrToken.AsNode()
Dim rightNode = newNodeOrToken.AsNode()
distance = ComputeDistance(leftNode, rightNode)
Debug.Assert(Not SyntaxFactory.AreEquivalent(leftNode, rightNode) OrElse distance = ExactMatchDist)
End If
Return distance
End Function
Friend Overloads Shared Function ComputeDistance(Of TSyntaxNode As SyntaxNode)(oldList As SyntaxList(Of TSyntaxNode), newList As SyntaxList(Of TSyntaxNode)) As Double
Return ComputeDistance(GetDescendantTokensIgnoringSeparators(oldList), GetDescendantTokensIgnoringSeparators(newList))
End Function
Friend Overloads Shared Function ComputeDistance(Of TSyntaxNode As SyntaxNode)(oldList As SeparatedSyntaxList(Of TSyntaxNode), newList As SeparatedSyntaxList(Of TSyntaxNode)) As Double
Return ComputeDistance(GetDescendantTokensIgnoringSeparators(oldList), GetDescendantTokensIgnoringSeparators(newList))
End Function
''' <summary>
''' Enumerates tokens of all nodes in the list.
''' </summary>
Friend Shared Iterator Function GetDescendantTokensIgnoringSeparators(Of TSyntaxNode As SyntaxNode)(list As SyntaxList(Of TSyntaxNode)) As IEnumerable(Of SyntaxToken)
For Each node In list
For Each token In node.DescendantTokens()
Yield token
Next
Next
End Function
''' <summary>
''' Enumerates tokens of all nodes in the list. Doesn't include separators.
''' </summary>
Private Shared Iterator Function GetDescendantTokensIgnoringSeparators(Of TSyntaxNode As SyntaxNode)(list As SeparatedSyntaxList(Of TSyntaxNode)) As IEnumerable(Of SyntaxToken)
For Each node In list
For Each token In node.DescendantTokens()
Yield token
Next
Next
End Function
''' <summary>
''' Calculates the distance between two syntax nodes, disregarding trivia.
''' </summary>
''' <remarks>
''' Distance is a number within [0, 1], the smaller the more similar the nodes are.
''' </remarks>
Public Overloads Shared Function ComputeDistance(oldNode As SyntaxNode, newNode As SyntaxNode) As Double
If oldNode Is Nothing OrElse newNode Is Nothing Then
Return If(oldNode Is newNode, 0.0, 1.0)
End If
Return ComputeDistance(oldNode.DescendantTokens(), newNode.DescendantTokens())
End Function
''' <summary>
''' Calculates the distance between two syntax tokens, disregarding trivia.
''' </summary>
''' <remarks>
''' Distance is a number within [0, 1], the smaller the more similar the tokens are.
''' </remarks>
Public Overloads Shared Function ComputeDistance(oldToken As SyntaxToken, newToken As SyntaxToken) As Double
Return LongestCommonSubstring.ComputePrefixDistance(
oldToken.Text, Math.Min(oldToken.Text.Length, LongestCommonSubsequence.MaxSequenceLengthForDistanceCalculation),
newToken.Text, Math.Min(newToken.Text.Length, LongestCommonSubsequence.MaxSequenceLengthForDistanceCalculation))
End Function
Private Shared Function CreateArrayForDistanceCalculation(Of T)(enumerable As IEnumerable(Of T)) As ImmutableArray(Of T)
Return If(enumerable Is Nothing, ImmutableArray(Of T).Empty, enumerable.Take(LongestCommonSubsequence.MaxSequenceLengthForDistanceCalculation).ToImmutableArray())
End Function
''' <summary>
''' Calculates the distance between two sequences of syntax tokens, disregarding trivia.
''' </summary>
''' <remarks>
''' Distance is a number within [0, 1], the smaller the more similar the sequences are.
''' </remarks>
Public Overloads Shared Function ComputeDistance(oldTokens As IEnumerable(Of SyntaxToken), newTokens As IEnumerable(Of SyntaxToken)) As Double
Return LcsTokens.Instance.ComputeDistance(CreateArrayForDistanceCalculation(oldTokens), CreateArrayForDistanceCalculation(newTokens))
End Function
''' <summary>
''' Calculates the distance between two sequences of syntax nodes, disregarding trivia.
''' </summary>
''' <remarks>
''' Distance is a number within [0, 1], the smaller the more similar the sequences are.
''' </remarks>
Public Overloads Shared Function ComputeDistance(oldTokens As IEnumerable(Of SyntaxNode), newTokens As IEnumerable(Of SyntaxNode)) As Double
Return LcsNodes.Instance.ComputeDistance(CreateArrayForDistanceCalculation(oldTokens), CreateArrayForDistanceCalculation(newTokens))
End Function
''' <summary>
''' Calculates the edits that transform one sequence of syntax nodes to another, disregarding trivia.
''' </summary>
Public Shared Function GetSequenceEdits(oldNodes As IEnumerable(Of SyntaxNode), newNodes As IEnumerable(Of SyntaxNode)) As IEnumerable(Of SequenceEdit)
Return LcsNodes.Instance.GetEdits(oldNodes.AsImmutableOrEmpty(), newNodes.AsImmutableOrEmpty())
End Function
''' <summary>
''' Calculates the edits that transform one sequence of syntax nodes to another, disregarding trivia.
''' </summary>
Public Shared Function GetSequenceEdits(oldNodes As ImmutableArray(Of SyntaxNode), newNodes As ImmutableArray(Of SyntaxNode)) As IEnumerable(Of SequenceEdit)
Return LcsNodes.Instance.GetEdits(oldNodes.NullToEmpty(), newNodes.NullToEmpty())
End Function
''' <summary>
''' Calculates the edits that transform one sequence of syntax tokens to another, disregarding trivia.
''' </summary>
Public Shared Function GetSequenceEdits(oldTokens As IEnumerable(Of SyntaxToken), newTokens As IEnumerable(Of SyntaxToken)) As IEnumerable(Of SequenceEdit)
Return LcsTokens.Instance.GetEdits(oldTokens.AsImmutableOrEmpty(), newTokens.AsImmutableOrEmpty())
End Function
''' <summary>
''' Calculates the edits that transform one sequence of syntax tokens to another, disregarding trivia.
''' </summary>
Public Shared Function GetSequenceEdits(oldTokens As ImmutableArray(Of SyntaxToken), newTokens As ImmutableArray(Of SyntaxToken)) As IEnumerable(Of SequenceEdit)
Return LcsTokens.Instance.GetEdits(oldTokens.NullToEmpty(), newTokens.NullToEmpty())
End Function
Private NotInheritable Class LcsTokens
Inherits LongestCommonImmutableArraySubsequence(Of SyntaxToken)
Friend Shared ReadOnly Instance As LcsTokens = New LcsTokens()
Protected Overrides Function Equals(oldElement As SyntaxToken, newElement As SyntaxToken) As Boolean
Return SyntaxFactory.AreEquivalent(oldElement, newElement)
End Function
End Class
Private NotInheritable Class LcsNodes
Inherits LongestCommonImmutableArraySubsequence(Of SyntaxNode)
Friend Shared ReadOnly Instance As LcsNodes = New LcsNodes()
Protected Overrides Function Equals(oldElement As SyntaxNode, newElement As SyntaxNode) As Boolean
Return SyntaxFactory.AreEquivalent(oldElement, newElement)
End Function
End Class
#End Region
End Class
End Namespace
|