File: Analysis\FlowAnalysis\DataFlowsInWalker.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.
 
Imports System.Collections.Generic
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    ''' <summary>
    ''' A region analysis walker that computes the set of variables whose values flow into (are used in)
    ''' the region.
    ''' A variable assigned outside is used inside if an analysis
    ''' that leaves the variable unassigned on entry to the region would cause the
    ''' generation of "unassigned" errors within the region.
    ''' </summary>
    Friend Class DataFlowsInWalker
        Inherits AbstractRegionDataFlowPass
 
        ' TODO: normalize the result by removing variables that are unassigned in an unmodified flow analysis.
        Private Sub New(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo, unassignedVariables As HashSet(Of Symbol))
            MyBase.New(info, region, unassignedVariables, trackStructsWithIntrinsicTypedFields:=True)
        End Sub
 
        Friend Overloads Shared Function Analyze(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo,
                                                 unassignedVariables As HashSet(Of Symbol),
                                                 ByRef succeeded As Boolean?,
                                                 ByRef invalidRegionDetected As Boolean) As HashSet(Of Symbol)
 
            ' remove static locals from unassigned, otherwise they will never reach ReportUnassigned(...)
            Dim unassignedWithoutStatic As New HashSet(Of Symbol)
            For Each var In unassignedVariables
                If var.Kind <> SymbolKind.Local OrElse Not DirectCast(var, LocalSymbol).IsStatic Then
                    unassignedWithoutStatic.Add(var)
                End If
            Next
 
            Dim walker = New DataFlowsInWalker(info, region, unassignedWithoutStatic)
            Try
                succeeded = walker.Analyze() AndAlso Not walker.InvalidRegionDetected
                invalidRegionDetected = walker.InvalidRegionDetected
                Return If(succeeded, walker._dataFlowsIn, New HashSet(Of Symbol)())
            Finally
                walker.Free()
            End Try
        End Function
 
        Private ReadOnly _dataFlowsIn As HashSet(Of Symbol) = New HashSet(Of Symbol)()
 
        Private Function ResetState(state As LocalState) As LocalState
            Dim unreachable As Boolean = Not state.Reachable
            state = ReachableState()
            If unreachable Then
                state.Assign(0)
            End If
            Return state
        End Function
 
        Protected Overrides Sub EnterRegion()
            Me.SetState(ResetState(Me.State))
            Me._dataFlowsIn.Clear()
            MyBase.EnterRegion()
        End Sub
 
        Protected Overrides Sub NoteBranch(pending As PendingBranch, stmt As BoundStatement, labelStmt As BoundLabelStatement)
            If stmt.Syntax IsNot Nothing AndAlso labelStmt.Syntax IsNot Nothing AndAlso Not IsInsideRegion(stmt.Syntax.Span) AndAlso IsInsideRegion(labelStmt.Syntax.Span) Then
                pending.State = ResetState(pending.State)
            End If
            MyBase.NoteBranch(pending, stmt, labelStmt)
        End Sub
 
        Public Overrides Function VisitRangeVariable(node As BoundRangeVariable) As BoundNode
            ' Sometimes query expressions refer to query range variable just to 
            ' copy its value to a new compound variable. There is no reference
            ' to the range variable in code and, from user point of view, there is
            ' no access to it.
            ' If and only if range variable is declared outside of the region and read inside, it flows in.
            If Not node.WasCompilerGenerated AndAlso
               IsInside AndAlso
               Not IsInsideRegion(node.RangeVariable.Syntax.Span) Then
 
                _dataFlowsIn.Add(node.RangeVariable)
            End If
 
            Return Nothing
        End Function
 
        Protected Overrides Sub VisitAmbiguousLocalSymbol(ambiguous As DataFlowPass.AmbiguousLocalsPseudoSymbol)
            MyBase.VisitAmbiguousLocalSymbol(ambiguous)
 
            ' Locals from ambiguous implicit receiver can only be unassigned in *REGION* flow analysis 
            ' if a new region starts after they are declared and before the implicit receiver is referenced; 
            ' region data flow analysis for such regions is prohibited and should return Succeeded = False.
 
            ' Check if the first local in the collection was 'unassigned' by entering a region, 
            ' in which case set a flag that the region is not valid
            If IsInside Then
                Dim firstLocal As LocalSymbol = ambiguous.Locals(0)
                If Not Me.State.IsAssigned(VariableSlot(firstLocal)) Then
                    Me.SetInvalidRegion()
                End If
            End If
        End Sub
 
        Protected Overrides Sub ReportUnassigned(local As Symbol,
                                                 node As SyntaxNode,
                                                 rwContext As ReadWriteContext,
                                                 Optional slot As Integer = SlotKind.NotTracked,
                                                 Optional boundFieldAccess As BoundFieldAccess = Nothing)
 
            Debug.Assert(local.Kind <> SymbolKind.Field OrElse boundFieldAccess IsNot Nothing)
 
            If IsInsideRegion(node.Span) Then
                Debug.Assert(local.Kind <> SymbolKind.RangeVariable)
 
                If local.Kind = SymbolKind.Field Then
                    Dim sym As Symbol = GetNodeSymbol(boundFieldAccess)
 
                    ' Unreachable for AmbiguousLocalsPseudoSymbol: ambiguous implicit 
                    ' receiver should not ever be considered unassigned
                    Debug.Assert(Not TypeOf sym Is AmbiguousLocalsPseudoSymbol)
 
                    If sym IsNot Nothing Then
                        _dataFlowsIn.Add(sym)
                    End If
 
                Else
                    _dataFlowsIn.Add(local)
                End If
            End If
 
            MyBase.ReportUnassigned(local, node, rwContext, slot, boundFieldAccess)
        End Sub
 
        Friend Overrides Sub AssignLocalOnDeclaration(local As LocalSymbol, node As BoundLocalDeclaration)
            ' NOTE: static locals should not be considered assigned even in presence of initializer
            If Not local.IsStatic Then
                MyBase.AssignLocalOnDeclaration(local, node)
            End If
        End Sub
 
    End Class
 
End Namespace