| File: Analysis\FlowAnalysis\DataFlowsOutWalker.vb | Web Access |
| Project: src\roslyn\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 System.Collections.Immutable 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 for ''' which their assigned values flow out of the region. ''' A variable assigned inside is used outside if an analysis that ''' treats assignments in the region as un-assigning the variable would ''' cause "unassigned" errors outside the region. ''' </summary> Friend Class DataFlowsOutWalker Inherits AbstractRegionDataFlowPass Private ReadOnly _dataFlowsIn As ImmutableArray(Of ISymbol) Private ReadOnly _originalUnassigned As HashSet(Of Symbol) Private ReadOnly _dataFlowsOut As New HashSet(Of Symbol)() #If DEBUG Then ' we'd like to ensure that only variables get returned in DataFlowsOut that were assigned to inside the region. Private ReadOnly _assignedInside As HashSet(Of Symbol) = New HashSet(Of Symbol)() #End If Private Sub New(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo, unassignedVariables As HashSet(Of Symbol), originalUnassigned As HashSet(Of Symbol), dataFlowsIn As ImmutableArray(Of ISymbol)) MyBase.New(info, region, unassignedVariables, trackUnassignments:=True, trackStructsWithIntrinsicTypedFields:=True) Me._dataFlowsIn = dataFlowsIn Me._originalUnassigned = originalUnassigned End Sub Friend Overloads Shared Function Analyze(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo, unassignedVariables As HashSet(Of Symbol), dataFlowsIn As ImmutableArray(Of ISymbol)) 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 DataFlowsOutWalker(info, region, unassignedWithoutStatic, unassignedVariables, dataFlowsIn) Try Dim success = walker.Analyze Dim result = walker._dataFlowsOut #If DEBUG Then ' Assert that DataFlowsOut only contains variables that were assigned to inside the region Debug.Assert(Not success OrElse Not result.Any(Function(variable) Not walker._assignedInside.Contains(variable))) #End If Return If(success, result, New HashSet(Of Symbol)()) Finally walker.Free() End Try End Function Protected Overrides Sub EnterRegion() ' to handle loops properly, we must assume that every variable (except static locals) ' that flows in is assigned at the beginning of the loop. If it isn't, then it must ' be in a loop and flow out of the region in that loop (and into the region inside the loop). For Each variable As Symbol In _dataFlowsIn Dim slot As Integer = Me.GetOrCreateSlot(variable) If Not Me.State.IsAssigned(slot) AndAlso variable.Kind <> SymbolKind.RangeVariable AndAlso (variable.Kind <> SymbolKind.Local OrElse Not DirectCast(variable, LocalSymbol).IsStatic) Then _dataFlowsOut.Add(variable) End If Next MyBase.EnterRegion() End Sub Protected Overrides Sub Assign(node As BoundNode, value As BoundExpression, Optional assigned As Boolean = True) If IsInside Then If assigned Then Dim symbol = GetNodeSymbol(node) If symbol IsNot Nothing Then #If DEBUG Then _assignedInside.Add(symbol) #End If End If End If ' assignments inside the region are recorded as un-assignments assigned = False ' any reachable assignment to a ref or out parameter can ' be visible to the caller in the face of exceptions. If Me.State.Reachable Then Select Case node.Kind Case BoundKind.Parameter, BoundKind.MeReference Dim expression = DirectCast(node, BoundExpression) Dim slots As SlotCollection = MakeSlotsForExpression(expression) Debug.Assert(slots.Count <= 1) If slots.Count > 0 Then Dim slot As Integer = slots(0) If slot >= SlotKind.FirstAvailable Then Dim exprIdentifier = DirectCast(variableBySlot(slot).Symbol, ParameterSymbol) If exprIdentifier IsNot Nothing AndAlso exprIdentifier.IsByRef Then _dataFlowsOut.Add(exprIdentifier) End If End If End If Case BoundKind.Local Dim local = DirectCast(node, BoundLocal) Dim locSymbol As LocalSymbol = local.LocalSymbol If locSymbol.IsStatic AndAlso WasUsedBeforeAssignment(locSymbol) Then Dim slots As SlotCollection = MakeSlotsForExpression(local) Debug.Assert(slots.Count <= 1) If slots.Count > 0 Then Dim slot As Integer = slots(0) If slot >= SlotKind.FirstAvailable Then Debug.Assert(locSymbol Is variableBySlot(slot).Symbol) _dataFlowsOut.Add(variableBySlot(slot).Symbol) End If End If End If End Select End If End If MyBase.Assign(node, value, assigned) 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 Not _dataFlowsOut.Contains(local) AndAlso local.Kind <> SymbolKind.RangeVariable AndAlso Not IsInside Then 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 _dataFlowsOut.Add(sym) End If Else _dataFlowsOut.Add(local) End If End If MyBase.ReportUnassigned(local, node, rwContext, slot, boundFieldAccess) End Sub Protected Overrides Sub ReportUnassignedByRefParameter(parameter As ParameterSymbol) _dataFlowsOut.Add(parameter) MyBase.ReportUnassignedByRefParameter(parameter) End Sub Protected Overrides Sub NoteWrite(variable As Symbol, value As BoundExpression) If Me.State.Reachable Then Dim isByRefParameter As Boolean = variable.Kind = SymbolKind.Parameter AndAlso DirectCast(variable, ParameterSymbol).IsByRef Dim isStaticLocal As Boolean = variable.Kind = SymbolKind.Local AndAlso DirectCast(variable, LocalSymbol).IsStatic If IsInside AndAlso (isByRefParameter OrElse isStaticLocal AndAlso WasUsedBeforeAssignment(variable)) Then _dataFlowsOut.Add(variable) #If DEBUG Then _assignedInside.Add(variable) #End If End If End If MyBase.NoteWrite(variable, value) End Sub Private Function WasUsedBeforeAssignment(sym As Symbol) As Boolean Return Me._originalUnassigned.Contains(sym) End Function Friend Overrides Sub AssignLocalOnDeclaration(local As LocalSymbol, node As BoundLocalDeclaration) If local.IsStatic Then ' We should pretend that all the static locals are ' initialized by previous method invocations Assign(node, node.InitializerOpt) Else MyBase.AssignLocalOnDeclaration(local, node) End If End Sub End Class End Namespace