File: EditAndContinue\SyntaxComparer.vb
Web Access
Project: src\src\roslyn\src\Features\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Features.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features)
' 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