File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\Extensions\SyntaxNodeExtensions.vb
Web Access
Project: src\src\Workspaces\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj (Microsoft.CodeAnalysis.VisualBasic.Workspaces)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports System.Runtime.CompilerServices
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.LanguageService
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
    Friend Module SyntaxNodeExtensions
        <Extension()>
        Public Function IsParentKind(node As SyntaxNode, kind As SyntaxKind) As Boolean
            Return node IsNot Nothing AndAlso
                   node.Parent.IsKind(kind)
        End Function
 
        <Extension()>
        Public Function IsParentKind(node As SyntaxNode, kind1 As SyntaxKind, kind2 As SyntaxKind) As Boolean
            Return node IsNot Nothing AndAlso
                   IsKind(node.Parent, kind1, kind2)
        End Function
 
        <Extension()>
        Public Function IsParentKind(node As SyntaxNode, kind1 As SyntaxKind, kind2 As SyntaxKind, kind3 As SyntaxKind) As Boolean
            Return node IsNot Nothing AndAlso
                   IsKind(node.Parent, kind1, kind2, kind3)
        End Function
 
        <Extension()>
        Public Function IsKind(node As SyntaxNode, kind1 As SyntaxKind, kind2 As SyntaxKind) As Boolean
            If node Is Nothing Then
                Return False
            End If
 
            Return node.Kind = kind1 OrElse
                   node.Kind = kind2
        End Function
 
        <Extension()>
        Public Function IsKind(node As SyntaxNode, kind1 As SyntaxKind, kind2 As SyntaxKind, kind3 As SyntaxKind) As Boolean
            If node Is Nothing Then
                Return False
            End If
 
            Return node.Kind = kind1 OrElse
                   node.Kind = kind2 OrElse
                   node.Kind = kind3
        End Function
 
        <Extension()>
        Public Function IsKind(node As SyntaxNode, kind1 As SyntaxKind, kind2 As SyntaxKind, kind3 As SyntaxKind, kind4 As SyntaxKind) As Boolean
            If node Is Nothing Then
                Return False
            End If
 
            Return node.Kind = kind1 OrElse
                   node.Kind = kind2 OrElse
                   node.Kind = kind3 OrElse
                   node.Kind = kind4
        End Function
 
        <Extension()>
        Public Function IsKind(node As SyntaxNode, ParamArray kinds As SyntaxKind()) As Boolean
            If node Is Nothing Then
                Return False
            End If
 
            Return kinds.Contains(node.Kind())
        End Function
 
        <Extension()>
        Public Function IsInConstantContext(expression As SyntaxNode) As Boolean
            If expression.GetAncestor(Of ParameterSyntax)() IsNot Nothing Then
                Return True
            End If
 
            ' TODO(cyrusn): Add more cases
            Return False
        End Function
 
        <Extension()>
        Public Function IsInStaticContext(node As SyntaxNode) As Boolean
            Dim containingType = node.GetAncestorOrThis(Of TypeBlockSyntax)()
            If containingType.IsKind(SyntaxKind.ModuleBlock) Then
                Return True
            End If
 
            Return node.GetAncestorsOrThis(Of StatementSyntax)().
                        SelectMany(Function(s) s.GetModifiers()).
                        Any(Function(t) t.Kind = SyntaxKind.SharedKeyword)
        End Function
 
        <Extension()>
        Public Function IsStatementContainerNode(node As SyntaxNode) As Boolean
            If node.IsExecutableBlock() Then
                Return True
            End If
 
            Dim singleLineLambdaExpression = TryCast(node, SingleLineLambdaExpressionSyntax)
            If singleLineLambdaExpression IsNot Nothing AndAlso TypeOf singleLineLambdaExpression.Body Is ExecutableStatementSyntax Then
                Return True
            End If
 
            Return False
        End Function
 
        <Extension()>
        Public Function GetStatements(node As SyntaxNode) As SyntaxList(Of StatementSyntax)
            Contract.ThrowIfNull(node)
            Contract.ThrowIfFalse(node.IsStatementContainerNode())
 
            Dim methodBlock = TryCast(node, MethodBlockBaseSyntax)
            If methodBlock IsNot Nothing Then
                Return methodBlock.Statements
            End If
 
            Dim whileBlock = TryCast(node, WhileBlockSyntax)
            If whileBlock IsNot Nothing Then
                Return whileBlock.Statements
            End If
 
            Dim usingBlock = TryCast(node, UsingBlockSyntax)
            If usingBlock IsNot Nothing Then
                Return usingBlock.Statements
            End If
 
            Dim syncLockBlock = TryCast(node, SyncLockBlockSyntax)
            If syncLockBlock IsNot Nothing Then
                Return syncLockBlock.Statements
            End If
 
            Dim withBlock = TryCast(node, WithBlockSyntax)
            If withBlock IsNot Nothing Then
                Return withBlock.Statements
            End If
 
            Dim singleLineIfStatement = TryCast(node, SingleLineIfStatementSyntax)
            If singleLineIfStatement IsNot Nothing Then
                Return singleLineIfStatement.Statements
            End If
 
            Dim singleLineElseClause = TryCast(node, SingleLineElseClauseSyntax)
            If singleLineElseClause IsNot Nothing Then
                Return singleLineElseClause.Statements
            End If
 
            Dim ifBlock = TryCast(node, MultiLineIfBlockSyntax)
            If ifBlock IsNot Nothing Then
                Return ifBlock.Statements
            End If
 
            Dim elseIfBlock = TryCast(node, ElseIfBlockSyntax)
            If elseIfBlock IsNot Nothing Then
                Return elseIfBlock.Statements
            End If
 
            Dim elseBlock = TryCast(node, ElseBlockSyntax)
            If elseBlock IsNot Nothing Then
                Return elseBlock.Statements
            End If
 
            Dim tryBlock = TryCast(node, TryBlockSyntax)
            If tryBlock IsNot Nothing Then
                Return tryBlock.Statements
            End If
 
            Dim catchBlock = TryCast(node, CatchBlockSyntax)
            If catchBlock IsNot Nothing Then
                Return catchBlock.Statements
            End If
 
            Dim finallyBlock = TryCast(node, FinallyBlockSyntax)
            If finallyBlock IsNot Nothing Then
                Return finallyBlock.Statements
            End If
 
            Dim caseBlock = TryCast(node, CaseBlockSyntax)
            If caseBlock IsNot Nothing Then
                Return caseBlock.Statements
            End If
 
            Dim doLoopBlock = TryCast(node, DoLoopBlockSyntax)
            If doLoopBlock IsNot Nothing Then
                Return doLoopBlock.Statements
            End If
 
            Dim forBlock = TryCast(node, ForOrForEachBlockSyntax)
            If forBlock IsNot Nothing Then
                Return forBlock.Statements
            End If
 
            Dim singleLineLambdaExpression = TryCast(node, SingleLineLambdaExpressionSyntax)
            If singleLineLambdaExpression IsNot Nothing Then
                Return If(TypeOf singleLineLambdaExpression.Body Is StatementSyntax,
                            SyntaxFactory.SingletonList(DirectCast(singleLineLambdaExpression.Body, StatementSyntax)),
                            Nothing)
            End If
 
            Dim multiLineLambdaExpression = TryCast(node, MultiLineLambdaExpressionSyntax)
            If multiLineLambdaExpression IsNot Nothing Then
                Return multiLineLambdaExpression.Statements
            End If
 
            Throw ExceptionUtilities.UnexpectedValue(node)
        End Function
 
        <Extension()>
        Friend Function IsAsyncSupportedFunctionSyntax(node As SyntaxNode) As Boolean
            Select Case node?.Kind()
                Case _
                SyntaxKind.FunctionBlock,
                SyntaxKind.SubBlock,
                SyntaxKind.MultiLineFunctionLambdaExpression,
                SyntaxKind.MultiLineSubLambdaExpression,
                SyntaxKind.SingleLineFunctionLambdaExpression,
                SyntaxKind.SingleLineSubLambdaExpression
                    Return True
            End Select
 
            Return False
        End Function
 
        <Extension()>
        Friend Function IsMultiLineLambda(node As SyntaxNode) As Boolean
            Return SyntaxFacts.IsMultiLineLambdaExpression(node.Kind())
        End Function
 
        <Extension()>
        Friend Function GetTypeCharacterString(type As TypeCharacter) As String
            Select Case type
                Case TypeCharacter.Integer
                    Return "%"
                Case TypeCharacter.Long
                    Return "&"
                Case TypeCharacter.Decimal
                    Return "@"
                Case TypeCharacter.Single
                    Return "!"
                Case TypeCharacter.Double
                    Return "#"
                Case TypeCharacter.String
                    Return "$"
                Case Else
                    Throw New ArgumentException("Unexpected TypeCharacter.", NameOf(type))
            End Select
        End Function
 
        <Extension()>
        Public Function SpansPreprocessorDirective(Of TSyntaxNode As SyntaxNode)(list As IEnumerable(Of TSyntaxNode)) As Boolean
            Return VisualBasicSyntaxFacts.Instance.SpansPreprocessorDirective(list)
        End Function
 
        <Extension()>
        Public Function ConvertToSingleLine(Of TNode As SyntaxNode)(node As TNode, Optional useElasticTrivia As Boolean = False) As TNode
            If node Is Nothing Then
                Return node
            End If
 
            Dim rewriter = New SingleLineRewriter(useElasticTrivia)
            Return DirectCast(rewriter.Visit(node), TNode)
        End Function
 
        ''' <summary>
        ''' Breaks up the list of provided nodes, based on how they are 
        ''' interspersed with pp directives, into groups.  Within these groups
        ''' nodes can be moved around safely, without breaking any pp 
        ''' constructs.
        ''' </summary>
        <Extension()>
        Public Function SplitNodesOnPreprocessorBoundaries(Of TSyntaxNode As SyntaxNode)(
            nodes As IEnumerable(Of TSyntaxNode),
            cancellationToken As CancellationToken) As IList(Of IList(Of TSyntaxNode))
 
            Dim result = New List(Of IList(Of TSyntaxNode))()
 
            Dim currentGroup = New List(Of TSyntaxNode)()
            For Each node In nodes
                Dim hasUnmatchedInteriorDirective = node.ContainsInterleavedDirective(cancellationToken)
                Dim hasLeadingDirective = node.GetLeadingTrivia().Any(Function(t) SyntaxFacts.IsPreprocessorDirective(t.Kind))
 
                If hasUnmatchedInteriorDirective Then
                    ' we have a #if/#endif/#region/#endregion/#else/#elif in
                    ' this node that belongs to a span of pp directives that
                    ' is not entirely contained within the node.  i.e.:
                    '
                    '   void Goo() {
                    '      #if ...
                    '   }
                    '
                    ' This node cannot be moved at all.  It is in a group that
                    ' only contains itself (and thus can never be moved).
 
                    ' add whatever group we've built up to now. And reset the 
                    ' next group to empty.
                    result.Add(currentGroup)
                    currentGroup = New List(Of TSyntaxNode)()
 
                    result.Add(New List(Of TSyntaxNode) From {node})
                ElseIf hasLeadingDirective Then
                    ' We have a PP directive before us.  i.e.:
                    ' 
                    '   #if ...
                    '      void Goo() {
                    '
                    ' That means we start a new group that is contained between
                    ' the above directive and the following directive.
 
                    ' add whatever group we've built up to now. And reset the 
                    ' next group to empty.
                    result.Add(currentGroup)
                    currentGroup = New List(Of TSyntaxNode)()
 
                    currentGroup.Add(node)
                Else
                    ' simple case.  just add ourselves to the current group
                    currentGroup.Add(node)
                End If
            Next
 
            ' add the remainder of the final group.
            result.Add(currentGroup)
 
            ' Now, filter out any empty groups.
            result = result.Where(Function(group) Not group.IsEmpty()).ToList()
            Return result
        End Function
 
        ''' <summary>
        ''' Returns true if the passed in node contains an interleaved pp 
        ''' directive.
        ''' 
        ''' i.e. The following returns false:
        ''' 
        '''   void Goo() {
        ''' #if true
        ''' #endif
        '''   }
        ''' 
        ''' #if true
        '''   void Goo() {
        '''   }
        ''' #endif
        ''' 
        ''' but these return true:
        ''' 
        ''' #if true
        '''   void Goo() {
        ''' #endif
        '''   }
        ''' 
        '''   void Goo() {
        ''' #if true
        '''   }
        ''' #endif
        ''' 
        ''' #if true
        '''   void Goo() {
        ''' #else
        '''   }
        ''' #endif
        ''' 
        ''' i.e. the method returns true if it contains a PP directive that 
        ''' belongs to a grouping constructs (like #if/#endif or 
        ''' #region/#endregion), but the grouping construct isn't entirely c
        ''' contained within the span of the node.
        ''' </summary>
        <Extension()>
        Public Function ContainsInterleavedDirective(node As SyntaxNode, cancellationToken As CancellationToken) As Boolean
            Return VisualBasicSyntaxFacts.Instance.ContainsInterleavedDirective(node, cancellationToken)
        End Function
 
        <Extension>
        Public Function ContainsInterleavedDirective(
            token As SyntaxToken,
            textSpan As TextSpan,
            cancellationToken As CancellationToken) As Boolean
 
            Return ContainsInterleavedDirective(textSpan, token.LeadingTrivia, cancellationToken) OrElse
                ContainsInterleavedDirective(textSpan, token.TrailingTrivia, cancellationToken)
        End Function
 
        Private Function ContainsInterleavedDirective(
            textSpan As TextSpan,
            list As SyntaxTriviaList,
            cancellationToken As CancellationToken) As Boolean
 
            For Each trivia In list
                If textSpan.Contains(trivia.Span) Then
                    If ContainsInterleavedDirective(textSpan, trivia, cancellationToken) Then
                        Return True
                    End If
                End If
            Next trivia
 
            Return False
        End Function
 
        Private Function ContainsInterleavedDirective(
            textSpan As TextSpan,
            trivia As SyntaxTrivia,
            cancellationToken As CancellationToken) As Boolean
 
            If trivia.HasStructure AndAlso TypeOf trivia.GetStructure() Is DirectiveTriviaSyntax Then
                Dim parentSpan = trivia.GetStructure().Span
                Dim directiveSyntax = DirectCast(trivia.GetStructure(), DirectiveTriviaSyntax)
                If directiveSyntax.IsKind(SyntaxKind.RegionDirectiveTrivia, SyntaxKind.EndRegionDirectiveTrivia, SyntaxKind.IfDirectiveTrivia, SyntaxKind.EndIfDirectiveTrivia) Then
                    Dim match = directiveSyntax.GetMatchingStartOrEndDirective(cancellationToken)
                    If match IsNot Nothing Then
                        Dim matchSpan = match.Span
                        If Not textSpan.Contains(matchSpan.Start) Then
                            ' The match for this pp directive is outside
                            ' this node.
                            Return True
                        End If
                    End If
                ElseIf directiveSyntax.IsKind(SyntaxKind.ElseDirectiveTrivia, SyntaxKind.ElseIfDirectiveTrivia) Then
                    Dim directives = directiveSyntax.GetMatchingConditionalDirectives(cancellationToken)
                    If directives.Any() Then
                        If Not textSpan.Contains(directives(0).SpanStart) OrElse
                           Not textSpan.Contains(directives.Last().SpanStart) Then
                            ' This else/elif belongs to a pp span that isn't 
                            ' entirely within this node.
                            Return True
                        End If
                    End If
                End If
            End If
 
            Return False
        End Function
 
        <Extension()>
        Public Function GetLeadingBlankLines(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As ImmutableArray(Of SyntaxTrivia)
            Return VisualBasicFileBannerFacts.Instance.GetLeadingBlankLines(node)
        End Function
 
        <Extension()>
        Public Function GetNodeWithoutLeadingBlankLines(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As TSyntaxNode
            Return VisualBasicFileBannerFacts.Instance.GetNodeWithoutLeadingBlankLines(node)
        End Function
 
        <Extension()>
        Public Function GetNodeWithoutLeadingBlankLines(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode, ByRef strippedTrivia As ImmutableArray(Of SyntaxTrivia)) As TSyntaxNode
            Return VisualBasicFileBannerFacts.Instance.GetNodeWithoutLeadingBlankLines(node, strippedTrivia)
        End Function
 
        <Extension()>
        Public Function GetLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As ImmutableArray(Of SyntaxTrivia)
            Return VisualBasicFileBannerFacts.Instance.GetLeadingBannerAndPreprocessorDirectives(node)
        End Function
 
        <Extension()>
        Public Function GetNodeWithoutLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As TSyntaxNode
            Return VisualBasicFileBannerFacts.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node)
        End Function
 
        <Extension()>
        Public Function GetNodeWithoutLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode, ByRef strippedTrivia As ImmutableArray(Of SyntaxTrivia)) As TSyntaxNode
            Return VisualBasicFileBannerFacts.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node, strippedTrivia)
        End Function
 
        ''' <summary>
        ''' Returns true if this is a block that can contain multiple executable statements.  i.e.
        ''' this node is the VB equivalent of a BlockSyntax in C#.
        ''' </summary>
        <Extension()>
        Public Function IsExecutableBlock(node As SyntaxNode) As Boolean
            If node IsNot Nothing Then
                If TypeOf node Is MethodBlockBaseSyntax OrElse
                   TypeOf node Is DoLoopBlockSyntax OrElse
                   TypeOf node Is ForOrForEachBlockSyntax OrElse
                   TypeOf node Is MultiLineLambdaExpressionSyntax Then
                    Return True
                End If
 
                Select Case node.Kind
                    Case SyntaxKind.WhileBlock,
                         SyntaxKind.UsingBlock,
                         SyntaxKind.SyncLockBlock,
                         SyntaxKind.WithBlock,
                         SyntaxKind.SingleLineIfStatement,
                         SyntaxKind.SingleLineElseClause,
                         SyntaxKind.SingleLineSubLambdaExpression,
                         SyntaxKind.MultiLineIfBlock,
                         SyntaxKind.ElseIfBlock,
                         SyntaxKind.ElseBlock,
                         SyntaxKind.TryBlock,
                         SyntaxKind.CatchBlock,
                         SyntaxKind.FinallyBlock,
                         SyntaxKind.CaseBlock,
                         SyntaxKind.CaseElseBlock
                        Return True
                End Select
            End If
 
            Return False
        End Function
 
        <Extension()>
        Public Function GetContainingExecutableBlocks(node As SyntaxNode) As IEnumerable(Of SyntaxNode)
            Return node.GetAncestorsOrThis(Of StatementSyntax).
                        Where(Function(s) s.Parent.IsExecutableBlock() AndAlso s.Parent.GetExecutableBlockStatements().Contains(s)).
                        Select(Function(s) s.Parent)
        End Function
 
        <Extension()>
        Public Function GetContainingMultiLineExecutableBlocks(node As SyntaxNode) As IEnumerable(Of SyntaxNode)
            Return node.GetAncestorsOrThis(Of StatementSyntax).
                        Where(Function(s) s.Parent.IsMultiLineExecutableBlock AndAlso s.Parent.GetExecutableBlockStatements().Contains(s)).
                        Select(Function(s) s.Parent)
        End Function
 
        <Extension()>
        Public Function FindInnermostCommonExecutableBlock(nodes As IEnumerable(Of SyntaxNode)) As SyntaxNode
            Dim blocks As IEnumerable(Of SyntaxNode) = Nothing
            For Each node In nodes
                blocks = If(blocks Is Nothing,
                             node.GetContainingExecutableBlocks(),
                             blocks.Intersect(node.GetContainingExecutableBlocks()))
            Next
 
            Return If(blocks Is Nothing, Nothing, blocks.FirstOrDefault())
        End Function
 
        <Extension()>
        Public Function GetExecutableBlockStatements(node As SyntaxNode) As SyntaxList(Of StatementSyntax)
            If node IsNot Nothing Then
                If TypeOf node Is MethodBlockBaseSyntax Then
                    Return DirectCast(node, MethodBlockBaseSyntax).Statements
                ElseIf TypeOf node Is DoLoopBlockSyntax Then
                    Return DirectCast(node, DoLoopBlockSyntax).Statements
                ElseIf TypeOf node Is ForOrForEachBlockSyntax Then
                    Return DirectCast(node, ForOrForEachBlockSyntax).Statements
                ElseIf TypeOf node Is MultiLineLambdaExpressionSyntax Then
                    Return DirectCast(node, MultiLineLambdaExpressionSyntax).Statements
                End If
 
                Select Case node.Kind
                    Case SyntaxKind.WhileBlock
                        Return DirectCast(node, WhileBlockSyntax).Statements
                    Case SyntaxKind.UsingBlock
                        Return DirectCast(node, UsingBlockSyntax).Statements
                    Case SyntaxKind.SyncLockBlock
                        Return DirectCast(node, SyncLockBlockSyntax).Statements
                    Case SyntaxKind.WithBlock
                        Return DirectCast(node, WithBlockSyntax).Statements
                    Case SyntaxKind.SingleLineIfStatement
                        Return DirectCast(node, SingleLineIfStatementSyntax).Statements
                    Case SyntaxKind.SingleLineElseClause
                        Return DirectCast(node, SingleLineElseClauseSyntax).Statements
                    Case SyntaxKind.SingleLineSubLambdaExpression
                        Return SyntaxFactory.SingletonList(DirectCast(DirectCast(node, SingleLineLambdaExpressionSyntax).Body, StatementSyntax))
                    Case SyntaxKind.MultiLineIfBlock
                        Return DirectCast(node, MultiLineIfBlockSyntax).Statements
                    Case SyntaxKind.ElseIfBlock
                        Return DirectCast(node, ElseIfBlockSyntax).Statements
                    Case SyntaxKind.ElseBlock
                        Return DirectCast(node, ElseBlockSyntax).Statements
                    Case SyntaxKind.TryBlock
                        Return DirectCast(node, TryBlockSyntax).Statements
                    Case SyntaxKind.CatchBlock
                        Return DirectCast(node, CatchBlockSyntax).Statements
                    Case SyntaxKind.FinallyBlock
                        Return DirectCast(node, FinallyBlockSyntax).Statements
                    Case SyntaxKind.CaseBlock, SyntaxKind.CaseElseBlock
                        Return DirectCast(node, CaseBlockSyntax).Statements
                End Select
            End If
 
            Return Nothing
        End Function
 
        <Extension()>
        Public Function ReplaceStatements(node As SyntaxNode,
                                          statements As SyntaxList(Of StatementSyntax),
                                          ParamArray annotations As SyntaxAnnotation()) As SyntaxNode
            Return node.TypeSwitch(
                Function(x As MethodBlockSyntax) x.WithStatements(statements),
                Function(x As ConstructorBlockSyntax) x.WithStatements(statements),
                Function(x As OperatorBlockSyntax) x.WithStatements(statements),
                Function(x As AccessorBlockSyntax) x.WithStatements(statements),
                Function(x As DoLoopBlockSyntax) x.WithStatements(statements),
                Function(x As ForBlockSyntax) x.WithStatements(statements),
                Function(x As ForEachBlockSyntax) x.WithStatements(statements),
                Function(x As MultiLineLambdaExpressionSyntax) x.WithStatements(statements),
                Function(x As WhileBlockSyntax) x.WithStatements(statements),
                Function(x As UsingBlockSyntax) x.WithStatements(statements),
                Function(x As SyncLockBlockSyntax) x.WithStatements(statements),
                Function(x As WithBlockSyntax) x.WithStatements(statements),
                Function(x As SingleLineIfStatementSyntax) x.WithStatements(statements),
                Function(x As SingleLineElseClauseSyntax) x.WithStatements(statements),
                Function(x As SingleLineLambdaExpressionSyntax) ReplaceSingleLineLambdaExpressionStatements(x, statements, annotations),
                Function(x As MultiLineIfBlockSyntax) x.WithStatements(statements),
                Function(x As ElseIfBlockSyntax) x.WithStatements(statements),
                Function(x As ElseBlockSyntax) x.WithStatements(statements),
                Function(x As TryBlockSyntax) x.WithStatements(statements),
                Function(x As CatchBlockSyntax) x.WithStatements(statements),
                Function(x As FinallyBlockSyntax) x.WithStatements(statements),
                Function(x As CaseBlockSyntax) x.WithStatements(statements))
        End Function
 
        <Extension()>
        Public Function ReplaceSingleLineLambdaExpressionStatements(
                node As SingleLineLambdaExpressionSyntax,
                statements As SyntaxList(Of StatementSyntax),
                ParamArray annotations As SyntaxAnnotation()) As SyntaxNode
            If node.Kind = SyntaxKind.SingleLineSubLambdaExpression Then
                Dim singleLineLambda = DirectCast(node, SingleLineLambdaExpressionSyntax)
                If statements.Count = 1 Then
                    Return node.WithBody(statements.First())
                Else
#If False Then
                    Dim statementsAndSeparators = statements.GetWithSeparators()
                    If statementsAndSeparators.LastOrDefault().IsNode Then
                        statements = Syntax.SeparatedList(Of StatementSyntax)(
                            statementsAndSeparators.Concat(Syntax.Token(SyntaxKind.StatementTerminatorToken)))
                    End If
#End If
 
                    Return SyntaxFactory.MultiLineSubLambdaExpression(
                        singleLineLambda.SubOrFunctionHeader,
                        statements,
                        SyntaxFactory.EndSubStatement()).WithAdditionalAnnotations(annotations)
                End If
            End If
 
            ' Can't be called on a single line lambda (as it can't have statements for children)
            Throw New InvalidOperationException()
        End Function
 
        <Extension()>
        Public Function ReplaceStatements(tree As SyntaxTree,
                                          executableBlock As SyntaxNode,
                                          statements As SyntaxList(Of StatementSyntax),
                                          ParamArray annotations As SyntaxAnnotation()) As SyntaxNode
            If executableBlock.IsSingleLineExecutableBlock() Then
                Return ConvertSingleLineToMultiLineExecutableBlock(tree, executableBlock, statements, annotations)
            End If
 
            ' TODO(cyrusn): Implement this.
            Throw ExceptionUtilities.Unreachable
        End Function
 
        <Extension()>
        Public Function IsSingleLineExecutableBlock(executableBlock As SyntaxNode) As Boolean
            Select Case executableBlock.Kind
                Case SyntaxKind.SingleLineElseClause,
                     SyntaxKind.SingleLineIfStatement,
                     SyntaxKind.SingleLineSubLambdaExpression
                    Return executableBlock.GetExecutableBlockStatements().Count = 1
                Case Else
                    Return False
            End Select
        End Function
 
        <Extension()>
        Public Function IsMultiLineExecutableBlock(node As SyntaxNode) As Boolean
            Return node.IsExecutableBlock AndAlso Not node.IsSingleLineExecutableBlock
        End Function
 
        Private Sub UpdateStatements(executableBlock As SyntaxNode,
                                     newStatements As SyntaxList(Of StatementSyntax),
                                     annotations As SyntaxAnnotation(),
                                     ByRef singleLineIf As SingleLineIfStatementSyntax,
                                     ByRef multiLineIf As MultiLineIfBlockSyntax)
 
            Dim ifStatements As SyntaxList(Of StatementSyntax)
            Dim elseStatements As SyntaxList(Of StatementSyntax)
 
            Select Case executableBlock.Kind
                Case SyntaxKind.SingleLineIfStatement
                    singleLineIf = DirectCast(executableBlock, SingleLineIfStatementSyntax)
                    ifStatements = newStatements
                    elseStatements = If(singleLineIf.ElseClause Is Nothing, Nothing, singleLineIf.ElseClause.Statements)
                Case SyntaxKind.SingleLineElseClause
                    singleLineIf = DirectCast(executableBlock.Parent, SingleLineIfStatementSyntax)
                    ifStatements = singleLineIf.Statements
                    elseStatements = newStatements
                Case Else
                    Return
            End Select
 
            Dim ifStatement = SyntaxFactory.IfStatement(singleLineIf.IfKeyword, singleLineIf.Condition, singleLineIf.ThenKeyword) _
                                           .WithPrependedLeadingTrivia(SyntaxFactory.ElasticMarker).WithAppendedTrailingTrivia(SyntaxFactory.ElasticMarker)
 
            Dim elseBlockOpt = If(singleLineIf.ElseClause Is Nothing,
                                  Nothing,
                                  SyntaxFactory.ElseBlock(
                                                    SyntaxFactory.ElseStatement(singleLineIf.ElseClause.ElseKeyword).WithAppendedTrailingTrivia(SyntaxFactory.ElasticMarker),
                                                    elseStatements) _
                                               .WithPrependedLeadingTrivia(SyntaxFactory.ElasticMarker).WithAppendedTrailingTrivia(SyntaxFactory.ElasticMarker))
 
            Dim [endIf] = SyntaxFactory.EndIfStatement(SyntaxFactory.Token(SyntaxKind.EndKeyword), SyntaxFactory.Token(SyntaxKind.IfKeyword)) _
                                       .WithAdditionalAnnotations(annotations)
 
            multiLineIf = SyntaxFactory.MultiLineIfBlock(
                                            SyntaxFactory.IfStatement(singleLineIf.IfKeyword, singleLineIf.Condition, singleLineIf.ThenKeyword) _
                                                         .WithPrependedLeadingTrivia(SyntaxFactory.ElasticMarker).WithAppendedTrailingTrivia(SyntaxFactory.ElasticMarker),
                                            ifStatements,
                                            Nothing,
                                            elseBlockOpt,
                                            [endIf]) _
                                       .WithAdditionalAnnotations(annotations)
        End Sub
 
        <Extension()>
        Public Function ConvertSingleLineToMultiLineExecutableBlock(
                tree As SyntaxTree,
                block As SyntaxNode,
                statements As SyntaxList(Of StatementSyntax),
                ParamArray annotations As SyntaxAnnotation()) As SyntaxNode
 
            Dim oldBlock = block
            Dim newBlock = block
 
            Dim current = block
            While current.IsSingleLineExecutableBlock()
                If current.Kind = SyntaxKind.SingleLineIfStatement OrElse current.Kind = SyntaxKind.SingleLineElseClause Then
                    Dim singleLineIf As SingleLineIfStatementSyntax = Nothing
                    Dim multiLineIf As MultiLineIfBlockSyntax = Nothing
                    UpdateStatements(current, statements, annotations, singleLineIf, multiLineIf)
 
                    statements = SyntaxFactory.List({DirectCast(multiLineIf, StatementSyntax)})
 
                    current = singleLineIf.Parent
 
                    oldBlock = singleLineIf
                    newBlock = multiLineIf
                ElseIf current.Kind = SyntaxKind.SingleLineSubLambdaExpression Then
                    Dim singleLineLambda = DirectCast(current, SingleLineLambdaExpressionSyntax)
                    Dim multiLineLambda = SyntaxFactory.MultiLineSubLambdaExpression(
                        singleLineLambda.SubOrFunctionHeader,
                        statements,
                        SyntaxFactory.EndSubStatement()).WithAdditionalAnnotations(annotations)
 
                    oldBlock = singleLineLambda
                    newBlock = multiLineLambda
 
                    Exit While
                Else
                    Exit While
                End If
            End While
 
            Return tree.GetRoot().ReplaceNode(oldBlock, newBlock)
        End Function
 
        <Extension()>
        Public Function GetBraces(node As SyntaxNode) As (openBrace As SyntaxToken, closeBrace As SyntaxToken)
            Return node.TypeSwitch(
                Function(n As TypeParameterMultipleConstraintClauseSyntax) (n.OpenBraceToken, n.CloseBraceToken),
                Function(n As ObjectMemberInitializerSyntax) (n.OpenBraceToken, n.CloseBraceToken),
                Function(n As CollectionInitializerSyntax) (n.OpenBraceToken, n.CloseBraceToken),
                Function(n As SyntaxNode) CType(Nothing, (SyntaxToken, SyntaxToken)))
        End Function
 
        <Extension()>
        Public Function GetParentheses(node As SyntaxNode) As ValueTuple(Of SyntaxToken, SyntaxToken)
            Return node.TypeSwitch(
                Function(n As TypeParameterListSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As ParameterListSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As ArrayRankSpecifierSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As ParenthesizedExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As GetTypeExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As GetXmlNamespaceExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As CTypeExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As DirectCastExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As TryCastExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As PredefinedCastExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As BinaryConditionalExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As TernaryConditionalExpressionSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As ArgumentListSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As FunctionAggregationSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As TypeArgumentListSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As ExternalSourceDirectiveTriviaSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As ExternalChecksumDirectiveTriviaSyntax) (n.OpenParenToken, n.CloseParenToken),
                Function(n As SyntaxNode) CType(Nothing, (SyntaxToken, SyntaxToken)))
        End Function
 
        <Extension>
        Public Function IsLeftSideOfSimpleAssignmentStatement(node As SyntaxNode) As Boolean
            Return node.IsParentKind(SyntaxKind.SimpleAssignmentStatement) AndAlso
                DirectCast(node.Parent, AssignmentStatementSyntax).Left Is node
        End Function
 
        <Extension>
        Public Function IsLeftSideOfAnyAssignmentStatement(node As SyntaxNode) As Boolean
            Return node IsNot Nothing AndAlso
                node.Parent.IsAnyAssignmentStatement() AndAlso
                DirectCast(node.Parent, AssignmentStatementSyntax).Left Is node
        End Function
 
        <Extension>
        Public Function IsAnyAssignmentStatement(node As SyntaxNode) As Boolean
            Return node IsNot Nothing AndAlso
                SyntaxFacts.IsAssignmentStatement(node.Kind)
        End Function
 
        <Extension>
        Public Function IsLeftSideOfCompoundAssignmentStatement(node As SyntaxNode) As Boolean
            Return node IsNot Nothing AndAlso
                node.Parent.IsCompoundAssignmentStatement() AndAlso
                DirectCast(node.Parent, AssignmentStatementSyntax).Left Is node
        End Function
 
        <Extension>
        Public Function IsCompoundAssignmentStatement(node As SyntaxNode) As Boolean
            If node IsNot Nothing Then
                Select Case node.Kind
                    Case SyntaxKind.AddAssignmentStatement,
                         SyntaxKind.SubtractAssignmentStatement,
                         SyntaxKind.MultiplyAssignmentStatement,
                         SyntaxKind.DivideAssignmentStatement,
                         SyntaxKind.IntegerDivideAssignmentStatement,
                         SyntaxKind.ExponentiateAssignmentStatement,
                         SyntaxKind.LeftShiftAssignmentStatement,
                         SyntaxKind.RightShiftAssignmentStatement,
                         SyntaxKind.ConcatenateAssignmentStatement
                        Return True
                End Select
            End If
 
            Return False
        End Function
 
        <Extension>
        Public Function ParentingNodeContainsDiagnostics(node As SyntaxNode) As Boolean
            Dim topMostStatement = node _
                .AncestorsAndSelf() _
                .OfType(Of ExecutableStatementSyntax) _
                .LastOrDefault()
 
            If topMostStatement IsNot Nothing Then
                Return topMostStatement.ContainsDiagnostics
            End If
 
            Dim topMostExpression = node _
                .AncestorsAndSelf() _
                .TakeWhile(Function(n) Not TypeOf n Is StatementSyntax) _
                .OfType(Of ExpressionSyntax) _
                .LastOrDefault()
 
            If topMostExpression.Parent IsNot Nothing Then
                Return topMostExpression.Parent.ContainsDiagnostics
            End If
 
            Return False
        End Function
 
        <Extension()>
        Public Function CheckTopLevel(node As SyntaxNode, span As TextSpan) As Boolean
            Dim block = TryCast(node, MethodBlockBaseSyntax)
            If block IsNot Nothing AndAlso block.ContainsInMethodBlockBody(span) Then
                Return True
            End If
 
            Dim field = TryCast(node, FieldDeclarationSyntax)
            If field IsNot Nothing Then
                For Each declaration In field.Declarators
                    If declaration.Initializer IsNot Nothing AndAlso declaration.Initializer.Span.Contains(span) Then
                        Return True
                    End If
                Next
            End If
 
            Dim [property] = TryCast(node, PropertyStatementSyntax)
            If [property] IsNot Nothing AndAlso [property].Initializer IsNot Nothing AndAlso [property].Initializer.Span.Contains(span) Then
                Return True
            End If
 
            Return False
        End Function
 
        <Extension()>
        Public Function ContainsInMethodBlockBody(block As MethodBlockBaseSyntax, textSpan As TextSpan) As Boolean
            If block Is Nothing Then
                Return False
            End If
 
            Dim blockSpan = TextSpan.FromBounds(block.BlockStatement.Span.End, block.EndBlockStatement.SpanStart)
            Return blockSpan.Contains(textSpan)
        End Function
 
        <Extension()>
        Public Function GetMembers(node As SyntaxNode) As SyntaxList(Of StatementSyntax)
            Dim compilation = TryCast(node, CompilationUnitSyntax)
            If compilation IsNot Nothing Then
                Return compilation.Members
            End If
 
            Dim [namespace] = TryCast(node, NamespaceBlockSyntax)
            If [namespace] IsNot Nothing Then
                Return [namespace].Members
            End If
 
            Dim type = TryCast(node, TypeBlockSyntax)
            If type IsNot Nothing Then
                Return type.Members
            End If
 
            Dim [enum] = TryCast(node, EnumBlockSyntax)
            If [enum] IsNot Nothing Then
                Return [enum].Members
            End If
 
            Return Nothing
        End Function
 
        <Extension()>
        Public Function GetBodies(node As SyntaxNode) As IEnumerable(Of SyntaxNode)
            Dim method = TryCast(node, MethodBlockBaseSyntax)
            If method IsNot Nothing Then
                Return method.Statements
            End If
 
            Dim [event] = TryCast(node, EventBlockSyntax)
            If [event] IsNot Nothing Then
                Return [event].Accessors.SelectMany(Function(a) a.Statements)
            End If
 
            Dim [property] = TryCast(node, PropertyBlockSyntax)
            If [property] IsNot Nothing Then
                Return [property].Accessors.SelectMany(Function(a) a.Statements)
            End If
 
            Dim field = TryCast(node, FieldDeclarationSyntax)
            If field IsNot Nothing Then
                Return field.Declarators.Where(Function(d) d.Initializer IsNot Nothing).Select(Function(d) d.Initializer.Value).WhereNotNull()
            End If
 
            Dim initializer As EqualsValueSyntax = Nothing
            Dim [enum] = TryCast(node, EnumMemberDeclarationSyntax)
            If [enum] IsNot Nothing Then
                initializer = [enum].Initializer
            End If
 
            Dim propStatement = TryCast(node, PropertyStatementSyntax)
            If propStatement IsNot Nothing Then
                initializer = propStatement.Initializer
            End If
 
            If initializer IsNot Nothing AndAlso initializer.Value IsNot Nothing Then
                Return SpecializedCollections.SingletonEnumerable(initializer.Value)
            End If
 
            Return SpecializedCollections.EmptyEnumerable(Of SyntaxNode)()
        End Function
 
        <Extension()>
        Public Iterator Function GetAliasImportsClauses(root As CompilationUnitSyntax) As IEnumerable(Of SimpleImportsClauseSyntax)
            For i = 0 To root.Imports.Count - 1
                Dim statement = root.Imports(i)
 
                For j = 0 To statement.ImportsClauses.Count - 1
                    Dim importsClause = statement.ImportsClauses(j)
 
                    If importsClause.Kind = SyntaxKind.SimpleImportsClause Then
                        Dim simpleImportsClause = DirectCast(importsClause, SimpleImportsClauseSyntax)
 
                        If simpleImportsClause.Alias IsNot Nothing Then
                            Yield simpleImportsClause
                        End If
                    End If
                Next
            Next
        End Function
 
        <Extension>
        Friend Function GetParentConditionalAccessExpression(node As ExpressionSyntax) As ConditionalAccessExpressionSyntax
            ' Walk upwards based on the grammar/parser rules around ?. expressions (can be seen in
            ' ParseExpression.vb (.ParsePostFixExpression).
            '
            ' These are the parts of the expression that the ?... expression can end with.  Specifically
            '
            '  1.      x?.y.M()             // invocation
            '  2.      x?.y and x?.y.z      // member access (covered under MemberAccessExpressionSyntax below)
            '  3.      x?!y                 // dictionary access (covered under MemberAccessExpressionSyntax below)
            '  4.      x?.y<...>            // xml access
 
            If node.IsAnyMemberAccessExpressionName() Then
                node = DirectCast(node.Parent, ExpressionSyntax)
            End If
 
            ' Effectively, if we're on the RHS of the ? we have to walk up the RHS spine first until we hit the first
            ' conditional access.
 
            While (TypeOf node Is InvocationExpressionSyntax OrElse
                   TypeOf node Is MemberAccessExpressionSyntax OrElse
                   TypeOf node Is XmlMemberAccessExpressionSyntax) AndAlso
                   TypeOf node.Parent IsNot ConditionalAccessExpressionSyntax
 
                node = TryCast(node.Parent, ExpressionSyntax)
            End While
 
            ' Two cases we have to care about
            '
            '      1. a?.b.$$c.d        And
            '      2. a?.b.$$c.d?.e...
            '
            ' Note that `a?.b.$$c.d?.e.f?.g.h.i` falls into the same bucket as two.  i.e. the parts after `.e` are
            ' lower in the tree And are Not seen as we walk upwards.
            '
            '
            ' To get the root ?. (the one after the `a`) we have to potentially consume the first ?. on the RHS of the
            ' right spine (i.e. the one after `d`).  Once we do this, we then see if that itself Is on the RHS of a
            ' another conditional, And if so we hten return the one on the left.  i.e. for '2' this goes in this direction:
            '
            '      a?.b.$$c.d?.e            // it will do:
            '           ----->
            '       <---------
            '
            ' Note that this only one CAE consumption on both sides.  GetRootConditionalAccessExpression can be used to
            ' get the root parent in a case Like:
            '
            '      x?.y?.z?.a?.b.$$c.d?.e.f?.g.h.i              // It will do:
            '                    ----->
            '                <---------
            '             <---
            '          <---
            '       <---
 
            If TypeOf node?.Parent Is ConditionalAccessExpressionSyntax AndAlso
               DirectCast(node.Parent, ConditionalAccessExpressionSyntax).Expression Is node Then
 
                node = DirectCast(node.Parent, ExpressionSyntax)
            End If
 
            If TypeOf node?.Parent Is ConditionalAccessExpressionSyntax AndAlso
               DirectCast(node.Parent, ConditionalAccessExpressionSyntax).WhenNotNull Is node Then
 
                node = DirectCast(node.Parent, ExpressionSyntax)
            End If
 
            Return TryCast(node, ConditionalAccessExpressionSyntax)
        End Function
 
        ''' <summary>
        ''' <see cref="ISyntaxFacts.GetRootConditionalAccessExpression"/>
        ''' </summary>
        <Extension>
        Friend Function GetRootConditionalAccessExpression(node As ExpressionSyntax) As ConditionalAccessExpressionSyntax
            ' Once we've walked up the entire RHS, now we continually walk up the conditional accesses until we're at
            ' the root. For example, if we have `a?.b` And we're on the `.b`, this will give `a?.b`.  Similarly with
            ' `a?.b?.c` if we're on either `.b` or `.c` this will result in `a?.b?.c` (i.e. the root of this CAE
            ' sequence).
 
            node = node.GetParentConditionalAccessExpression()
            While TypeOf node?.Parent Is ConditionalAccessExpressionSyntax
                Dim conditionalParent = DirectCast(node.Parent, ConditionalAccessExpressionSyntax)
                If conditionalParent.WhenNotNull Is node Then
                    node = conditionalParent
                Else
                    Exit While
                End If
            End While
 
            Return TryCast(node, ConditionalAccessExpressionSyntax)
        End Function
 
        <Extension>
        Public Function IsInExpressionTree(node As SyntaxNode,
                                           semanticModel As SemanticModel,
                                           expressionTypeOpt As INamedTypeSymbol,
                                           cancellationToken As CancellationToken) As Boolean
 
            If expressionTypeOpt IsNot Nothing Then
                Dim current = node
                While current IsNot Nothing
                    If SyntaxFacts.IsSingleLineLambdaExpression(current.Kind) OrElse
                       SyntaxFacts.IsMultiLineLambdaExpression(current.Kind) Then
                        Dim typeInfo = semanticModel.GetTypeInfo(current, cancellationToken)
                        If expressionTypeOpt.Equals(typeInfo.ConvertedType?.OriginalDefinition) Then
                            Return True
                        End If
                    ElseIf TypeOf current Is OrderingSyntax OrElse
                           TypeOf current Is QueryClauseSyntax OrElse
                           TypeOf current Is FunctionAggregationSyntax OrElse
                           TypeOf current Is ExpressionRangeVariableSyntax Then
 
                        Dim info = semanticModel.GetSymbolInfo(current, cancellationToken)
                        For Each symbol In info.GetAllSymbols()
                            Dim method = TryCast(symbol, IMethodSymbol)
 
                            If method IsNot Nothing AndAlso
                               method.Parameters.Length > 0 AndAlso
                               expressionTypeOpt.Equals(method.Parameters(0).Type.OriginalDefinition) Then
 
                                Return True
                            End If
                        Next
                    End If
 
                    current = current.Parent
                End While
            End If
 
            Return False
        End Function
 
        <Extension>
        Public Function GetParameterList(declaration As SyntaxNode) As ParameterListSyntax
            Select Case declaration.Kind
                Case SyntaxKind.SubBlock,
                    SyntaxKind.FunctionBlock
                    Return DirectCast(declaration, MethodBlockSyntax).BlockStatement.ParameterList
                Case SyntaxKind.ConstructorBlock
                    Return DirectCast(declaration, ConstructorBlockSyntax).BlockStatement.ParameterList
                Case SyntaxKind.OperatorBlock
                    Return DirectCast(declaration, OperatorBlockSyntax).BlockStatement.ParameterList
                Case SyntaxKind.SubStatement,
                    SyntaxKind.FunctionStatement
                    Return DirectCast(declaration, MethodStatementSyntax).ParameterList
                Case SyntaxKind.SubNewStatement
                    Return DirectCast(declaration, SubNewStatementSyntax).ParameterList
                Case SyntaxKind.OperatorStatement
                    Return DirectCast(declaration, OperatorStatementSyntax).ParameterList
                Case SyntaxKind.DeclareSubStatement,
                    SyntaxKind.DeclareFunctionStatement
                    Return DirectCast(declaration, DeclareStatementSyntax).ParameterList
                Case SyntaxKind.DelegateSubStatement,
                    SyntaxKind.DelegateFunctionStatement
                    Return DirectCast(declaration, DelegateStatementSyntax).ParameterList
                Case SyntaxKind.PropertyBlock
                    Return DirectCast(declaration, PropertyBlockSyntax).PropertyStatement.ParameterList
                Case SyntaxKind.PropertyStatement
                    Return DirectCast(declaration, PropertyStatementSyntax).ParameterList
                Case SyntaxKind.EventBlock
                    Return DirectCast(declaration, EventBlockSyntax).EventStatement.ParameterList
                Case SyntaxKind.EventStatement
                    Return DirectCast(declaration, EventStatementSyntax).ParameterList
                Case SyntaxKind.MultiLineFunctionLambdaExpression,
                     SyntaxKind.MultiLineSubLambdaExpression
                    Return DirectCast(declaration, MultiLineLambdaExpressionSyntax).SubOrFunctionHeader.ParameterList
                Case SyntaxKind.SingleLineFunctionLambdaExpression,
                     SyntaxKind.SingleLineSubLambdaExpression
                    Return DirectCast(declaration, SingleLineLambdaExpressionSyntax).SubOrFunctionHeader.ParameterList
                Case Else
                    Return Nothing
            End Select
        End Function
 
        <Extension>
        Public Function GetAttributeLists(node As SyntaxNode) As SyntaxList(Of AttributeListSyntax)
            Select Case node.Kind
                Case SyntaxKind.CompilationUnit
                    Return SyntaxFactory.List(DirectCast(node, CompilationUnitSyntax).Attributes.SelectMany(Function(s) s.AttributeLists))
                Case SyntaxKind.ClassBlock
                    Return DirectCast(node, ClassBlockSyntax).BlockStatement.AttributeLists
                Case SyntaxKind.ClassStatement
                    Return DirectCast(node, ClassStatementSyntax).AttributeLists
                Case SyntaxKind.StructureBlock
                    Return DirectCast(node, StructureBlockSyntax).BlockStatement.AttributeLists
                Case SyntaxKind.StructureStatement
                    Return DirectCast(node, StructureStatementSyntax).AttributeLists
                Case SyntaxKind.InterfaceBlock
                    Return DirectCast(node, InterfaceBlockSyntax).BlockStatement.AttributeLists
                Case SyntaxKind.InterfaceStatement
                    Return DirectCast(node, InterfaceStatementSyntax).AttributeLists
                Case SyntaxKind.EnumBlock
                    Return DirectCast(node, EnumBlockSyntax).EnumStatement.AttributeLists
                Case SyntaxKind.EnumStatement
                    Return DirectCast(node, EnumStatementSyntax).AttributeLists
                Case SyntaxKind.EnumMemberDeclaration
                    Return DirectCast(node, EnumMemberDeclarationSyntax).AttributeLists
                Case SyntaxKind.DelegateFunctionStatement,
                     SyntaxKind.DelegateSubStatement
                    Return DirectCast(node, DelegateStatementSyntax).AttributeLists
                Case SyntaxKind.FieldDeclaration
                    Return DirectCast(node, FieldDeclarationSyntax).AttributeLists
                Case SyntaxKind.FunctionBlock,
                     SyntaxKind.SubBlock,
                     SyntaxKind.ConstructorBlock
                    Return DirectCast(node, MethodBlockBaseSyntax).BlockStatement.AttributeLists
                Case SyntaxKind.FunctionStatement,
                     SyntaxKind.SubStatement
                    Return DirectCast(node, MethodStatementSyntax).AttributeLists
                Case SyntaxKind.SubNewStatement
                    Return DirectCast(node, SubNewStatementSyntax).AttributeLists
                Case SyntaxKind.Parameter
                    Return DirectCast(node, ParameterSyntax).AttributeLists
                Case SyntaxKind.PropertyBlock
                    Return DirectCast(node, PropertyBlockSyntax).PropertyStatement.AttributeLists
                Case SyntaxKind.PropertyStatement
                    Return DirectCast(node, PropertyStatementSyntax).AttributeLists
                Case SyntaxKind.OperatorBlock
                    Return DirectCast(node, OperatorBlockSyntax).BlockStatement.AttributeLists
                Case SyntaxKind.OperatorStatement
                    Return DirectCast(node, OperatorStatementSyntax).AttributeLists
                Case SyntaxKind.EventBlock
                    Return DirectCast(node, EventBlockSyntax).EventStatement.AttributeLists
                Case SyntaxKind.EventStatement
                    Return DirectCast(node, EventStatementSyntax).AttributeLists
                Case SyntaxKind.GetAccessorBlock,
                    SyntaxKind.SetAccessorBlock,
                    SyntaxKind.AddHandlerAccessorBlock,
                    SyntaxKind.RemoveHandlerAccessorBlock,
                    SyntaxKind.RaiseEventAccessorBlock
                    Return DirectCast(node, AccessorBlockSyntax).AccessorStatement.AttributeLists
                Case SyntaxKind.GetAccessorStatement,
                    SyntaxKind.SetAccessorStatement,
                    SyntaxKind.AddHandlerAccessorStatement,
                    SyntaxKind.RemoveHandlerAccessorStatement,
                    SyntaxKind.RaiseEventAccessorStatement
                    Return DirectCast(node, AccessorStatementSyntax).AttributeLists
                Case Else
                    Return Nothing
            End Select
        End Function
 
        ''' <summary>
        ''' If "node" is the begin statement of a declaration block, return that block, otherwise
        ''' return node.
        ''' </summary>
        <Extension>
        Public Function GetBlockFromBegin(node As SyntaxNode) As SyntaxNode
            Dim parent As SyntaxNode = node.Parent
            Dim begin As SyntaxNode = Nothing
 
            If parent IsNot Nothing Then
                Select Case parent.Kind
                    Case SyntaxKind.NamespaceBlock
                        begin = DirectCast(parent, NamespaceBlockSyntax).NamespaceStatement
 
                    Case SyntaxKind.ModuleBlock, SyntaxKind.StructureBlock, SyntaxKind.InterfaceBlock, SyntaxKind.ClassBlock
                        begin = DirectCast(parent, TypeBlockSyntax).BlockStatement
 
                    Case SyntaxKind.EnumBlock
                        begin = DirectCast(parent, EnumBlockSyntax).EnumStatement
 
                    Case SyntaxKind.SubBlock, SyntaxKind.FunctionBlock, SyntaxKind.ConstructorBlock,
                         SyntaxKind.OperatorBlock, SyntaxKind.GetAccessorBlock, SyntaxKind.SetAccessorBlock,
                         SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock, SyntaxKind.RaiseEventAccessorBlock
                        begin = DirectCast(parent, MethodBlockBaseSyntax).BlockStatement
 
                    Case SyntaxKind.PropertyBlock
                        begin = DirectCast(parent, PropertyBlockSyntax).PropertyStatement
 
                    Case SyntaxKind.EventBlock
                        begin = DirectCast(parent, EventBlockSyntax).EventStatement
 
                    Case SyntaxKind.VariableDeclarator
                        If DirectCast(parent, VariableDeclaratorSyntax).Names.Count = 1 Then
                            begin = node
                        End If
                End Select
            End If
 
            If begin Is node Then
                Return parent
            Else
                Return node
            End If
        End Function
 
        <Extension>
        Public Function GetDeclarationBlockFromBegin(node As DeclarationStatementSyntax) As DeclarationStatementSyntax
            Dim parent As SyntaxNode = node.Parent
            Dim begin As SyntaxNode = Nothing
 
            If parent IsNot Nothing Then
                Select Case parent.Kind
                    Case SyntaxKind.NamespaceBlock
                        begin = DirectCast(parent, NamespaceBlockSyntax).NamespaceStatement
 
                    Case SyntaxKind.ModuleBlock, SyntaxKind.StructureBlock, SyntaxKind.InterfaceBlock, SyntaxKind.ClassBlock
                        begin = DirectCast(parent, TypeBlockSyntax).BlockStatement
 
                    Case SyntaxKind.EnumBlock
                        begin = DirectCast(parent, EnumBlockSyntax).EnumStatement
 
                    Case SyntaxKind.SubBlock, SyntaxKind.FunctionBlock, SyntaxKind.ConstructorBlock,
                         SyntaxKind.OperatorBlock, SyntaxKind.GetAccessorBlock, SyntaxKind.SetAccessorBlock,
                         SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock, SyntaxKind.RaiseEventAccessorBlock
                        begin = DirectCast(parent, MethodBlockBaseSyntax).BlockStatement
 
                    Case SyntaxKind.PropertyBlock
                        begin = DirectCast(parent, PropertyBlockSyntax).PropertyStatement
 
                    Case SyntaxKind.EventBlock
                        begin = DirectCast(parent, EventBlockSyntax).EventStatement
                End Select
            End If
 
            If begin Is node Then
                ' Every one of these parent casts is of a subtype of DeclarationStatementSyntax
                ' So if the cast worked above, it will work here
                Return DirectCast(parent, DeclarationStatementSyntax)
            Else
                Return node
            End If
        End Function
    End Module
End Namespace