| File: EditAndContinue\SyntaxUtilities.vb | Web Access |
| Project: src\roslyn\src\Features\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Features.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features) |
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.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