|
' 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.Generic
Imports System.Diagnostics
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic
''' <summary>
''' A region analysis walker that records jumps out of the region.
''' </summary>
''' <remarks></remarks>
Friend Class ExitPointsWalker
Inherits AbstractRegionControlFlowPass
Friend Overloads Shared Function Analyze(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo) As IEnumerable(Of StatementSyntax)
Dim walker = New ExitPointsWalker(info, region)
Try
Return If(walker.Analyze(), walker._branchesOutOf.ToImmutable(), SpecializedCollections.EmptyEnumerable(Of StatementSyntax)())
Finally
walker.Free()
End Try
End Function
Private _branchesOutOf As ArrayBuilder(Of StatementSyntax) = ArrayBuilder(Of StatementSyntax).GetInstance()
Private Overloads Function Analyze() As Boolean
' only one pass is needed.
Return Scan()
End Function
Private Sub New(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo)
MyBase.New(info, region)
End Sub
Protected Overrides Sub Free()
Me._branchesOutOf.Free()
Me._branchesOutOf = Nothing
Me._labelsInside.Free()
Me._labelsInside = Nothing
MyBase.Free()
End Sub
Private _labelsInside As ArrayBuilder(Of LabelSymbol) = ArrayBuilder(Of LabelSymbol).GetInstance()
Public Overrides Function VisitLabelStatement(node As BoundLabelStatement) As BoundNode
' The syntax can be a label or an end block statement when the label represents an exit
Dim syntax = node.Syntax
If IsInside Then
_labelsInside.Add(node.Label)
End If
Return MyBase.VisitLabelStatement(node)
End Function
Public Overrides Function VisitDoLoopStatement(node As BoundDoLoopStatement) As BoundNode
If IsInside Then
_labelsInside.Add(node.ExitLabel)
_labelsInside.Add(node.ContinueLabel)
End If
Return MyBase.VisitDoLoopStatement(node)
End Function
Public Overrides Function VisitForToStatement(node As BoundForToStatement) As BoundNode
If IsInside Then
_labelsInside.Add(node.ExitLabel)
_labelsInside.Add(node.ContinueLabel)
End If
Return MyBase.VisitForToStatement(node)
End Function
Public Overrides Function VisitForEachStatement(node As BoundForEachStatement) As BoundNode
If IsInside Then
_labelsInside.Add(node.ExitLabel)
_labelsInside.Add(node.ContinueLabel)
End If
Return MyBase.VisitForEachStatement(node)
End Function
Public Overrides Function VisitWhileStatement(node As BoundWhileStatement) As BoundNode
If IsInside Then
_labelsInside.Add(node.ExitLabel)
_labelsInside.Add(node.ContinueLabel)
End If
Return MyBase.VisitWhileStatement(node)
End Function
Public Overrides Function VisitSelectStatement(node As BoundSelectStatement) As BoundNode
If IsInside Then
_labelsInside.Add(node.ExitLabel)
End If
Return MyBase.VisitSelectStatement(node)
End Function
Protected Overrides Sub LeaveRegion()
' Process the pending returns only from this region.
For Each pending In Me.PendingBranches
If IsInsideRegion(pending.Branch.Syntax.Span) Then
Select Case pending.Branch.Kind
Case BoundKind.GotoStatement
If _labelsInside.Contains((TryCast((pending.Branch), BoundGotoStatement)).Label) Then
Continue For
End If
Case BoundKind.ExitStatement
If _labelsInside.Contains((TryCast((pending.Branch), BoundExitStatement)).Label) Then
Continue For
End If
Case BoundKind.ContinueStatement
If _labelsInside.Contains((TryCast((pending.Branch), BoundContinueStatement)).Label) Then
Continue For
End If
Case BoundKind.YieldStatement
Case BoundKind.ReturnStatement
' These are always included (we don't dive into lambda expressions)
Case Else
Throw ExceptionUtilities.UnexpectedValue(pending.Branch.Kind) ' there are no other branch statements
End Select
_branchesOutOf.Add(DirectCast(pending.Branch.Syntax, StatementSyntax))
End If
Next
MyBase.LeaveRegion()
End Sub
End Class
End Namespace
|