File: EditAndContinue\SyntaxUtilities.vb
Web Access
Project: src\src\Features\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Features.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.EditAndContinue
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
    Friend NotInheritable Class SyntaxUtilities
        Public Shared Function CreateLambdaBody(node As SyntaxNode) As LambdaBody
            Return New VisualBasicLambdaBody(node)
        End Function
 
        Public Shared Function TryGetDeclarationBody(node As SyntaxNode) As MemberBody
            Select Case node.Kind
                Case SyntaxKind.SubBlock,
                    SyntaxKind.FunctionBlock,
                    SyntaxKind.ConstructorBlock,
                    SyntaxKind.OperatorBlock,
                    SyntaxKind.GetAccessorBlock,
                    SyntaxKind.SetAccessorBlock,
                    SyntaxKind.AddHandlerAccessorBlock,
                    SyntaxKind.RemoveHandlerAccessorBlock,
                    SyntaxKind.RaiseEventAccessorBlock
                    Return New MethodBody(DirectCast(node, MethodBlockBaseSyntax))
 
                Case SyntaxKind.PropertyStatement
                    Dim propertyStatement = DirectCast(node, PropertyStatementSyntax)
                    If propertyStatement.Initializer IsNot Nothing Then
                        Return New PropertyWithInitializerDeclarationBody(propertyStatement)
                    End If
 
                    If HasAsNewClause(propertyStatement) Then
                        Return New PropertyWithNewClauseDeclarationBody(propertyStatement)
                    End If
 
                    Return Nothing
 
                Case SyntaxKind.ModifiedIdentifier
                    Dim modifiedIdentifier = DirectCast(node, ModifiedIdentifierSyntax)
 
                    If Not node.IsParentKind(SyntaxKind.VariableDeclarator) Then
                        ' parameter
                        Return Nothing
                    End If
 
                    Dim variableDeclarator = DirectCast(node.Parent, VariableDeclaratorSyntax)
                    Dim fieldDeclaration = DirectCast(variableDeclarator.Parent, FieldDeclarationSyntax)
 
                    If fieldDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword) Then
                        Return Nothing
                    End If
 
                    Dim body As MemberBody = Nothing
 
                    If IsFieldDeclaration(modifiedIdentifier) Then
                        ' Dim a, b As New C()
                        If HasAsNewClause(variableDeclarator) Then
                            body = New FieldWithMultipleAsNewClauseDeclarationBody(modifiedIdentifier)
                        End If
 
                        ' Dim a(n), b(n) As Integer
                        If modifiedIdentifier.ArrayBounds IsNot Nothing Then
                            ' AsNew clause can be syntactically specified at the same time as array bounds can be  (it's a semantic error).
                            ' Guard against such case to maintain consistency and set body to Nothing in that case.
                            body = If(body Is Nothing, New FieldWithMultipleArrayBoundsDeclarationBody(modifiedIdentifier), Nothing)
                        End If
                    Else
                        If variableDeclarator.Initializer IsNot Nothing Then
                            ' Dim a = initializer
                            body = New FieldWithInitializerDeclarationBody(variableDeclarator)
                        ElseIf HasAsNewClause(variableDeclarator) Then
                            ' Dim a As New T
                            body = New FieldWithSingleAsNewClauseDeclarationBody(variableDeclarator)
                        End If
 
                        ' Dim a(n) As T
                        Dim name = variableDeclarator.Names(0)
 
                        If name.ArrayBounds IsNot Nothing Then
                            ' Initializer and AsNew clause can't be syntactically specified at the same time, but array bounds can be (it's a semantic error).
                            ' Guard against such case to maintain consistency and set body to Nothing in that case.
                            body = If(body Is Nothing, New FieldWithSingleArrayBoundsDeclarationBody(variableDeclarator), Nothing)
                        End If
                    End If
 
                    Return body
 
                Case Else
                    Return Nothing
            End Select
        End Function
 
        Public Shared Function HasAsNewClause(variableDeclarator As VariableDeclaratorSyntax) As Boolean
            Return variableDeclarator.AsClause IsNot Nothing AndAlso variableDeclarator.AsClause.IsKind(SyntaxKind.AsNewClause)
        End Function
 
        Public Shared Function HasAsNewClause(propertyStatement As PropertyStatementSyntax) As Boolean
            Return propertyStatement.AsClause IsNot Nothing AndAlso propertyStatement.AsClause.IsKind(SyntaxKind.AsNewClause)
        End Function
 
        ''' <summary>
        ''' Returns true if the <see cref="ModifiedIdentifierSyntax"/> node represents a field declaration.
        ''' </summary>
        Friend Shared Function IsFieldDeclaration(node As ModifiedIdentifierSyntax) As Boolean
            Return node.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) AndAlso DirectCast(node.Parent, VariableDeclaratorSyntax).Names.Count > 1
        End Function
 
        ''' <summary>
        ''' Returns true if the <see cref="VariableDeclaratorSyntax"/> node represents a field declaration.
        ''' </summary>
        Friend Shared Function IsFieldDeclaration(node As VariableDeclaratorSyntax) As Boolean
            Return node.Parent.IsKind(SyntaxKind.FieldDeclaration) AndAlso node.Names.Count = 1
        End Function
 
        Friend Shared Function GetArrayBoundsCapturedVariables(model As SemanticModel, arrayBounds As ArgumentListSyntax) As ImmutableArray(Of ISymbol)
            ' Edge case, no need to be efficient, currently there can either be no captured variables or just "Me".
            ' Dim a((Function(n) n + 1).Invoke(1), (Function(n) n + 2).Invoke(2)) As Integer
            Return ImmutableArray.CreateRange(
                    arrayBounds.Arguments.
                        SelectMany(AddressOf GetArgumentExpressions).
                        SelectMany(Function(expr) model.AnalyzeDataFlow(expr).CapturedInside).
                        Distinct())
        End Function
 
        Private Shared Iterator Function GetArgumentExpressions(argument As ArgumentSyntax) As IEnumerable(Of ExpressionSyntax)
            Select Case argument.Kind
                Case SyntaxKind.SimpleArgument
                    Yield DirectCast(argument, SimpleArgumentSyntax).Expression
 
                Case SyntaxKind.RangeArgument
                    Dim range = DirectCast(argument, RangeArgumentSyntax)
                    Yield range.LowerBound
                    Yield range.UpperBound
 
                Case SyntaxKind.OmittedArgument
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(argument.Kind)
            End Select
        End Function
 
        <Conditional("DEBUG")>
        Public Shared Sub AssertIsBody(syntax As SyntaxNode, allowLambda As Boolean)
            ' lambda/query
            If LambdaUtilities.IsLambdaBody(syntax) Then
                Debug.Assert(allowLambda)
                Debug.Assert(TypeOf syntax Is ExpressionSyntax OrElse TypeOf syntax Is LambdaHeaderSyntax)
                Return
            End If
 
            ' sub/function/ctor/operator/accessor
            If TypeOf syntax Is MethodBlockBaseSyntax Then
                Return
            End If
 
            ' field/property initializer
            If TypeOf syntax Is ExpressionSyntax Then
                If syntax.Parent.Kind = SyntaxKind.EqualsValue Then
                    If syntax.Parent.Parent.IsKind(SyntaxKind.PropertyStatement) Then
                        Return
                    End If
 
                    Debug.Assert(syntax.Parent.Parent.IsKind(SyntaxKind.VariableDeclarator))
                    Debug.Assert(syntax.Parent.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration))
                    Return
                ElseIf syntax.Parent.Kind = SyntaxKind.AsNewClause Then
                    If syntax.Parent.Parent.IsKind(SyntaxKind.PropertyStatement) Then
                        Return
                    End If
 
                    Debug.Assert(syntax.Parent.Parent.IsKind(SyntaxKind.VariableDeclarator))
                    Debug.Assert(syntax.Parent.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration))
                    Return
                End If
            End If
 
            ' field array initializer
            If TypeOf syntax Is ArgumentListSyntax Then
                Debug.Assert(syntax.Parent.IsKind(SyntaxKind.ModifiedIdentifier))
                Debug.Assert(syntax.Parent.Parent.IsKind(SyntaxKind.VariableDeclarator))
                Debug.Assert(syntax.Parent.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration))
                Return
            End If
 
            Throw ExceptionUtilities.Unreachable
        End Sub
 
        Public Shared Function GetBody(node As LambdaExpressionSyntax) As SyntaxList(Of SyntaxNode)
            Select Case node.Kind
                Case SyntaxKind.MultiLineFunctionLambdaExpression, SyntaxKind.MultiLineSubLambdaExpression
                    Return DirectCast(node, MultiLineLambdaExpressionSyntax).Statements
                Case SyntaxKind.SingleLineFunctionLambdaExpression, SyntaxKind.SingleLineSubLambdaExpression
                    Return SyntaxFactory.SingletonList(DirectCast(node, SingleLineLambdaExpressionSyntax).Body)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(node.Kind)
            End Select
        End Function
 
        Public Shared Function IsParameterlessConstructor(declaration As SyntaxNode) As Boolean
            If Not declaration.IsKind(SyntaxKind.ConstructorBlock) Then
                Return False
            End If
 
            Dim ctor = DirectCast(declaration, ConstructorBlockSyntax)
            Return ctor.BlockStatement.ParameterList.Parameters.Count = 0
        End Function
 
        Public Shared Function HasBackingField(propertyDeclaration As SyntaxNode) As Boolean
            Return propertyDeclaration.IsKind(SyntaxKind.PropertyStatement) AndAlso
                   Not propertyDeclaration.Parent.IsKind(SyntaxKind.PropertyBlock) AndAlso
                   Not DirectCast(propertyDeclaration, PropertyStatementSyntax).Modifiers.Any(SyntaxKind.MustOverrideKeyword)
        End Function
 
        Public Shared Function IsAsyncMethodOrLambda(declarationOrBody As SyntaxNode) As Boolean
            Return GetModifiers(declarationOrBody).Any(SyntaxKind.AsyncKeyword)
        End Function
 
        Public Shared Function IsIteratorMethodOrLambda(declaration As SyntaxNode) As Boolean
            Return GetModifiers(declaration).Any(SyntaxKind.IteratorKeyword)
        End Function
 
        Public Shared Function GetAwaitExpressions(body As SyntaxNode) As ImmutableArray(Of SyntaxNode)
            ' skip lambda bodies
            Return ImmutableArray.CreateRange(body.DescendantNodes(AddressOf LambdaUtilities.IsNotLambda).
                Where(Function(n) n.IsKind(SyntaxKind.AwaitExpression)))
        End Function
 
        Public Shared Function GetYieldStatements(body As SyntaxNode) As ImmutableArray(Of SyntaxNode)
            ' enumerate statements:
            Return ImmutableArray.CreateRange(body.DescendantNodes(Function(n) TypeOf n IsNot ExpressionSyntax).
                Where(Function(n) n.IsKind(SyntaxKind.YieldStatement)))
 
        End Function
 
        Public Shared Function GetModifiers(declarationOrBody As SyntaxNode) As SyntaxTokenList
            Select Case declarationOrBody.Kind
                Case SyntaxKind.SubBlock,
                     SyntaxKind.FunctionBlock
                    Return DirectCast(declarationOrBody, MethodBlockBaseSyntax).BlockStatement.Modifiers
 
                Case SyntaxKind.MultiLineFunctionLambdaExpression,
                     SyntaxKind.SingleLineFunctionLambdaExpression,
                     SyntaxKind.MultiLineSubLambdaExpression,
                     SyntaxKind.SingleLineSubLambdaExpression
                    Return DirectCast(declarationOrBody, LambdaExpressionSyntax).SubOrFunctionHeader.Modifiers
 
                Case SyntaxKind.FunctionLambdaHeader,
                     SyntaxKind.SubLambdaHeader
                    Return DirectCast(declarationOrBody, LambdaHeaderSyntax).Modifiers
            End Select
 
            Return Nothing
        End Function
    End Class
End Namespace