File: Syntax\InternalSyntax\SyntaxNodeExtensions.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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.
 
'-----------------------------------------------------------------------------------------------------------
'
'  Extension methods to st leading/trailing trivia in syntax nodes.
'-----------------------------------------------------------------------------------------------------------
 
Imports System.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Syntax.InternalSyntax
Imports CoreInternalSyntax = Microsoft.CodeAnalysis.Syntax.InternalSyntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
 
    Friend Module SyntaxExtensions
 
        <Extension()>
        Public Function WithAnnotations(Of TNode As VisualBasicSyntaxNode)(node As TNode, ParamArray annotations() As SyntaxAnnotation) As TNode
            If annotations Is Nothing Then Throw New ArgumentNullException(NameOf(annotations))
            Return CType(node.SetAnnotations(annotations), TNode)
        End Function
 
        <Extension()>
        Public Function WithAdditionalAnnotations(Of TNode As VisualBasicSyntaxNode)(node As TNode, ParamArray annotations() As SyntaxAnnotation) As TNode
            If annotations Is Nothing Then Throw New ArgumentNullException(NameOf(annotations))
            Return CType(node.SetAnnotations(node.GetAnnotations().Concat(annotations).ToArray()), TNode)
        End Function
 
        <Extension()>
        Public Function WithoutAnnotations(Of TNode As VisualBasicSyntaxNode)(node As TNode, ParamArray removalAnnotations() As SyntaxAnnotation) As TNode
            Dim newAnnotations = ArrayBuilder(Of SyntaxAnnotation).GetInstance()
            Dim annotations = node.GetAnnotations()
            For Each candidate In annotations
                If Array.IndexOf(removalAnnotations, candidate) < 0 Then
                    newAnnotations.Add(candidate)
                End If
            Next
            Return CType(node.SetAnnotations(newAnnotations.ToArrayAndFree()), TNode)
        End Function
 
        <Extension()>
        Public Function WithAdditionalDiagnostics(Of TNode As GreenNode)(node As TNode, ParamArray diagnostics As DiagnosticInfo()) As TNode
            Dim current As DiagnosticInfo() = node.GetDiagnostics
            If current IsNot Nothing Then
                Return DirectCast(node.SetDiagnostics(current.Concat(diagnostics).ToArray()), TNode)
            Else
                Return node.WithDiagnostics(diagnostics)
            End If
        End Function
 
        <Extension()>
        Public Function WithDiagnostics(Of TNode As GreenNode)(node As TNode, ParamArray diagnostics As DiagnosticInfo()) As TNode
            Return DirectCast(node.SetDiagnostics(diagnostics), TNode)
        End Function
 
        <Extension()>
        Public Function WithoutDiagnostics(Of TNode As VisualBasicSyntaxNode)(node As TNode) As TNode
            Dim current As DiagnosticInfo() = node.GetDiagnostics
            If ((current Is Nothing) OrElse (current.Length = 0)) Then
                Return node
            End If
            Return DirectCast(node.SetDiagnostics(Nothing), TNode)
        End Function
 
        <Extension()>
        Public Function LastTriviaIfAny(node As VisualBasicSyntaxNode) As VisualBasicSyntaxNode
            Dim trailingTriviaNode = node.GetTrailingTrivia()
            If trailingTriviaNode Is Nothing Then
                Return Nothing
            End If
            Return New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(trailingTriviaNode).Last
        End Function
 
        <Extension()>
        Public Function EndsWithEndOfLineOrColonTrivia(node As VisualBasicSyntaxNode) As Boolean
            Dim trailingTrivia = node.LastTriviaIfAny()
            Return trailingTrivia IsNot Nothing AndAlso
                (trailingTrivia.Kind = SyntaxKind.EndOfLineTrivia OrElse trailingTrivia.Kind = SyntaxKind.ColonTrivia)
        End Function
    End Module
 
    Friend Module SyntaxNodeExtensions
 
        Private Function IsMissingToken(token As SyntaxToken) As Boolean
            Return token.Width = 0 AndAlso token.Kind <> SyntaxKind.EmptyToken
        End Function
 
#Region "AddLeading"
        ' Add "trivia" as a leading trivia of node. If node is not a token, traverses down to the tree to add it to the first token.
        <Extension()>
        Private Function AddLeadingTrivia(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)) As TSyntax
            If node Is Nothing Then
                Throw New ArgumentNullException(NameOf(node))
            End If
 
            If Not trivia.Any Then
                Return node
            End If
 
            Dim tk = TryCast(node, SyntaxToken)
            Dim result As TSyntax
            If tk IsNot Nothing Then
                ' Cannot add unexpected tokens as leading trivia on a missing token since
                ' if the unexpected tokens end with a statement terminator, the missing
                ' token would follow the statement terminator. That would result in an
                ' incorrect syntax tree and if this missing token is the end of an expression,
                ' and the expression represents a transition between VB and XML, the
                ' terminator will be overlooked (see ParseXmlEmbedded for instance).
                If IsMissingToken(tk) Then
                    Dim leadingTrivia = trivia.GetStartOfTrivia()
                    Dim trailingTrivia = trivia.GetEndOfTrivia()
                    tk = SyntaxToken.AddLeadingTrivia(tk, leadingTrivia).AddTrailingTrivia(trailingTrivia)
                Else
                    tk = SyntaxToken.AddLeadingTrivia(tk, trivia)
                End If
 
                result = DirectCast(CObj(tk), TSyntax)
            Else
                result = FirstTokenReplacer.Replace(node, Function(t) SyntaxToken.AddLeadingTrivia(t, trivia))
            End If
 
            'Debug.Assert(result.hasDiagnostics)
 
            Return result
        End Function
 
        ' Add "unexpected" as skipped leading trivia to "node". Leaves any diagnostics in place, and also adds a diagnostic with code "errorId"
        ' to the first token in the list.
        <Extension()>
        Friend Function AddLeadingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As CoreInternalSyntax.SyntaxList(Of GreenNode), errorId As ERRID) As TSyntax
            Dim diagnostic = ErrorFactory.ErrorInfo(errorId)
            If unexpected.Node IsNot Nothing Then
                Dim trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode) = CreateSkippedTrivia(unexpected.Node,
                                                                              preserveDiagnostics:=True,
                                                                              addDiagnosticToFirstTokenOnly:=True,
                                                                              addDiagnostic:=diagnostic)
                Return AddLeadingTrivia(node, trivia)
            Else
                Return DirectCast(node.AddError(diagnostic), TSyntax)
            End If
            Return node
        End Function
 
        ' Add "unexpected" as skipped leading trivia of "node". Removes all diagnostics from "unexpected", replacing them with 
        ' a new diagnostic with the given "errorId".
        <Extension()>
        Friend Function AddLeadingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As SyntaxToken, errorId As ERRID) As TSyntax
            Return node.AddLeadingSyntax(DirectCast(unexpected, VisualBasicSyntaxNode), errorId)
        End Function
 
        ' Add "unexpected" as skipped leading trivia of "node". Leaves any diagnostics in place (possibly reattaching them to the created trivia node).
        <Extension()>
        Friend Function AddLeadingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As CoreInternalSyntax.SyntaxList(Of SyntaxToken)) As TSyntax
            Return node.AddLeadingSyntax(unexpected.Node)
        End Function
 
        ' Add "unexpected" as skipped leading trivia of "node". Removes all diagnostics from "unexpected", replacing them with 
        ' a new diagnostic with the given "errorId".
        <Extension()>
        Friend Function AddLeadingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As GreenNode, errorId As ERRID) As TSyntax
            Dim diagnostic = ErrorFactory.ErrorInfo(errorId)
            If unexpected IsNot Nothing Then
                Dim trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode) = CreateSkippedTrivia(unexpected,
                                                                              preserveDiagnostics:=False,
                                                                              addDiagnosticToFirstTokenOnly:=False,
                                                                              addDiagnostic:=diagnostic)
                Return AddLeadingTrivia(node, trivia)
            Else
                Return DirectCast(node.AddError(diagnostic), TSyntax)
            End If
            Return node
        End Function
 
        ' Add "unexpected" as skipped leading trivia of "node". Leaves any diagnostics in place (possibly reattaching them to the created trivia node).
        <Extension()>
        Friend Function AddLeadingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As GreenNode) As TSyntax
            If unexpected IsNot Nothing Then
                Dim trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode) = CreateSkippedTrivia(unexpected,
                                                                              preserveDiagnostics:=True,
                                                                              addDiagnosticToFirstTokenOnly:=False,
                                                                              addDiagnostic:=Nothing)
                Return AddLeadingTrivia(node, trivia)
            End If
            Return node
        End Function
 
#End Region
 
#Region "AddTrailing"
        ' Add "trivia" as a trailing trivia of node. If node is not a token, traverses down to the tree to add it to the last token.
        <Extension()>
        Friend Function AddTrailingTrivia(Of TSyntax As GreenNode)(node As TSyntax, trivia As CoreInternalSyntax.SyntaxList(Of GreenNode)) As TSyntax
            If node Is Nothing Then
                Throw New ArgumentNullException(NameOf(node))
            End If
 
            Dim tk = TryCast(node, SyntaxToken)
            Dim result As TSyntax
            If tk IsNot Nothing Then
                result = DirectCast(CObj(SyntaxToken.AddTrailingTrivia(tk, trivia)), TSyntax)
            Else
                result = LastTokenReplacer.Replace(node, Function(t) SyntaxToken.AddTrailingTrivia(t, trivia))
            End If
 
            'Debug.Assert(result.ContainsDiagnostics)
            Return result
        End Function
 
        ' Add "unexpected" as skipped trailing trivia to "node". Leaves any diagnostics in place, and also adds a diagnostic with code "errorId"
        ' to the first token in the list.
        <Extension()>
        Friend Function AddTrailingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As CoreInternalSyntax.SyntaxList(Of SyntaxToken), errorId As ERRID) As TSyntax
            Dim diagnostic = ErrorFactory.ErrorInfo(errorId)
            If unexpected.Node IsNot Nothing Then
                Dim trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode) = CreateSkippedTrivia(unexpected.Node,
                                                                                  preserveDiagnostics:=True,
                                                                                  addDiagnosticToFirstTokenOnly:=True,
                                                                                  addDiagnostic:=diagnostic)
                Return AddTrailingTrivia(node, trivia)
            Else
                Return DirectCast(node.AddError(diagnostic), TSyntax)
            End If
            Return node
        End Function
 
        ' Add "unexpected" as skipped trailing trivia of "node". Removes all diagnostics from "unexpected", replacing them with 
        ' a new diagnostic with the given "errorId".
        <Extension()>
        Friend Function AddTrailingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As SyntaxToken, errorId As ERRID) As TSyntax
            Return node.AddTrailingSyntax(DirectCast(unexpected, VisualBasicSyntaxNode), errorId)
        End Function
 
        ' Add "unexpected" as skipped trailing trivia of "node". Leaves any diagnostics in place (possibly reattaching them to the created trivia node).
        <Extension()>
        Friend Function AddTrailingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As CoreInternalSyntax.SyntaxList(Of SyntaxToken)) As TSyntax
            Return node.AddTrailingSyntax(unexpected.Node)
        End Function
 
        ' Add "unexpected" as skipped trailing trivia of "node". Leaves any diagnostics in place (possibly reattaching them to the created trivia node).
        <Extension()>
        Friend Function AddTrailingSyntax(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, unexpected As SyntaxToken) As TSyntax
            Return node.AddTrailingSyntax(DirectCast(unexpected, VisualBasicSyntaxNode))
        End Function
 
        ' Add "unexpected" as skipped trailing trivia of "node". Removes all diagnostics from "unexpected", replacing them with 
        ' a new diagnostic with the given "errorId".
        <Extension()>
        Friend Function AddTrailingSyntax(Of TSyntax As GreenNode)(node As TSyntax, unexpected As GreenNode, errorId As ERRID) As TSyntax
            Dim diagnostic = ErrorFactory.ErrorInfo(errorId)
            If unexpected IsNot Nothing Then
                Dim trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode) = CreateSkippedTrivia(unexpected,
                                                               preserveDiagnostics:=False,
                                                               addDiagnosticToFirstTokenOnly:=False,
                                                               addDiagnostic:=diagnostic)
                Return AddTrailingTrivia(node, trivia)
            Else
                Return DirectCast(node.AddError(diagnostic), TSyntax)
            End If
            Return node
        End Function
 
        ' Add "unexpected" as skipped trailing trivia of "node". Leaves any diagnostics in place (possibly reattaching them to the created trivia node).
        <Extension()>
        Friend Function AddTrailingSyntax(Of TSyntax As GreenNode)(node As TSyntax, unexpected As GreenNode) As TSyntax
            If unexpected IsNot Nothing Then
                Dim trivia As CoreInternalSyntax.SyntaxList(Of GreenNode) = CreateSkippedTrivia(unexpected, preserveDiagnostics:=True, addDiagnosticToFirstTokenOnly:=False, addDiagnostic:=Nothing)
                Return AddTrailingTrivia(node, trivia)
            End If
            Return node
        End Function
 
        <Extension()>
        Friend Function AddError(Of TSyntax As VisualBasicSyntaxNode)(node As TSyntax, errorId As ERRID) As TSyntax
            Return DirectCast(node.AddError(ErrorFactory.ErrorInfo(errorId)), TSyntax)
        End Function
 
#End Region
 
        <Extension()>
        Friend Function GetStartOfTrivia(trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)) As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)
            Return trivia.GetStartOfTrivia(trivia.GetIndexOfEndOfTrivia())
        End Function
 
        <Extension()>
        Friend Function GetStartOfTrivia(trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode), indexOfEnd As Integer) As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)
            If indexOfEnd = 0 Then
                Return Nothing
            ElseIf indexOfEnd = trivia.Count Then
                Return trivia
            Else
                Dim builder = SyntaxListBuilder(Of VisualBasicSyntaxNode).Create()
                For i = 0 To indexOfEnd - 1
                    builder.Add(trivia(i))
                Next
                Return builder.ToList()
            End If
        End Function
 
        <Extension()>
        Friend Function GetEndOfTrivia(trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)) As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)
            Return trivia.GetEndOfTrivia(trivia.GetIndexOfEndOfTrivia())
        End Function
 
        <Extension()>
        Friend Function GetEndOfTrivia(trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode), indexOfEnd As Integer) As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)
            If indexOfEnd = 0 Then
                Return trivia
            ElseIf indexOfEnd = trivia.Count Then
                Return Nothing
            Else
                Dim builder = SyntaxListBuilder(Of VisualBasicSyntaxNode).Create()
                For i = indexOfEnd To trivia.Count - 1
                    builder.Add(trivia(i))
                Next
                Return builder.ToList()
            End If
        End Function
 
        ''' <summary>
        ''' Return the length of the common ending between the two
        ''' sets of trivia. The valid trivia (following skipped tokens)
        ''' of one must be contained in the valid trivia of the other. 
        ''' </summary>
        Friend Function GetLengthOfCommonEnd(trivia1 As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode), trivia2 As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)) As Integer
            Dim n1 = trivia1.Count
            Dim n2 = trivia2.Count
            Dim offset1 = trivia1.GetIndexAfterLastSkippedToken()
            Dim offset2 = trivia2.GetIndexAfterLastSkippedToken()
            Dim n = Math.Min(n1 - offset1, n2 - offset2)
#If DEBUG Then
            For i = 0 To n - 1
                Dim t1 = trivia1(i + n1 - n)
                Dim t2 = trivia2(i + n2 - n)
                Debug.Assert(t1.Kind = t2.Kind)
                Debug.Assert(t1.ToFullString() = t2.ToFullString())
            Next
#End If
            Return n
        End Function
 
        <Extension()>
        Private Function GetIndexAfterLastSkippedToken(trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)) As Integer
            Dim n = trivia.Count
            For i = n - 1 To 0 Step -1
                If trivia(i).Kind = SyntaxKind.SkippedTokensTrivia Then
                    Return i + 1
                End If
            Next
            Return 0
        End Function
 
        ''' <summary>
        ''' Return the index within the trivia of what would be considered trailing
        ''' single-line trivia by the Scanner. This behavior must match ScanSingleLineTrivia.
        ''' In short, search walks backwards and stops at the second terminator
        ''' (colon or EOL) from the end, ignoring EOLs preceded by line continuations.
        ''' </summary>
        <Extension()>
        Private Function GetIndexOfEndOfTrivia(trivia As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)) As Integer
            Dim n = trivia.Count
            If n > 0 Then
                Dim i = n - 1
                Select Case trivia(i).Kind
                    Case SyntaxKind.ColonTrivia
                        Return i
 
                    Case SyntaxKind.EndOfLineTrivia
                        If i > 0 Then
                            Select Case trivia(i - 1).Kind
                                Case SyntaxKind.LineContinuationTrivia
                                    ' An EOL preceded by a line continuation should
                                    ' be considered whitespace rather than EOL.
                                    Return n
                                Case SyntaxKind.CommentTrivia
                                    Return i - 1
                                Case Else
                                    Return i
                            End Select
                        Else
                            Return i
                        End If
 
                    Case SyntaxKind.LineContinuationTrivia,
                         SyntaxKind.IfDirectiveTrivia,
                        SyntaxKind.ElseIfDirectiveTrivia,
                        SyntaxKind.ElseDirectiveTrivia,
                        SyntaxKind.EndIfDirectiveTrivia,
                        SyntaxKind.RegionDirectiveTrivia,
                        SyntaxKind.EndRegionDirectiveTrivia,
                        SyntaxKind.ConstDirectiveTrivia,
                        SyntaxKind.ExternalSourceDirectiveTrivia,
                        SyntaxKind.EndExternalSourceDirectiveTrivia,
                        SyntaxKind.ExternalChecksumDirectiveTrivia,
                        SyntaxKind.EnableWarningDirectiveTrivia,
                        SyntaxKind.DisableWarningDirectiveTrivia,
                        SyntaxKind.ReferenceDirectiveTrivia,
                        SyntaxKind.BadDirectiveTrivia
 
                        Throw ExceptionUtilities.UnexpectedValue(trivia(i).Kind)
                End Select
            End If
            Return n
        End Function
 
#Region "Skipped trivia creation"
        ' In order to handle creating SkippedTokens trivia correctly, we need to know if any structured
        ' trivia is present in a trivia list (because structured trivia can't contain structured trivia). 
        Private Function TriviaListContainsStructuredTrivia(triviaList As GreenNode) As Boolean
            If triviaList Is Nothing Then
                Return False
            End If
 
            Dim trivia = New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(triviaList)
 
            For i = 0 To trivia.Count - 1
                Select Case trivia.ItemUntyped(i).RawKind
                    Case SyntaxKind.XmlDocument,
                        SyntaxKind.SkippedTokensTrivia,
                        SyntaxKind.IfDirectiveTrivia,
                        SyntaxKind.ElseIfDirectiveTrivia,
                        SyntaxKind.ElseDirectiveTrivia,
                        SyntaxKind.EndIfDirectiveTrivia,
                        SyntaxKind.RegionDirectiveTrivia,
                        SyntaxKind.EndRegionDirectiveTrivia,
                        SyntaxKind.ConstDirectiveTrivia,
                        SyntaxKind.ExternalSourceDirectiveTrivia,
                        SyntaxKind.EndExternalSourceDirectiveTrivia,
                        SyntaxKind.ExternalChecksumDirectiveTrivia,
                        SyntaxKind.ReferenceDirectiveTrivia,
                        SyntaxKind.EnableWarningDirectiveTrivia,
                        SyntaxKind.DisableWarningDirectiveTrivia,
                        SyntaxKind.BadDirectiveTrivia
                        Return True
                End Select
            Next
 
            Return False
        End Function
 
        ' Simple class to create the best representation of skipped trivia as a combination of "regular" trivia
        ' and SkippedNode trivia. The initial trivia and trailing trivia are preserved as regular trivia, as well
        ' as any structured trivia. We also remove any missing tokens and promote their trivia. Otherwise we try to put
        ' as many consecutive tokens as possible into a SkippedTokens trivia node.
        Private Class SkippedTriviaBuilder
            ' Maintain the list of trivia that we're accumulating.
#Disable Warning IDE0044 ' Add readonly modifier - Adding readonly generates compile error - see https://github.com/dotnet/roslyn/issues/47198
            Private _triviaListBuilder As SyntaxListBuilder(Of GreenNode) = SyntaxListBuilder(Of GreenNode).Create()
#Enable Warning IDE0044 ' Add readonly modifier
 
            ' Maintain a list of tokens we're accumulating to put into a SkippedNodes trivia.
            Private ReadOnly _skippedTokensBuilder As SyntaxListBuilder(Of SyntaxToken) = SyntaxListBuilder(Of SyntaxToken).Create()
 
            Private ReadOnly _preserveExistingDiagnostics As Boolean
            Private _addDiagnosticsToFirstTokenOnly As Boolean
            Private _diagnosticsToAdd As IEnumerable(Of DiagnosticInfo)
 
            ' Add a trivia to the trivia we are accumulating.
            Private Sub AddTrivia(trivia As GreenNode)
                FinishInProgressTokens()
                _triviaListBuilder.AddRange(trivia)
            End Sub
 
            ' Create a SkippedTokens trivia from any tokens currently accumulated into the skippedTokensBuilder. If not,
            ' don't do anything.
            Private Sub FinishInProgressTokens()
                If _skippedTokensBuilder.Count > 0 Then
                    Dim skippedTokensTrivia As GreenNode = SyntaxFactory.SkippedTokensTrivia(_skippedTokensBuilder.ToList())
 
                    If _diagnosticsToAdd IsNot Nothing Then
                        For Each d In _diagnosticsToAdd
                            skippedTokensTrivia = skippedTokensTrivia.AddError(d)
                        Next
                        _diagnosticsToAdd = Nothing ' only add once.
                    End If
 
                    _triviaListBuilder.Add(skippedTokensTrivia)
 
                    _skippedTokensBuilder.Clear()
                End If
            End Sub
 
            Public Sub New(preserveExistingDiagnostics As Boolean,
                           addDiagnosticsToFirstTokenOnly As Boolean,
                           diagnosticsToAdd As IEnumerable(Of DiagnosticInfo))
                Me._addDiagnosticsToFirstTokenOnly = addDiagnosticsToFirstTokenOnly
                Me._preserveExistingDiagnostics = preserveExistingDiagnostics
                Me._diagnosticsToAdd = diagnosticsToAdd
            End Sub
 
            ' Process a token. and add to the list of trivia/tokens we're accumulating.
            Public Sub AddToken(token As SyntaxToken, isFirst As Boolean, isLast As Boolean)
                Dim isMissing As Boolean = token.IsMissing
 
                If token.HasLeadingTrivia() AndAlso (isFirst OrElse isMissing OrElse TriviaListContainsStructuredTrivia(token.GetLeadingTrivia())) Then
                    FinishInProgressTokens()
                    AddTrivia(token.GetLeadingTrivia())
                    token = DirectCast(token.WithLeadingTrivia(Nothing), SyntaxToken)
                End If
 
                If Not _preserveExistingDiagnostics Then
                    token = token.WithoutDiagnostics()
                End If
 
                Dim trailingTrivia As GreenNode = Nothing
 
                If token.HasTrailingTrivia() AndAlso (isLast OrElse isMissing OrElse TriviaListContainsStructuredTrivia(token.GetTrailingTrivia())) Then
                    trailingTrivia = token.GetTrailingTrivia()
                    token = DirectCast(token.WithTrailingTrivia(Nothing), SyntaxToken)
                End If
 
                If isMissing Then
                    ' Don't add missing tokens to skipped tokens, but preserve their diagnostics.
                    If token.ContainsDiagnostics() Then
                        ' Move diagnostics on missing token to next token.
                        If _diagnosticsToAdd IsNot Nothing Then
                            _diagnosticsToAdd = _diagnosticsToAdd.Concat(token.GetDiagnostics())
                        Else
                            _diagnosticsToAdd = token.GetDiagnostics()
                        End If
                        _addDiagnosticsToFirstTokenOnly = True
                    End If
                Else
                    _skippedTokensBuilder.Add(token)
                End If
 
                If trailingTrivia IsNot Nothing Then
                    FinishInProgressTokens()
                    AddTrivia(trailingTrivia)
                End If
 
                If isFirst AndAlso _addDiagnosticsToFirstTokenOnly Then
                    FinishInProgressTokens() ' implicitly adds the diagnostics.
                End If
            End Sub
 
            ' Get the final list of trivia nodes we should attached.
            Public Function GetTriviaList() As CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)
                FinishInProgressTokens()
                If _diagnosticsToAdd IsNot Nothing AndAlso _diagnosticsToAdd.Any() Then
                    ' Still have diagnostics. Add to the last item.
                    If _triviaListBuilder.Count > 0 Then
                        _triviaListBuilder(_triviaListBuilder.Count - 1) = _triviaListBuilder(_triviaListBuilder.Count - 1).WithAdditionalDiagnostics(_diagnosticsToAdd.ToArray())
                    End If
                End If
                Return _triviaListBuilder.ToList()
            End Function
        End Class
 
        ' From a syntax node, create a list of trivia node that encapsulates the same text. We use SkippedTokens trivia
        ' to encapsulate the tokens, plus extract trivia from those tokens into the trivia list because:
        '    - We want leading trivia and trailing trivia to be directly visible in the trivia list, not on the tokens
        '      inside the skipped tokens trivia.
        '    - We have to expose structured trivia directives.
        '
        ' Several options controls how diagnostics are handled:
        '   "preserveDiagnostics" means existing diagnostics are preserved, otherwise they are thrown away
        '   "addDiagnostic", if not Nothing, is added as a diagnostics
        '   "addDiagnosticsToFirstTokenOnly" means that "addDiagnostics" is attached only to the first token, otherwise
        '    it is attached to all tokens.
        Private Function CreateSkippedTrivia(node As GreenNode,
                                             preserveDiagnostics As Boolean,
                                             addDiagnosticToFirstTokenOnly As Boolean,
                                             addDiagnostic As DiagnosticInfo) As CoreInternalSyntax.SyntaxList(Of GreenNode)
            If node.RawKind = SyntaxKind.SkippedTokensTrivia Then
                ' already skipped trivia
                If addDiagnostic IsNot Nothing Then
                    node = node.AddError(addDiagnostic)
                End If
                Return node
            End If
 
            ' Get the tokens and diagnostics.
            Dim diagnostics As IList(Of DiagnosticInfo) = New List(Of DiagnosticInfo)
            Dim tokenListBuilder = SyntaxListBuilder(Of SyntaxToken).Create
 
            CollectConstituentTokensAndDiagnostics(node, tokenListBuilder, diagnostics)
 
            ' Adjust diagnostics based on input.
            If Not preserveDiagnostics Then
                diagnostics.Clear()
            End If
            If addDiagnostic IsNot Nothing Then
                diagnostics.Add(addDiagnostic)
            End If
 
            Dim skippedTriviaBuilder As New SkippedTriviaBuilder(preserveDiagnostics, addDiagnosticToFirstTokenOnly, diagnostics)
 
            ' Get through each token and add it. 
            For i As Integer = 0 To tokenListBuilder.Count - 1
                Dim currentToken As SyntaxToken = tokenListBuilder(i)
 
                skippedTriviaBuilder.AddToken(currentToken, isFirst:=(i = 0), isLast:=(i = tokenListBuilder.Count - 1))
            Next
 
            Return skippedTriviaBuilder.GetTriviaList()
        End Function
 
        ''' <summary>
        ''' Add all the tokens in this node and children to the build token list builder. While doing this, add any
        ''' diagnostics not on tokens to the given diagnostic info list.
        ''' </summary>
        Friend Sub CollectConstituentTokensAndDiagnostics(
                this As GreenNode,
                tokenListBuilder As SyntaxListBuilder(Of SyntaxToken),
                nonTokenDiagnostics As IList(Of DiagnosticInfo))
            If this Is Nothing Then
                Return
            End If
 
            If this.IsToken Then
                tokenListBuilder.Add(DirectCast(this, SyntaxToken))
                Return
            End If
 
            ' Add diagnostics.
            Dim diagnostics As DiagnosticInfo() = this.GetDiagnostics()
            If diagnostics IsNot Nothing AndAlso diagnostics.Length > 0 Then
                For Each diag In diagnostics
                    nonTokenDiagnostics.Add(diag)
                Next
            End If
 
            ' Recurse to subtrees.
            For i = 0 To this.SlotCount() - 1
                Dim green = this.GetSlot(i)
                If green IsNot Nothing Then
                    CollectConstituentTokensAndDiagnostics(green, tokenListBuilder, nonTokenDiagnostics)
                End If
            Next
        End Sub
 
#End Region
 
#Region "Whitespace Containment"
        <Extension()>
        Friend Function ContainsWhitespaceTrivia(this As GreenNode) As Boolean
            If this Is Nothing Then
                Return False
            End If
 
            Dim trivia = New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(this)
 
            For i = 0 To trivia.Count - 1
                Dim kind = trivia.ItemUntyped(i).RawKind
                If kind = SyntaxKind.WhitespaceTrivia OrElse
                    kind = SyntaxKind.EndOfLineTrivia Then
 
                    Return True
                End If
            Next
 
            Return False
        End Function
#End Region
 
        <Extension()>
        Friend Function ContainsCommentTrivia(this As GreenNode) As Boolean
            If this Is Nothing Then
                Return False
            End If
 
            Dim trivia = New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(this)
 
            For i = 0 To trivia.Count - 1
                Dim kind = trivia.ItemUntyped(i).RawKind
                If kind = SyntaxKind.CommentTrivia Then
                    Return True
                End If
            Next
 
            Return False
        End Function
 
        ' This was Semantics::ExtractAnonTypeMemberName in Dev 10
        <Extension()>
        Friend Function ExtractAnonymousTypeMemberName(input As ExpressionSyntax,
                                           ByRef isNameDictionaryAccess As Boolean,
                                           ByRef isRejectedXmlName As Boolean) As SyntaxToken
            Dim conditionalAccessStack As ArrayBuilder(Of ConditionalAccessExpressionSyntax) = Nothing
            Dim result As SyntaxToken = ExtractAnonymousTypeMemberName(conditionalAccessStack, input, isNameDictionaryAccess, isRejectedXmlName)
 
            If conditionalAccessStack IsNot Nothing Then
                conditionalAccessStack.Free()
            End If
 
            Return result
        End Function
 
        <Extension()>
        Private Function ExtractAnonymousTypeMemberName(
            ByRef conditionalAccessStack As ArrayBuilder(Of ConditionalAccessExpressionSyntax),
            input As ExpressionSyntax,
            ByRef isNameDictionaryAccess As Boolean,
            ByRef isRejectedXmlName As Boolean
        ) As SyntaxToken
TryAgain:
            Select Case input.Kind
                Case SyntaxKind.IdentifierName
                    Return DirectCast(input, IdentifierNameSyntax).Identifier
 
                Case SyntaxKind.XmlName
                    Dim xmlNameInferredFrom = DirectCast(input, XmlNameSyntax)
                    If Not Scanner.IsIdentifier(xmlNameInferredFrom.LocalName.ToString) Then
                        isRejectedXmlName = True
                        Return Nothing
                    End If
 
                    Return xmlNameInferredFrom.LocalName
 
                Case SyntaxKind.XmlBracketedName
                    ' handles something like <a-a>
                    Dim xmlNameInferredFrom = DirectCast(input, XmlBracketedNameSyntax)
                    input = xmlNameInferredFrom.Name
                    GoTo TryAgain
 
                Case SyntaxKind.SimpleMemberAccessExpression,
                     SyntaxKind.DictionaryAccessExpression
 
                    Dim memberAccess = DirectCast(input, MemberAccessExpressionSyntax)
                    Dim receiver As ExpressionSyntax = If(memberAccess.Expression, PopAndGetConditionalAccessReceiver(conditionalAccessStack))
 
                    If input.Kind = SyntaxKind.SimpleMemberAccessExpression Then
                        ' See if this is an identifier qualified with XmlElementAccessExpression or XmlDescendantAccessExpression
                        If receiver IsNot Nothing Then
                            Select Case receiver.Kind
                                Case SyntaxKind.XmlElementAccessExpression,
                                    SyntaxKind.XmlDescendantAccessExpression
 
                                    input = receiver
                                    GoTo TryAgain
                            End Select
                        End If
                    End If
 
                    ClearConditionalAccessStack(conditionalAccessStack)
 
                    isNameDictionaryAccess = input.Kind = SyntaxKind.DictionaryAccessExpression
                    input = memberAccess.Name
                    GoTo TryAgain
 
                Case SyntaxKind.XmlElementAccessExpression,
                     SyntaxKind.XmlAttributeAccessExpression,
                     SyntaxKind.XmlDescendantAccessExpression
 
                    Dim xmlAccess = DirectCast(input, XmlMemberAccessExpressionSyntax)
                    ClearConditionalAccessStack(conditionalAccessStack)
 
                    input = xmlAccess.Name
                    GoTo TryAgain
 
                Case SyntaxKind.InvocationExpression
                    Dim invocation = DirectCast(input, InvocationExpressionSyntax)
                    Dim target As ExpressionSyntax = If(invocation.Expression, PopAndGetConditionalAccessReceiver(conditionalAccessStack))
 
                    If target Is Nothing Then
                        Exit Select
                    End If
 
                    If invocation.ArgumentList Is Nothing OrElse invocation.ArgumentList.Arguments.Count = 0 Then
                        input = target
                        GoTo TryAgain
                    End If
 
                    Debug.Assert(invocation.ArgumentList IsNot Nothing)
 
                    If invocation.ArgumentList.Arguments.Count = 1 Then
                        ' See if this is an indexed XmlElementAccessExpression or XmlDescendantAccessExpression
                        Select Case target.Kind
                            Case SyntaxKind.XmlElementAccessExpression,
                                SyntaxKind.XmlDescendantAccessExpression
                                input = target
                                GoTo TryAgain
                        End Select
                    End If
 
                Case SyntaxKind.ConditionalAccessExpression
                    Dim access = DirectCast(input, ConditionalAccessExpressionSyntax)
 
                    If conditionalAccessStack Is Nothing Then
                        conditionalAccessStack = ArrayBuilder(Of ConditionalAccessExpressionSyntax).GetInstance()
                    End If
 
                    conditionalAccessStack.Push(access)
 
                    input = access.WhenNotNull
                    GoTo TryAgain
            End Select
 
            Return Nothing
        End Function
 
        Private Sub ClearConditionalAccessStack(conditionalAccessStack As ArrayBuilder(Of ConditionalAccessExpressionSyntax))
            If conditionalAccessStack IsNot Nothing Then
                conditionalAccessStack.Clear()
            End If
        End Sub
 
        Private Function PopAndGetConditionalAccessReceiver(conditionalAccessStack As ArrayBuilder(Of ConditionalAccessExpressionSyntax)) As ExpressionSyntax
            If conditionalAccessStack Is Nothing OrElse conditionalAccessStack.Count = 0 Then
                Return Nothing
            End If
 
            Return conditionalAccessStack.Pop().Expression
        End Function
 
        Friend Function IsExecutableStatementOrItsPart(node As VisualBasicSyntaxNode) As Boolean
            If TypeOf node Is ExecutableStatementSyntax Then
                Return True
            End If
 
            ' Parser parses some statements part-by-part and then wraps them with executable 
            ' statements, so we may stumble on such a part in error-recovery scenarios in case parser 
            ' didn't wrap it because of some parse error; in some cases such statements should 
            ' be considered equal to executable statements
            ' 
            ' Example: parsing a simple text 'If True' produces just an IfStatementSyntax (which
            ' is not an executable statement) which is supposed to be wrapped with MultiLineIfBlockSyntax
            ' or SingleLineIfStatement in non-error scenario.
            Select Case node.Kind
                Case SyntaxKind.IfStatement,
                     SyntaxKind.ElseIfStatement,
                     SyntaxKind.ElseStatement,
                     SyntaxKind.WithStatement,
                     SyntaxKind.TryStatement,
                     SyntaxKind.CatchStatement,
                     SyntaxKind.FinallyStatement,
                     SyntaxKind.SyncLockStatement,
                     SyntaxKind.WhileStatement,
                     SyntaxKind.UsingStatement,
                     SyntaxKind.SelectStatement,
                     SyntaxKind.CaseStatement,
                     SyntaxKind.CaseElseStatement,
                     SyntaxKind.SimpleDoStatement,
                     SyntaxKind.DoWhileStatement,
                     SyntaxKind.DoUntilStatement,
                     SyntaxKind.ForStatement,
                     SyntaxKind.ForEachStatement
                    Return True
            End Select
 
            Return False
        End Function
 
    End Module
End Namespace