' 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 Microsoft.CodeAnalysis.PooledObjects
#If DEBUG Then
' We use a struct rather than a class to represent the state for efficiency
' for data flow analysis, with 32 bits of data inline. Merely copying the state
' variable causes the first 32 bits to be cloned, as they are inline. This can
' hide a plethora of errors that would only be exhibited in programs with more
' than 32 variables to be tracked. However, few of our tests have that many
' variables.
'
' To help diagnose these problems, we use the preprocessor symbol REFERENCE_STATE
' to cause the data flow state be a class rather than a struct. When it is a class,
' this category of problems would be exhibited in programs with a small number of
' tracked variables. But it is slower, so we only do it in DEBUG mode.
#Const REFERENCE_STATE = True
#End If

Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports System.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

#If REFERENCE_STATE Then
Imports OptionalState = Microsoft.CodeAnalysis.[Optional](Of Microsoft.CodeAnalysis.VisualBasic.DataFlowPass.LocalState)
#Else
Imports OptionalState = System.Nullable(Of Microsoft.CodeAnalysis.VisualBasic.DataFlowPass.LocalState)
#End If

Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend Class DataFlowPass
        Inherits AbstractFlowPass(Of LocalState)

        Public Enum SlotKind
            ''' <summary>
            '''  Special slot for untracked variables
            ''' </summary>
            NotTracked = -1
            ''' <summary>
            ''' Special slot for tracking whether code is reachable
            ''' </summary>
            Unreachable = 0
            ''' <summary>
            ''' Special slot for tracking the implicit local for the function return value
            ''' </summary>
            FunctionValue = 1
            ''' <summary>
            ''' The first available slot for variables
            ''' </summary>
            ''' <remarks></remarks>
            FirstAvailable = 2
        End Enum

        ''' <summary>
        ''' Some variables that should be considered initially assigned.  Used for region analysis.
        ''' </summary>
        Protected ReadOnly initiallyAssignedVariables As HashSet(Of Symbol)

        ''' <summary>
        ''' Defines whether or not fields of intrinsic type should be tracked. Such fields should 
        ''' not be tracked for error reporting purposes, but should be tracked for region flow analysis
        ''' </summary>
        Private ReadOnly _trackStructsWithIntrinsicTypedFields As Boolean

        ''' <summary>
        ''' Variables that were used anywhere, in the sense required to suppress warnings about unused variables.
        ''' </summary>
        Private ReadOnly _unusedVariables As HashSet(Of LocalSymbol) = New HashSet(Of LocalSymbol)()

        ''' <summary>
        ''' Variables that were initialized or written anywhere.
        ''' </summary>
        Private ReadOnly _writtenVariables As HashSet(Of Symbol) = New HashSet(Of Symbol)()

        ''' <summary> 
        ''' A mapping from local variables to the index of their slot in a flow analysis local state. 
        ''' WARNING: if variable identifier maps into SlotKind.NotTracked, it may mean that VariableIdentifier 
        '''          is a structure without traceable fields. This mapping is created in MakeSlotImpl(...)
        ''' </summary>
        Private ReadOnly _variableSlot As Dictionary(Of VariableIdentifier, Integer) = New Dictionary(Of VariableIdentifier, Integer)()

        ''' <summary>
        ''' A mapping from the local variable slot to the symbol for the local variable itself.  This is used in the
        ''' implementation of region analysis (support for extract method) to compute the set of variables "always
        ''' assigned" in a region of code.
        ''' </summary>
        Protected variableBySlot As VariableIdentifier() = New VariableIdentifier(10) {}

        ''' <summary>
        ''' Variable slots are allocated to local variables sequentially and never reused.  This is
        ''' the index of the next slot number to use.
        ''' </summary>
        Protected nextVariableSlot As Integer = SlotKind.FirstAvailable

        ''' <summary>
        ''' Tracks variables for which we have already reported a definite assignment error.  This
        ''' allows us to report at most one such error per variable.
        ''' </summary>
        Private _alreadyReported As BitVector

        ''' <summary>
        ''' Did we see [On Error] or [Resume] statement? Used to suppress some diagnostics.
        ''' </summary>
        Private _seenOnErrorOrResume As Boolean

        Protected _convertInsufficientExecutionStackExceptionToCancelledByStackGuardException As Boolean = False ' By default, just let the original exception to bubble up.

        Friend Sub New(info As FlowAnalysisInfo, suppressConstExpressionsSupport As Boolean, Optional trackStructsWithIntrinsicTypedFields As Boolean = False)
            MyBase.New(info, suppressConstExpressionsSupport)
            Me.initiallyAssignedVariables = Nothing
            Me._trackStructsWithIntrinsicTypedFields = trackStructsWithIntrinsicTypedFields
        End Sub

        Friend Sub New(info As FlowAnalysisInfo, region As FlowAnalysisRegionInfo,
                       suppressConstExpressionsSupport As Boolean,
                       Optional initiallyAssignedVariables As HashSet(Of Symbol) = Nothing,
                       Optional trackUnassignments As Boolean = False,
                       Optional trackStructsWithIntrinsicTypedFields As Boolean = False)

            MyBase.New(info, region, suppressConstExpressionsSupport, trackUnassignments)
            Me.initiallyAssignedVariables = initiallyAssignedVariables
            Me._trackStructsWithIntrinsicTypedFields = trackStructsWithIntrinsicTypedFields
        End Sub

        Protected Overrides Sub InitForScan()
            MyBase.InitForScan()

            For Each parameter In MethodParameters
                GetOrCreateSlot(parameter)
            Next

            Me._alreadyReported = BitVector.Empty          ' no variables yet reported unassigned
            Me.EnterParameters(MethodParameters)         ' with parameters assigned
            Dim methodMeParameter = Me.MeParameter       ' and with 'Me' assigned as well
            If methodMeParameter IsNot Nothing Then
                Me.GetOrCreateSlot(methodMeParameter)
                Me.EnterParameter(methodMeParameter)
            End If

            ' Usually we need to treat locals used without being assigned first as assigned in the beginning of the block.
            ' When data flow analysis determines that the variable is sometimes used without being assigned first, we want 
            ' to treat that variable, during region analysis, as assigned where it is introduced.
            ' However with goto statements that branch into for/foreach/using statements (error case) we need to treat them
            ' as assigned in the beginning of the method body.
            '
            ' Example:
            ' Goto label1
            ' For x = 0 to 10
            ' label1:
            '    Dim y = [| x |]  ' x should not be in dataFlowsOut.
            ' Next
            '
            ' This can only happen in VB because the labels are visible in the whole method. In C# the labels are not visible
            ' this case.            
            '
            If initiallyAssignedVariables IsNot Nothing Then
                For Each local In initiallyAssignedVariables
                    SetSlotAssigned(GetOrCreateSlot(local))
                Next
            End If
        End Sub

        Protected Overrides Function Scan() As Boolean
            If Not MyBase.Scan() Then
                Return False
            End If

            ' check unused variables
            If Not _seenOnErrorOrResume Then
                For Each local In _unusedVariables
                    ReportUnused(local)
                Next
            End If

            ' VB doesn't support "out" so it doesn't warn for unassigned parameters. However, check variables passed
            ' byref are assigned so that data flow analysis detects parameters flowing out.
            If ShouldAnalyzeByRefParameters Then
                LeaveParameters(MethodParameters)
            End If
            Dim methodMeParameter = Me.MeParameter       ' and also 'Me'
            If methodMeParameter IsNot Nothing Then
                Me.LeaveParameter(methodMeParameter)
            End If

            Return True
        End Function

        Private Sub ReportUnused(local As LocalSymbol)
            ' Never report that the function value is unused
            ' Never report that local with empty name is unused
            ' Locals with empty name can be generated in code with syntax errors and
            ' we shouldn't report such locals as unused because they were not explicitly declared
            ' by the user in the code
            If Not local.IsFunctionValue AndAlso Not String.IsNullOrEmpty(local.Name) Then
                If _writtenVariables.Contains(local) Then
                    If local.IsConst Then
                        Me.diagnostics.Add(ERRID.WRN_UnusedLocalConst, local.Locations(0), If(local.Name, "dummy"))
                    End If
                Else
                    Me.diagnostics.Add(ERRID.WRN_UnusedLocal, local.Locations(0), If(local.Name, "dummy"))
                End If
            End If
        End Sub

        Protected Overridable Sub ReportUnassignedByRefParameter(parameter As ParameterSymbol)
        End Sub

        ''' <summary>
        ''' Perform data flow analysis, reporting all necessary diagnostics.
        ''' </summary>
        Public Overloads Shared Sub Analyze(info As FlowAnalysisInfo, diagnostics As DiagnosticBag, suppressConstExpressionsSupport As Boolean)
            Dim walker = New DataFlowPass(info, suppressConstExpressionsSupport)

            If diagnostics IsNot Nothing Then
                walker._convertInsufficientExecutionStackExceptionToCancelledByStackGuardException = True
            End If

            Try
                Dim result As Boolean = walker.Analyze()
                Debug.Assert(result)
                If diagnostics IsNot Nothing Then
                    diagnostics.AddRange(walker.diagnostics)
                End If

            Catch ex As CancelledByStackGuardException When diagnostics IsNot Nothing
                ex.AddAnError(diagnostics)
            Finally
                walker.Free()
            End Try
        End Sub

        Protected Overrides Function ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException() As Boolean
            Return _convertInsufficientExecutionStackExceptionToCancelledByStackGuardException
        End Function

        Protected Overrides Sub Free()
            Me._alreadyReported = BitVector.Null
            MyBase.Free()
        End Sub

        Protected Overrides Function Dump(state As LocalState) As String
            Dim builder As New StringBuilder()
            builder.Append("[assigned ")
            AppendBitNames(state.Assigned, builder)
            builder.Append("]")
            Return builder.ToString()
        End Function

        Protected Sub AppendBitNames(a As BitVector, builder As StringBuilder)
            Dim any As Boolean = False
            For Each bit In a.TrueBits
                If any Then
                    builder.Append(", ")
                End If
                any = True
                AppendBitName(bit, builder)
            Next
        End Sub

        Protected Sub AppendBitName(bit As Integer, builder As StringBuilder)
            Dim id As VariableIdentifier = variableBySlot(bit)
            If id.ContainingSlot > 0 Then
                AppendBitName(id.ContainingSlot, builder)
                builder.Append(".")
            End If
            builder.Append(If(bit = 0, "*", id.Symbol.Name))
        End Sub

#Region "Note Read/Write"

        Protected Overridable Sub NoteRead(variable As Symbol)
            If variable IsNot Nothing AndAlso variable.Kind = SymbolKind.Local Then
                _unusedVariables.Remove(DirectCast(variable, LocalSymbol))
            End If
        End Sub

        Protected Overridable Sub NoteWrite(variable As Symbol, value As BoundExpression)
            If variable IsNot Nothing Then
                _writtenVariables.Add(variable)
                Dim local = TryCast(variable, LocalSymbol)
                If value IsNot Nothing AndAlso (local IsNot Nothing AndAlso Not local.IsConst AndAlso local.Type.IsReferenceType OrElse value.HasErrors) Then
                    ' We duplicate Dev10's behavior here.  The reasoning is, I would guess, that otherwise unread
                    ' variables of reference type are useful because they keep objects alive, i.e., they are read by the
                    ' VM.  And variables that are initialized by non-constant expressions presumably are useful because
                    ' they enable us to clearly document a discarded return value from a method invocation, e.g. var
                    ' discardedValue = F(); >shrug<
                    ' Note, this rule only applies to local variables and not to const locals, i.e. we always report unused 
                    ' const locals. In addition, if the value has errors then suppress these diagnostics in all cases.
                    _unusedVariables.Remove(local)
                End If
            End If
        End Sub

        Protected Function GetNodeSymbol(node As BoundNode) As Symbol
            While node IsNot Nothing
                Select Case node.Kind

                    Case BoundKind.FieldAccess
                        Dim fieldAccess = DirectCast(node, BoundFieldAccess)
                        If FieldAccessMayRequireTracking(fieldAccess) Then
                            node = fieldAccess.ReceiverOpt
                            Continue While
                        End If
                        Return Nothing

                    Case BoundKind.PropertyAccess
                        node = DirectCast(node, BoundPropertyAccess).ReceiverOpt
                        Continue While

                    Case BoundKind.MeReference
                        Return MeParameter

                    Case BoundKind.Local
                        Return DirectCast(node, BoundLocal).LocalSymbol

                    Case BoundKind.RangeVariable
                        Return DirectCast(node, BoundRangeVariable).RangeVariable

                    Case BoundKind.Parameter
                        Return DirectCast(node, BoundParameter).ParameterSymbol

                    Case BoundKind.WithLValueExpressionPlaceholder, BoundKind.WithRValueExpressionPlaceholder
                        Dim substitute As BoundExpression = GetPlaceholderSubstitute(DirectCast(node, BoundValuePlaceholderBase))
                        If substitute IsNot Nothing Then
                            Return GetNodeSymbol(substitute)
                        End If

                        Return Nothing

                    Case BoundKind.LocalDeclaration
                        Return DirectCast(node, BoundLocalDeclaration).LocalSymbol

                    Case BoundKind.ForToStatement,
                        BoundKind.ForEachStatement
                        Return DirectCast(node, BoundForStatement).DeclaredOrInferredLocalOpt

                    Case BoundKind.ByRefArgumentWithCopyBack
                        node = DirectCast(node, BoundByRefArgumentWithCopyBack).OriginalArgument
                        Continue While

                    Case Else
                        Return Nothing

                End Select
            End While
            Return Nothing
        End Function

        Protected Overridable Sub NoteWrite(node As BoundExpression, value As BoundExpression)
            Dim symbol As Symbol = GetNodeSymbol(node)

            If symbol IsNot Nothing Then

                If symbol.Kind = SymbolKind.Local AndAlso DirectCast(symbol, LocalSymbol).DeclarationKind = LocalDeclarationKind.AmbiguousLocals Then
                    ' We have several locals grouped in AmbiguousLocalsPseudoSymbol
                    For Each local In DirectCast(symbol, AmbiguousLocalsPseudoSymbol).Locals
                        NoteWrite(local, value)
                    Next

                Else
                    NoteWrite(symbol, value)
                End If
            End If
        End Sub

        Protected Overridable Sub NoteRead(fieldAccess As BoundFieldAccess)
            Dim symbol As Symbol = GetNodeSymbol(fieldAccess)
            If symbol IsNot Nothing Then

                If symbol.Kind = SymbolKind.Local AndAlso DirectCast(symbol, LocalSymbol).DeclarationKind = LocalDeclarationKind.AmbiguousLocals Then
                    ' We have several locals grouped in AmbiguousLocalsPseudoSymbol
                    For Each local In DirectCast(symbol, AmbiguousLocalsPseudoSymbol).Locals
                        NoteRead(local)
                    Next

                Else
                    NoteRead(symbol)
                End If
            End If
        End Sub

#End Region

#Region "Operations with slots"

        ''' <summary>
        ''' Locals are given slots when their declarations are encountered.  We only need give slots to local variables, and 
        ''' the "Me" variable of a structure constructs. Other variables are not given slots, and are therefore not tracked 
        ''' by the analysis.  This returns SlotKind.NotTracked for a variable that is not tracked, for fields of structs 
        ''' that have the same assigned status as the container, and for structs that (recursively) contain no data members.
        ''' We do not need to track references to variables that occur before the variable is declared, as those are reported 
        ''' in an earlier phase as "use before declaration". That allows us to avoid giving slots to local variables before
        ''' processing their declarations.
        ''' </summary>
        Protected Function VariableSlot(symbol As Symbol, Optional containingSlot As Integer = 0) As Integer

            containingSlot = DescendThroughTupleRestFields(symbol, containingSlot, forceContainingSlotsToExist:=False)

            If symbol Is Nothing Then
                ' NOTE: This point may be hit in erroneous code, like 
                '       referencing instance members from static methods
                Return SlotKind.NotTracked
            End If

            Dim slot As Integer
            Return If((_variableSlot.TryGetValue(New VariableIdentifier(symbol, containingSlot), slot)), slot, SlotKind.NotTracked)

        End Function

        ''' <summary>
        ''' Return the slot for a variable, or SlotKind.NotTracked if it is not tracked (because, for example, it is an empty struct).
        ''' </summary>
        Protected Function MakeSlotsForExpression(node As BoundExpression) As SlotCollection
            Dim result As New SlotCollection() ' empty

            Select Case node.Kind

                Case BoundKind.MeReference, BoundKind.MyBaseReference, BoundKind.MyClassReference
                    If MeParameter IsNot Nothing Then
                        result.Append(GetOrCreateSlot(MeParameter))
                    End If

                Case BoundKind.Local
                    Dim local As LocalSymbol = DirectCast(node, BoundLocal).LocalSymbol

                    If local.DeclarationKind = LocalDeclarationKind.AmbiguousLocals Then
                        ' multiple locals
                        For Each loc In DirectCast(local, AmbiguousLocalsPseudoSymbol).Locals
                            result.Append(GetOrCreateSlot(loc))
                        Next

                    Else
                        ' just one simple local
                        result.Append(GetOrCreateSlot(local))
                    End If


                Case BoundKind.RangeVariable
                    result.Append(GetOrCreateSlot(DirectCast(node, BoundRangeVariable).RangeVariable))

                Case BoundKind.Parameter
                    result.Append(GetOrCreateSlot(DirectCast(node, BoundParameter).ParameterSymbol))

                Case BoundKind.FieldAccess
                    Dim fieldAccess = DirectCast(node, BoundFieldAccess)
                    Dim fieldsymbol = fieldAccess.FieldSymbol
                    Dim receiverOpt = fieldAccess.ReceiverOpt

                    ' do not track static fields
                    If fieldsymbol.IsShared OrElse receiverOpt Is Nothing OrElse receiverOpt.Kind = BoundKind.TypeExpression Then
                        Exit Select ' empty result
                    End If

                    ' do not track fields of non-structs
                    If receiverOpt.Type Is Nothing OrElse receiverOpt.Type.TypeKind <> TypeKind.Structure Then
                        Exit Select ' empty result
                    End If

                    result = MakeSlotsForExpression(receiverOpt)
                    ' update slots, reuse collection
                    ' NOTE: we don't filter out SlotKind.NotTracked values
                    For i = 0 To result.Count - 1
                        result(i) = GetOrCreateSlot(fieldAccess.FieldSymbol, result(0))
                    Next

                Case BoundKind.WithLValueExpressionPlaceholder, BoundKind.WithRValueExpressionPlaceholder
                    Dim substitute As BoundExpression = Me.GetPlaceholderSubstitute(DirectCast(node, BoundValuePlaceholderBase))
                    If substitute IsNot Nothing Then
                        Return MakeSlotsForExpression(substitute)
                    End If

            End Select

            Return result
        End Function

        ''' <summary>
        ''' Force a variable to have a slot.
        ''' </summary>
        ''' <param name = "symbol"></param>
        ''' <returns></returns>
        Protected Function GetOrCreateSlot(symbol As Symbol, Optional containingSlot As Integer = 0) As Integer
            containingSlot = DescendThroughTupleRestFields(symbol, containingSlot, forceContainingSlotsToExist:=True)


            If containingSlot = SlotKind.NotTracked Then
                Return SlotKind.NotTracked
            End If

            ' Check if the slot already exists
            Dim varIdentifier As New VariableIdentifier(symbol, containingSlot)

            Dim slot As Integer
            If Not _variableSlot.TryGetValue(varIdentifier, slot) Then

                If symbol.Kind = SymbolKind.Local Then
                    Me._unusedVariables.Add(DirectCast(symbol, LocalSymbol))
                End If

                Dim variableType As TypeSymbol = GetVariableType(symbol)

                If IsEmptyStructType(variableType) Then
                    Return SlotKind.NotTracked
                End If

                '  Create a slot
                slot = nextVariableSlot
                nextVariableSlot = nextVariableSlot + 1
                _variableSlot.Add(varIdentifier, slot)
                If slot >= variableBySlot.Length Then
                    Array.Resize(Me.variableBySlot, slot * 2)
                End If
                variableBySlot(slot) = varIdentifier
            End If

            Me.Normalize(Me.State)
            Return slot
        End Function

        ''' <summary>
        ''' Descends through Rest fields of a tuple if "symbol" is an extended field
        ''' As a result the "symbol" will be adjusted to be the field of the innermost tuple
        ''' and a corresponding containingSlot is returned.
        ''' Return value -1 indicates a failure which could happen for the following reasons
        ''' a) Rest field does not exist, which could happen in rare error scenarios involving broken ValueTuple types
        ''' b) Rest is not tracked already and forceSlotsToExist is false (otherwise we create slots on demand)
        ''' </summary>
        Private Function DescendThroughTupleRestFields(ByRef symbol As Symbol, containingSlot As Integer, forceContainingSlotsToExist As Boolean) As Integer
            Dim fieldSymbol = TryCast(symbol, TupleFieldSymbol)
            If fieldSymbol IsNot Nothing Then
                Dim containingType As TypeSymbol = DirectCast(symbol.ContainingType, TupleTypeSymbol).UnderlyingNamedType

                ' for tuple fields the variable identifier represents the underlying field
                symbol = fieldSymbol.TupleUnderlyingField

                ' descend through Rest fields
                ' force corresponding slots if do not exist
                While Not TypeSymbol.Equals(containingType, symbol.ContainingType, TypeCompareKind.ConsiderEverything)
                    Dim restField = TryCast(containingType.GetMembers(TupleTypeSymbol.RestFieldName).FirstOrDefault(), FieldSymbol)
                    If restField Is Nothing Then
                        Return -1
                    End If

                    If forceContainingSlotsToExist Then
                        containingSlot = GetOrCreateSlot(restField, containingSlot)
                    Else
                        If Not _variableSlot.TryGetValue(New VariableIdentifier(restField, containingSlot), containingSlot) Then
                            Return -1
                        End If
                    End If

                    containingType = restField.Type.GetTupleUnderlyingTypeOrSelf()
                End While
            End If

            Return containingSlot
        End Function


        ' In C#, we moved this cache to a separate cache held onto by the compilation, so we didn't
        ' recompute it each time.
        '
        ' This is not so simple in VB, because we'd have to move the cache for members of the 
        ' struct, and we'd need two different caches depending on _trackStructsWithIntrinsicTypedFields.
        ' So this optimization is not done for now in VB.

        Private ReadOnly _isEmptyStructType As New Dictionary(Of NamedTypeSymbol, Boolean)()

        Protected Overridable Function IsEmptyStructType(type As TypeSymbol) As Boolean
            Dim namedType = TryCast(type, NamedTypeSymbol)
            If namedType Is Nothing Then
                Return False
            End If

            If Not IsTrackableStructType(namedType) Then
                Return False
            End If

            Dim result As Boolean = False
            If _isEmptyStructType.TryGetValue(namedType, result) Then
                Return result
            End If

            ' to break cycles 
            _isEmptyStructType(namedType) = True

            For Each field In GetStructInstanceFields(namedType)
                If Not IsEmptyStructType(field.Type) Then
                    _isEmptyStructType(namedType) = False
                    Return False
                End If
            Next

            _isEmptyStructType(namedType) = True
            Return True
        End Function

        Private Shared Function IsTrackableStructType(symbol As TypeSymbol) As Boolean
            If IsNonPrimitiveValueType(symbol) Then
                Dim type = TryCast(symbol.OriginalDefinition, NamedTypeSymbol)
                Return (type IsNot Nothing) AndAlso Not type.KnownCircularStruct
            End If
            Return False
        End Function

        ''' <summary> Calculates the flag of being already reported; for structure types
        ''' the slot may be reported if ALL the children are reported </summary>
        Private Function IsSlotAlreadyReported(symbolType As TypeSymbol, slot As Integer) As Boolean
            If _alreadyReported(slot) Then
                Return True
            End If

            '  not applicable for 'special slots'
            If slot <= SlotKind.FunctionValue Then
                Return False
            End If

            If Not IsTrackableStructType(symbolType) Then
                Return False
            End If

            '  For structures - check field slots
            For Each field In GetStructInstanceFields(symbolType)

                Dim childSlot = VariableSlot(field, slot)
                If childSlot <> SlotKind.NotTracked Then

                    If Not IsSlotAlreadyReported(field.Type, childSlot) Then
                        Return False
                    End If

                Else
                    Return False ' slot not created yet 
                End If

            Next

            ' Seems to be assigned through children
            _alreadyReported(slot) = True
            Return True
        End Function

        ''' <summary> Marks slot as reported, propagates 'reported' flag to the children if necessary </summary>
        Private Sub MarkSlotAsReported(symbolType As TypeSymbol, slot As Integer)
            If _alreadyReported(slot) Then
                Return
            End If

            _alreadyReported(slot) = True

            '  not applicable for 'special slots'
            If slot <= SlotKind.FunctionValue Then
                Return
            End If

            If Not IsTrackableStructType(symbolType) Then
                Return
            End If

            '  For structures - propagate to field slots
            For Each field In GetStructInstanceFields(symbolType)

                Dim childSlot = VariableSlot(field, slot)
                If childSlot <> SlotKind.NotTracked Then
                    MarkSlotAsReported(field.Type, childSlot)
                End If

            Next
        End Sub

        ''' <summary> Unassign a slot for a regular variable </summary>
        Private Sub SetSlotUnassigned(slot As Integer)
            If slot >= Me.State.Assigned.Capacity Then
                Normalize(Me.State)
            End If

            If Me._tryState.HasValue Then
                Dim tryState = Me._tryState.Value
                SetSlotUnassigned(slot, tryState)
                Me._tryState = tryState
            End If

            SetSlotUnassigned(slot, Me.State)
        End Sub

        Private Sub SetSlotUnassigned(slot As Integer, ByRef state As LocalState)
            Dim id As VariableIdentifier = variableBySlot(slot)
            Dim type As TypeSymbol = GetVariableType(id.Symbol)
            Debug.Assert(Not IsEmptyStructType(type))

            ' If the state of the slot is 'assigned' we need to clear it 
            '    and possibly propagate it to all the parents
            state.Unassign(slot)

            'unassign struct children (if possible)
            If IsTrackableStructType(type) Then
                For Each field In GetStructInstanceFields(type)
                    ' get child's slot

                    ' NOTE: we don't need to care about possible cycles here because we took care of it 
                    '       in MakeSlotImpl when we created slots; we will just get SlotKind.NotTracked in worst case
                    Dim childSlot = VariableSlot(field, slot)

                    If childSlot >= SlotKind.FirstAvailable Then
                        SetSlotUnassigned(childSlot, state)
                    End If
                Next
            End If

            '  propagate to parents
            While id.ContainingSlot > 0
                '  check the parent
                Dim parentSlot = id.ContainingSlot
                Dim parentIdentifier = variableBySlot(parentSlot)
                Dim parentSymbol As Symbol = parentIdentifier.Symbol

                If Not state.IsAssigned(parentSlot) Then
                    Exit While
                End If

                '  unassign and continue loop
                state.Unassign(parentSlot)

                If parentSymbol.Kind = SymbolKind.Local AndAlso DirectCast(parentSymbol, LocalSymbol).IsFunctionValue Then
                    state.Unassign(SlotKind.FunctionValue)
                End If

                id = parentIdentifier
            End While
        End Sub

        ''' <summary>Assign a slot for a regular variable in a given state.</summary>
        Private Sub SetSlotAssigned(slot As Integer, ByRef state As LocalState)
            Dim id As VariableIdentifier = variableBySlot(slot)

            Dim type As TypeSymbol = GetVariableType(id.Symbol)
            Debug.Assert(Not IsEmptyStructType(type))

            If slot >= Me.State.Assigned.Capacity Then
                Normalize(Me.State)
            End If

            If Not state.IsAssigned(slot) Then
                '  assign this slot
                state.Assign(slot)

                If IsTrackableStructType(type) Then

                    '  propagate to children
                    For Each field In GetStructInstanceFields(type)
                        ' get child's slot

                        ' NOTE: we don't need to care about possible cycles here because we took care of it 
                        '       in MakeSlotImpl when we created slots; we will just get SlotKind.NotTracked in worst case
                        Dim childSlot = VariableSlot(field, slot)

                        If childSlot >= SlotKind.FirstAvailable Then
                            SetSlotAssigned(childSlot, state)
                        End If
                    Next

                End If

                ' propagate to parents
                While id.ContainingSlot > 0
                    ' check if all parent's children are set
                    Dim parentSlot = id.ContainingSlot
                    Dim parentIdentifier = variableBySlot(parentSlot)
                    Dim parentSymbol As Symbol = parentIdentifier.Symbol
                    Dim parentStructType = GetVariableType(parentSymbol)

                    '  exit while if there is at least one child with a slot which is not assigned
                    For Each childSymbol In GetStructInstanceFields(parentStructType)
                        Dim childSlot = GetOrCreateSlot(childSymbol, parentSlot)
                        If childSlot <> SlotKind.NotTracked AndAlso Not state.IsAssigned(childSlot) Then
                            Exit While
                        End If
                    Next

                    '  otherwise the parent can be set to assigned
                    state.Assign(parentSlot)
                    If parentSymbol.Kind = SymbolKind.Local AndAlso DirectCast(parentSymbol, LocalSymbol).IsFunctionValue Then
                        state.Assign(SlotKind.FunctionValue)
                    End If
                    id = parentIdentifier
                End While

            End If
        End Sub

        ''' <summary>Assign a slot for a regular variable.</summary>
        Private Sub SetSlotAssigned(slot As Integer)
            SetSlotAssigned(slot, Me.State)
        End Sub

        ''' <summary> Hash structure fields as we may query them many times </summary>
        Private _typeToMembersCache As Dictionary(Of TypeSymbol, ImmutableArray(Of FieldSymbol)) = Nothing

        Private Function ShouldIgnoreStructField(field As FieldSymbol) As Boolean
            If field.IsShared Then
                Return True
            End If

            If Me._trackStructsWithIntrinsicTypedFields Then
                ' If the flag is set we do not ignore any structure instance fields 
                Return False
            End If

            Dim fieldType As TypeSymbol = field.Type

            ' otherwise of fields of intrinsic type are ignored
            If fieldType.IsIntrinsicValueType() Then
                Return True
            End If

            ' it the type is a type parameter and also type does NOT guarantee it is 
            ' always a reference type, we ignore the field following Dev11 implementation
            If fieldType.IsTypeParameter() Then
                Dim typeParam = DirectCast(fieldType, TypeParameterSymbol)
                If Not typeParam.IsReferenceType Then
                    Return True
                End If
            End If

            ' We also do NOT ignore all non-private fields
            If field.DeclaredAccessibility <> Accessibility.Private Then
                Return False
            End If

            ' Do NOT ignore private fields from source
            ' NOTE: We should do this for all fields, but dev11 ignores private fields from
            ' metadata, so we need to as well to avoid breaking people.  That's why we're
            ' distinguishing between source and metadata, rather than between the current 
            ' compilation and another compilation.
            If field.Dangerous_IsFromSomeCompilationIncludingRetargeting Then
                ' NOTE: Dev11 ignores fields auto-generated for events
                Dim eventOrProperty As Symbol = field.AssociatedSymbol
                If eventOrProperty Is Nothing OrElse eventOrProperty.Kind <> SymbolKind.Event Then
                    Return False
                End If
            End If

            '' If the field is private we don't ignore it if it is a field of a generic struct
            If field.ContainingType.IsGenericType Then
                Return False
            End If

            Return True
        End Function

        Private Function GetStructInstanceFields(type As TypeSymbol) As ImmutableArray(Of FieldSymbol)
            Dim result As ImmutableArray(Of FieldSymbol) = Nothing
            If _typeToMembersCache Is Nothing OrElse Not _typeToMembersCache.TryGetValue(type, result) Then

                ' load and add to cache
                Dim builder = ArrayBuilder(Of FieldSymbol).GetInstance()

                For Each member In type.GetMembersUnordered()
                    ' only fields
                    If member.Kind = SymbolKind.Field Then
                        Dim field = DirectCast(member, FieldSymbol)

                        ' Do not report virtual tuple fields.
                        ' They are additional aliases to the fields of the underlying struct or nested extensions.
                        ' and as such are already accounted for via the nonvirtual fields.
                        If field.IsVirtualTupleField Then
                            Continue For
                        End If

                        ' only instance fields
                        If Not ShouldIgnoreStructField(field) Then
                            ' NOTE: DO NOT skip fields of intrinsic types if _trackStructsWithIntrinsicTypedFields is True
                            builder.Add(field)
                        End If
                    End If
                Next

                result = builder.ToImmutableAndFree()

                If _typeToMembersCache Is Nothing Then
                    _typeToMembersCache = New Dictionary(Of TypeSymbol, ImmutableArray(Of FieldSymbol))
                End If
                _typeToMembersCache.Add(type, result)
            End If

            Return result
        End Function

        Private Shared Function GetVariableType(symbol As Symbol) As TypeSymbol
            Select Case symbol.Kind

                Case SymbolKind.Local
                    Return DirectCast(symbol, LocalSymbol).Type

                Case SymbolKind.RangeVariable
                    Return DirectCast(symbol, RangeVariableSymbol).Type

                Case SymbolKind.Field
                    Return DirectCast(symbol, FieldSymbol).Type

                Case SymbolKind.Parameter
                    Return DirectCast(symbol, ParameterSymbol).Type

                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(symbol.Kind)

            End Select
        End Function

        Protected Sub SetSlotState(slot As Integer, assigned As Boolean)
            Debug.Assert(slot <> SlotKind.Unreachable)

            If slot = SlotKind.NotTracked Then
                Return

            ElseIf slot = SlotKind.FunctionValue Then
                '  Function value slot is treated in a special way. For example it does not have Symbol assigned
                '  Just do a simple assign/unassign
                If assigned Then
                    Me.State.Assign(slot)
                Else
                    Me.State.Unassign(slot)
                End If

            Else

                ' The rest should be a regular slot
                If assigned Then
                    SetSlotAssigned(slot)
                Else
                    SetSlotUnassigned(slot)
                End If

            End If

        End Sub

#End Region

#Region "Assignments: Check and Report"

        ''' <summary>
        ''' Check that the given variable is definitely assigned.  If not, produce an error.
        ''' </summary>
        Protected Sub CheckAssigned(symbol As Symbol, node As SyntaxNode, Optional rwContext As ReadWriteContext = ReadWriteContext.None)
            If symbol IsNot Nothing Then
                Dim local = TryCast(symbol, LocalSymbol)
                If local IsNot Nothing AndAlso local.IsCompilerGenerated AndAlso Not Me.ProcessCompilerGeneratedLocals Then
                    ' Do not process compiler generated temporary locals
                    Return
                End If

                ' 'local' can be a pseudo-local representing a set of real locals
                If local IsNot Nothing AndAlso local.DeclarationKind = LocalDeclarationKind.AmbiguousLocals Then
                    Dim ambiguous = DirectCast(local, AmbiguousLocalsPseudoSymbol)

                    ' Ambiguous implicit receiver is always considered to be assigned
                    VisitAmbiguousLocalSymbol(ambiguous)

                    ' Note reads of locals
                    For Each subLocal In ambiguous.Locals
                        NoteRead(subLocal)
                    Next

                Else
                    Dim slot As Integer = GetOrCreateSlot(symbol)
                    If slot >= Me.State.Assigned.Capacity Then
                        Normalize(Me.State)
                    End If

                    If slot >= SlotKind.FirstAvailable AndAlso Me.State.Reachable AndAlso Not Me.State.IsAssigned(slot) Then
                        ReportUnassigned(symbol, node, rwContext)
                    End If

                    NoteRead(symbol)
                End If
            End If
        End Sub

        ''' <summary> Version of CheckAssigned for bound field access </summary>
        Private Sub CheckAssigned(fieldAccess As BoundFieldAccess, node As SyntaxNode, Optional rwContext As ReadWriteContext = ReadWriteContext.None)
            Dim unassignedSlot As Integer

            If Me.State.Reachable AndAlso Not IsAssigned(fieldAccess, unassignedSlot) Then
                ReportUnassigned(fieldAccess.FieldSymbol, node, rwContext, unassignedSlot, fieldAccess)
            End If

            NoteRead(fieldAccess)
        End Sub

        Protected Overridable Sub VisitAmbiguousLocalSymbol(ambiguous As AmbiguousLocalsPseudoSymbol)
        End Sub

        Protected Overrides Sub VisitLvalue(node As BoundExpression, Optional dontLeaveRegion As Boolean = False)
            MyBase.VisitLvalue(node, True) ' Don't leave region

            If node.Kind = BoundKind.Local Then
                Dim symbol As LocalSymbol = DirectCast(node, BoundLocal).LocalSymbol
                If symbol.DeclarationKind = LocalDeclarationKind.AmbiguousLocals Then
                    VisitAmbiguousLocalSymbol(DirectCast(symbol, AmbiguousLocalsPseudoSymbol))
                End If
            End If

            ' NOTE: we can skip checking if Me._firstInRegion is nothing because 'node' is not nothing
            If Not dontLeaveRegion AndAlso node Is Me._lastInRegion AndAlso IsInside Then
                Me.LeaveRegion()
            End If
        End Sub

        ''' <summary> Check node for being assigned, return the value of unassigned slot in unassignedSlot </summary>
        Private Function IsAssigned(node As BoundExpression, ByRef unassignedSlot As Integer) As Boolean
            unassignedSlot = SlotKind.NotTracked

            If IsEmptyStructType(node.Type) Then
                Return True
            End If

            Select Case node.Kind

                Case BoundKind.MeReference
                    unassignedSlot = VariableSlot(MeParameter)

                Case BoundKind.Local
                    Dim local As LocalSymbol = DirectCast(node, BoundLocal).LocalSymbol
                    If local.DeclarationKind <> LocalDeclarationKind.AmbiguousLocals Then
                        unassignedSlot = VariableSlot(local)

                    Else
                        ' We never report ambiguous locals pseudo-symbol as unassigned, 
                        unassignedSlot = SlotKind.NotTracked
                        VisitAmbiguousLocalSymbol(DirectCast(local, AmbiguousLocalsPseudoSymbol))
                    End If

                Case BoundKind.RangeVariable
                    ' range variables are always assigned
                    unassignedSlot = -1
                    Return True

                Case BoundKind.Parameter
                    unassignedSlot = VariableSlot(DirectCast(node, BoundParameter).ParameterSymbol)

                Case BoundKind.FieldAccess
                    Dim fieldAccess = DirectCast(node, BoundFieldAccess)
                    If Not FieldAccessMayRequireTracking(fieldAccess) OrElse IsAssigned(fieldAccess.ReceiverOpt, unassignedSlot) Then
                        Return True
                    End If
                    ' NOTE: unassignedSlot must have been set by previous call to IsAssigned(...)
                    unassignedSlot = GetOrCreateSlot(fieldAccess.FieldSymbol, unassignedSlot)

                Case BoundKind.WithLValueExpressionPlaceholder, BoundKind.WithRValueExpressionPlaceholder
                    Dim substitute As BoundExpression = Me.GetPlaceholderSubstitute(DirectCast(node, BoundValuePlaceholderBase))
                    If substitute IsNot Nothing Then
                        Return IsAssigned(substitute, unassignedSlot)
                    End If

                    unassignedSlot = SlotKind.NotTracked

                Case Else
                    ' The value is a method call return value or something else we can assume is assigned.
                    unassignedSlot = SlotKind.NotTracked

            End Select

            Return Me.State.IsAssigned(unassignedSlot)
        End Function

        ''' <summary>
        ''' Property controls Roslyn data flow analysis features which are disabled in command-line 
        ''' compiler mode to maintain backward compatibility (mostly diagnostics not reported by Dev11), 
        ''' but *enabled* in flow analysis API
        ''' </summary>
        Protected Overridable ReadOnly Property EnableBreakingFlowAnalysisFeatures As Boolean
            Get
                Return False
            End Get
        End Property

        ''' <summary>
        ''' Specifies if the analysis should process compiler generated locals. 
        ''' 
        ''' Note that data flow API should never report compiler generated variables 
        ''' as well as those should not generate any diagnostics (like being unassigned, etc...).
        ''' 
        ''' But when the analysis is used for iterators or async captures it should process 
        ''' compiler generated locals as well...
        ''' </summary>
        Protected Overridable ReadOnly Property ProcessCompilerGeneratedLocals As Boolean
            Get
                Return False
            End Get
        End Property

        Private Function GetUnassignedSymbolFirstLocation(sym As Symbol, boundFieldAccess As BoundFieldAccess) As Location
            Select Case sym.Kind
                Case SymbolKind.Parameter
                    Return Nothing

                Case SymbolKind.RangeVariable
                    Return Nothing

                Case SymbolKind.Local
                    Dim locations As ImmutableArray(Of Location) = sym.Locations
                    Return If(locations.IsEmpty, Nothing, locations(0))

                Case SymbolKind.Field
                    Debug.Assert(boundFieldAccess IsNot Nothing)
                    If sym.IsShared Then
                        Return Nothing
                    End If

                    Dim receiver As BoundExpression = boundFieldAccess.ReceiverOpt
                    Debug.Assert(receiver IsNot Nothing)

                    Select Case receiver.Kind
                        Case BoundKind.Local
                            Return GetUnassignedSymbolFirstLocation(DirectCast(receiver, BoundLocal).LocalSymbol, Nothing)

                        Case BoundKind.FieldAccess
                            Dim fieldAccess = DirectCast(receiver, BoundFieldAccess)
                            Return GetUnassignedSymbolFirstLocation(fieldAccess.FieldSymbol, fieldAccess)

                        Case Else
                            Return Nothing
                    End Select

                Case Else
                    Debug.Assert(False, "Why is this reachable?")
                    Return Nothing
            End Select
        End Function

        ''' <summary>
        ''' Report a given variable as not definitely assigned.  Once a variable has been so
        ''' reported, we suppress further reports of that variable.
        ''' </summary>
        Protected Overridable Sub ReportUnassigned(sym As Symbol,
                                                   node As SyntaxNode,
                                                   rwContext As ReadWriteContext,
                                                   Optional slot As Integer = SlotKind.NotTracked,
                                                   Optional boundFieldAccess As BoundFieldAccess = Nothing)

            If slot < SlotKind.FirstAvailable Then
                slot = VariableSlot(sym)
            End If

            If slot >= Me._alreadyReported.Capacity Then
                _alreadyReported.EnsureCapacity(Me.nextVariableSlot)
            End If

            If sym.Kind = SymbolKind.Parameter Then
                ' Because there are no Out parameters in VB, general workflow should not hit this point;
                '   but in region analysis parameters are being unassigned, so it is reachable 
                Return
            End If

            If sym.Kind = SymbolKind.RangeVariable Then
                ' Range variables are always assigned for the purpose of error reporting.
                Return
            End If

            Debug.Assert(sym.Kind = SymbolKind.Local OrElse sym.Kind = SymbolKind.Field)

            Dim localOrFieldType As TypeSymbol
            Dim isFunctionValue As Boolean
            Dim isStaticLocal As Boolean
            Dim isImplicitlyDeclared As Boolean = False

            If sym.Kind = SymbolKind.Local Then
                Dim locSym = DirectCast(sym, LocalSymbol)
                localOrFieldType = locSym.Type
                isFunctionValue = locSym.IsFunctionValue AndAlso EnableBreakingFlowAnalysisFeatures
                isStaticLocal = locSym.IsStatic
                isImplicitlyDeclared = locSym.IsImplicitlyDeclared
            Else
                isFunctionValue = False
                isStaticLocal = False
                localOrFieldType = DirectCast(sym, FieldSymbol).Type
            End If

            If Not IsSlotAlreadyReported(localOrFieldType, slot) Then

                Dim warning As ERRID = Nothing

                ' NOTE: When a variable is being used before declaration VB generates ERR_UseOfLocalBeforeDeclaration1
                ' NOTE: error, thus we don't want to generate redundant warning; we base such an analysis on node
                ' NOTE: and symbol locations
                Dim firstLocation As Location = GetUnassignedSymbolFirstLocation(sym, boundFieldAccess)
                If isImplicitlyDeclared OrElse firstLocation Is Nothing OrElse firstLocation.SourceSpan.Start < node.SpanStart Then

                    ' Because VB does not support out parameters, only locals OR fields of local structures can be unassigned.
                    If localOrFieldType IsNot Nothing AndAlso Not isStaticLocal Then

                        If localOrFieldType.IsIntrinsicValueType OrElse localOrFieldType.IsReferenceType Then
                            If localOrFieldType.IsIntrinsicValueType AndAlso Not isFunctionValue Then
                                warning = Nothing
                            Else
                                warning = If(rwContext = ReadWriteContext.ByRefArgument, ERRID.WRN_DefAsgUseNullRefByRef, ERRID.WRN_DefAsgUseNullRef)
                            End If
                        ElseIf localOrFieldType.IsValueType Then
                            ' This is only reported for structures with reference type fields.
                            warning = If(rwContext = ReadWriteContext.ByRefArgument, ERRID.WRN_DefAsgUseNullRefByRefStr, ERRID.WRN_DefAsgUseNullRefStr)
                        Else
                            Debug.Assert(localOrFieldType.IsTypeParameter)
                        End If

                        If warning <> Nothing Then
                            Me.diagnostics.Add(warning, node.GetLocation(), If(sym.Name, "dummy"))
                        End If
                    End If

                    MarkSlotAsReported(localOrFieldType, slot)
                End If
            End If

        End Sub

        Private Sub CheckAssignedFunctionValue(local As LocalSymbol, node As SyntaxNode)
            If Not Me.State.FunctionAssignedValue AndAlso Not _seenOnErrorOrResume Then
                Dim type As TypeSymbol = local.Type
                ' NOTE: Dev11 does NOT report warning on user-defined empty value types
                ' Special case: We specifically want to give a warning if the user doesn't return from a WinRT AddHandler.
                ' NOTE: Strictly speaking, dev11 actually checks whether the return type is EventRegistrationToken (see IsWinRTEventAddHandler),
                ' but the conditions should be equivalent (i.e. return EventRegistrationToken if and only if WinRT).
                If EnableBreakingFlowAnalysisFeatures OrElse Not type.IsValueType OrElse type.IsIntrinsicOrEnumType OrElse Not IsEmptyStructType(type) OrElse
                    (Me.MethodSymbol.MethodKind = MethodKind.EventAdd AndAlso DirectCast(Me.MethodSymbol.AssociatedSymbol, EventSymbol).IsWindowsRuntimeEvent) Then
                    ReportUnassignedFunctionValue(local, node)
                End If
            End If
        End Sub

        Private Sub ReportUnassignedFunctionValue(local As LocalSymbol, node As SyntaxNode)
            If Not _alreadyReported(SlotKind.FunctionValue) Then

                Dim type As TypeSymbol = Nothing
                Dim method = Me.MethodSymbol

                type = method.ReturnType
                If type IsNot Nothing AndAlso
                   Not (method.IsIterator OrElse
                        (method.IsAsync AndAlso type.Equals(compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task)))) Then

                    Dim warning As ERRID = Nothing
                    Dim localName As String = GetFunctionLocalName(method, local)

                    type = type.GetEnumUnderlyingTypeOrSelf

                    ' Actual warning depends on whether the local is a function,
                    ' property, or operator value versus reference type.
                    If type.IsIntrinsicValueType Then

                        Select Case MethodSymbol.MethodKind
                            Case MethodKind.Conversion, MethodKind.UserDefinedOperator
                                warning = ERRID.WRN_DefAsgNoRetValOpVal1
                            Case MethodKind.PropertyGet
                                warning = ERRID.WRN_DefAsgNoRetValPropVal1
                            Case Else
                                Debug.Assert(MethodSymbol.MethodKind = MethodKind.Ordinary OrElse MethodSymbol.MethodKind = MethodKind.LambdaMethod)
                                warning = ERRID.WRN_DefAsgNoRetValFuncVal1
                        End Select

                    ElseIf type.IsReferenceType Then

                        Select Case MethodSymbol.MethodKind
                            Case MethodKind.Conversion, MethodKind.UserDefinedOperator
                                warning = ERRID.WRN_DefAsgNoRetValOpRef1
                            Case MethodKind.PropertyGet
                                warning = ERRID.WRN_DefAsgNoRetValPropRef1
                            Case Else
                                Debug.Assert(MethodSymbol.MethodKind = MethodKind.Ordinary OrElse MethodSymbol.MethodKind = MethodKind.LambdaMethod)
                                warning = ERRID.WRN_DefAsgNoRetValFuncRef1
                        End Select

                    ElseIf type.TypeKind = TypeKind.TypeParameter Then
                        ' IsReferenceType was false, so this type parameter was not known to be a reference type.
                        ' Following past practice, no warning is given in this case.

                    Else
                        Debug.Assert(type.IsValueType)
                        Select Case MethodSymbol.MethodKind
                            Case MethodKind.Conversion, MethodKind.UserDefinedOperator
                                ' no warning is given in this case.
                            Case MethodKind.PropertyGet
                                warning = ERRID.WRN_DefAsgNoRetValPropRef1
                            Case MethodKind.EventAdd
                                ' In Dev11, there wasn't time to change the syntax of AddHandler to allow the user
                                ' to specify a return type (necessarily, EventRegistrationToken) for WinRT events.
                                ' DevDiv #376690 reflects the fact that this leads to an incredibly confusing user
                                ' experience if nothing is return: no diagnostics are reported, but RemoveHandler
                                ' statements will silently fail.  To prompt the user, (1) warn if there is no
                                ' explicit return, and (2) modify the error message to say "AddHandler As 
                                ' EventRegistrationToken" to hint at the nature of the problem.
                                ' Update: Dev11 just mangles an existing error message, but we'll introduce a new, 
                                ' clearer, one.

                                warning = ERRID.WRN_DefAsgNoRetValWinRtEventVal1
                                localName = MethodSymbol.AssociatedSymbol.Name
                            Case Else
                                warning = ERRID.WRN_DefAsgNoRetValFuncRef1
                        End Select
                    End If

                    If warning <> Nothing Then
                        Debug.Assert(localName IsNot Nothing)
                        Me.diagnostics.Add(warning, node.GetLocation(), localName)
                    End If

                End If

                _alreadyReported(SlotKind.FunctionValue) = True
            End If
        End Sub

        Private Shared Function GetFunctionLocalName(method As MethodSymbol, local As LocalSymbol) As String
            Select Case method.MethodKind
                Case MethodKind.LambdaMethod
                    Return StringConstants.AnonymousMethodName

                Case MethodKind.Conversion, MethodKind.UserDefinedOperator
                    ' The operator's function local is op_<something> and not
                    ' VB's operator name, so we need to take the identifier token
                    ' directly
                    Dim operatorBlock = DirectCast(DirectCast(local.ContainingSymbol, SourceMemberMethodSymbol).BlockSyntax, OperatorBlockSyntax)
                    Return operatorBlock.OperatorStatement.OperatorToken.Text

                Case Else
                    Return If(local.Name, method.Name)
            End Select
        End Function

        ''' <summary>
        ''' Mark a variable as assigned (or unassigned).
        ''' </summary>
        Protected Overridable Sub Assign(node As BoundNode, value As BoundExpression, Optional assigned As Boolean = True)
            Select Case node.Kind
                Case BoundKind.LocalDeclaration
                    Dim local = DirectCast(node, BoundLocalDeclaration)
                    Debug.Assert(local.InitializerOpt Is value OrElse local.InitializedByAsNew)
                    Dim symbol = local.LocalSymbol
                    Dim slot As Integer = GetOrCreateSlot(symbol)
                    Dim written As Boolean = assigned OrElse Not Me.State.Reachable
                    SetSlotState(slot, written)
                    ' Note write if the local has an initializer or if it is part of an as-new.
                    If assigned AndAlso (value IsNot Nothing OrElse local.InitializedByAsNew) Then NoteWrite(symbol, value)

                Case BoundKind.ForToStatement,
                    BoundKind.ForEachStatement
                    Dim forStatement = DirectCast(node, BoundForStatement)
                    Dim symbol = forStatement.DeclaredOrInferredLocalOpt
                    Debug.Assert(symbol IsNot Nothing)
                    Dim slot As Integer = GetOrCreateSlot(symbol)
                    Dim written As Boolean = assigned OrElse Not Me.State.Reachable
                    SetSlotState(slot, written)

                Case BoundKind.Local
                    Dim local = DirectCast(node, BoundLocal)
                    Dim symbol = local.LocalSymbol
                    If symbol.IsCompilerGenerated AndAlso Not Me.ProcessCompilerGeneratedLocals Then
                        ' Do not process compiler generated temporary locals.
                        Return
                    End If

                    ' Regular variable
                    Dim slot As Integer = GetOrCreateSlot(symbol)
                    SetSlotState(slot, assigned)
                    If symbol.IsFunctionValue Then
                        SetSlotState(SlotKind.FunctionValue, assigned)
                    End If
                    If assigned Then
                        NoteWrite(symbol, value)
                    End If

                Case BoundKind.Parameter
                    Dim local = DirectCast(node, BoundParameter)
                    Dim symbol = local.ParameterSymbol
                    Dim slot As Integer = GetOrCreateSlot(symbol)
                    SetSlotState(slot, assigned)
                    If assigned Then NoteWrite(symbol, value)

                Case BoundKind.MeReference
                    ' var local = node as BoundThisReference;
                    Dim slot As Integer = GetOrCreateSlot(MeParameter)
                    SetSlotState(slot, assigned)
                    If assigned Then NoteWrite(MeParameter, value)

                Case BoundKind.FieldAccess, BoundKind.PropertyAccess
                    Dim expression = DirectCast(node, BoundExpression)
                    Dim slots As SlotCollection = MakeSlotsForExpression(expression)
                    For i = 0 To slots.Count - 1
                        SetSlotState(slots(i), assigned)
                    Next
                    slots.Free()
                    If assigned Then NoteWrite(expression, value)

                Case BoundKind.WithLValueExpressionPlaceholder, BoundKind.WithRValueExpressionPlaceholder
                    Dim substitute As BoundExpression = Me.GetPlaceholderSubstitute(DirectCast(node, BoundValuePlaceholderBase))
                    If substitute IsNot Nothing Then
                        Assign(substitute, value, assigned)
                    End If

                Case BoundKind.ByRefArgumentWithCopyBack
                    Assign(DirectCast(node, BoundByRefArgumentWithCopyBack).OriginalArgument, value, assigned)

                Case Else
                    ' Other kinds of left-hand-sides either represent things not tracked (e.g. array elements)
                    ' or errors that have been reported earlier (e.g. assignment to a unary increment)
            End Select
        End Sub

#End Region

#Region "Try statements"

        ' When assignments happen inside a region, they are treated as UN-assignments.
        ' Generally this is enough.
        ' 
        ' The tricky part is that any instruction in Try/Catch/Finally must be considered as potentially throwing.
        ' Even if a subsequent write outside of the region may kill an assignment inside the region, it cannot prevent escape of 
        ' the previous assignment.
        ' 
        ' === Example:
        ' 
        ' Dim x as integer  = 0
        ' Try
        '   [| region starts here
        '   x = 1
        '   Blah()              ' <- this statement may throw. (any instruction can).
        '   |] region ends here
        '   x = 2               ' <- it may seem that this assignment kills one above, where in fact "x = 1" can easily escape.
        ' Finally
        '   SomeUse(x)          ' we can see side effects of "x = 1" here
        ' End Try
        '
        ' As a result, when dealing with exception handling flows we need to maintain a separate state
        ' that collects UN-assignments only and is not affected by subsequent assignments.
        Private _tryState As OptionalState

        Protected Overrides Sub VisitTryBlock(tryBlock As BoundStatement, node As BoundTryStatement, ByRef _tryState As LocalState)
            If Me.TrackUnassignments Then

                ' store original try state
                Dim oldTryState As OptionalState = Me._tryState

                ' start with AllBitsSet (means there were no assignments)
                Me._tryState = AllBitsSet()
                ' visit try block. Any assignment inside the region will UN-assign corresponding bit in the tryState.
                MyBase.VisitTryBlock(tryBlock, node, _tryState)

                ' merge resulting tryState into tryState that we were given.
                Me.IntersectWith(_tryState, Me._tryState.Value)
                ' restore and merge old state with new changes.
                If oldTryState.HasValue Then
                    Dim tryState = Me._tryState.Value
                    Me.IntersectWith(tryState, oldTryState.Value)
                    Me._tryState = tryState
                Else
                    Me._tryState = oldTryState
                End If
            Else
                MyBase.VisitTryBlock(tryBlock, node, _tryState)
            End If
        End Sub

        Protected Overrides Sub VisitCatchBlock(catchBlock As BoundCatchBlock, ByRef finallyState As LocalState)
            If Me.TrackUnassignments Then

                Dim oldTryState As OptionalState = Me._tryState

                Me._tryState = AllBitsSet()
                Me.VisitCatchBlockInternal(catchBlock, finallyState)
                Me.IntersectWith(finallyState, Me._tryState.Value)

                If oldTryState.HasValue Then
                    Dim tryState = Me._tryState.Value
                    Me.IntersectWith(tryState, oldTryState.Value)
                    Me._tryState = tryState
                Else
                    Me._tryState = oldTryState
                End If
            Else
                Me.VisitCatchBlockInternal(catchBlock, finallyState)
            End If
        End Sub

        Private Sub VisitCatchBlockInternal(catchBlock As BoundCatchBlock, ByRef finallyState As LocalState)
            Dim local = catchBlock.LocalOpt
            If local IsNot Nothing Then
                GetOrCreateSlot(local)
            End If

            Dim exceptionSource = catchBlock.ExceptionSourceOpt
            If exceptionSource IsNot Nothing Then
                Assign(exceptionSource, value:=Nothing, assigned:=True)
            End If

            MyBase.VisitCatchBlock(catchBlock, finallyState)
        End Sub

        Protected Overrides Sub VisitFinallyBlock(finallyBlock As BoundStatement, ByRef unsetInFinally As LocalState)
            If Me.TrackUnassignments Then
                Dim oldTryState As OptionalState = Me._tryState

                Me._tryState = AllBitsSet()
                MyBase.VisitFinallyBlock(finallyBlock, unsetInFinally)
                Me.IntersectWith(unsetInFinally, Me._tryState.Value)

                If oldTryState.HasValue Then
                    Dim tryState = Me._tryState.Value
                    Me.IntersectWith(tryState, oldTryState.Value)
                    Me._tryState = tryState
                Else
                    Me._tryState = oldTryState
                End If
            Else
                MyBase.VisitFinallyBlock(finallyBlock, unsetInFinally)
            End If
        End Sub

#End Region

        Protected Overrides Function ReachableState() As LocalState
            Return New LocalState(BitVector.Empty)
        End Function

        Private Sub EnterParameters(parameters As ImmutableArray(Of ParameterSymbol))
            For Each parameter In parameters
                EnterParameter(parameter)
            Next
        End Sub

        Protected Overridable Sub EnterParameter(parameter As ParameterSymbol)
            ' this code has no effect except in the region analysis APIs, which assign
            ' variable slots to all parameters.
            Dim slot As Integer = VariableSlot(parameter)
            If slot >= SlotKind.FirstAvailable Then
                Me.State.Assign(slot)
            End If
            NoteWrite(parameter, Nothing)
        End Sub

        Private Sub LeaveParameters(parameters As ImmutableArray(Of ParameterSymbol))
            If Me.State.Reachable Then
                For Each parameter In parameters
                    LeaveParameter(parameter)
                Next
            End If
        End Sub

        Private Sub LeaveParameter(parameter As ParameterSymbol)
            If parameter.IsByRef Then
                Dim slot = VariableSlot(parameter)
                If Not Me.State.IsAssigned(slot) Then
                    ReportUnassignedByRefParameter(parameter)
                End If
                NoteRead(parameter)
            End If
        End Sub

        Protected Overrides Function UnreachableState() As LocalState
            '  at this point we don't know what size of the bitarray to use, so we only set 
            '  'reachable' flag which mean 'all bits are set' in intersect/union
            Return New LocalState(UnreachableBitsSet)
        End Function

        Protected Overrides Function AllBitsSet() As LocalState
            Dim result = New LocalState(BitVector.AllSet(nextVariableSlot))
            result.Unassign(SlotKind.Unreachable)
            Return result
        End Function

        Protected Overrides Sub VisitLocalInReadWriteContext(node As BoundLocal, rwContext As ReadWriteContext)
            MyBase.VisitLocalInReadWriteContext(node, rwContext)
            CheckAssigned(node.LocalSymbol, node.Syntax, rwContext)
        End Sub

        Public Overrides Function VisitLocal(node As BoundLocal) As BoundNode
            CheckAssigned(node.LocalSymbol, node.Syntax, ReadWriteContext.None)
            Return Nothing
        End Function

        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 Not node.WasCompilerGenerated Then
                CheckAssigned(node.RangeVariable, node.Syntax, ReadWriteContext.None)
            End If

            Return Nothing
        End Function

        ' Should this local variable be considered assigned at first? It is if in the set of initially
        ' assigned variables, or else the current state is not reachable.
        Protected Function ConsiderLocalInitiallyAssigned(variable As LocalSymbol) As Boolean
            Return Not Me.State.Reachable OrElse
                   initiallyAssignedVariables IsNot Nothing AndAlso initiallyAssignedVariables.Contains(variable)
        End Function

        Public Overrides Function VisitLocalDeclaration(node As BoundLocalDeclaration) As BoundNode
            Dim placeholder As BoundValuePlaceholderBase = Nothing
            Dim variableIsUsedDirectlyAndIsAlwaysAssigned = DeclaredVariableIsAlwaysAssignedBeforeInitializer(node.Syntax.Parent, node.InitializerOpt, placeholder)

            Dim local As LocalSymbol = node.LocalSymbol

            If variableIsUsedDirectlyAndIsAlwaysAssigned Then
                SetPlaceholderSubstitute(placeholder, New BoundLocal(node.Syntax, local, local.Type))
            End If

            ' Create a slot for the declared variable to be able to track it.
            Dim slot As Integer = GetOrCreateSlot(local)

            'If initializer is a lambda, we need to treat the local as assigned within the lambda.
            Assign(node, node.InitializerOpt,
                   ConsiderLocalInitiallyAssigned(local) OrElse
                   variableIsUsedDirectlyAndIsAlwaysAssigned OrElse
                   TreatTheLocalAsAssignedWithinTheLambda(local, node.InitializerOpt))

            If node.InitializerOpt IsNot Nothing OrElse node.InitializedByAsNew Then
                ' Assign must be called after the initializer is analyzed, because the variable needs to be
                ' consider unassigned which the initializer is being analyzed.
                MyBase.VisitLocalDeclaration(node)
            End If

            AssignLocalOnDeclaration(local, node)

            If variableIsUsedDirectlyAndIsAlwaysAssigned Then
                RemovePlaceholderSubstitute(placeholder)
            End If

            Return Nothing
        End Function

        Protected Overridable Function TreatTheLocalAsAssignedWithinTheLambda(local As LocalSymbol, right As BoundExpression) As Boolean
            Return IsConvertedLambda(right)
        End Function

        Private Shared Function IsConvertedLambda(value As BoundExpression) As Boolean
            If value Is Nothing Then
                Return False
            End If

            Do
                Select Case value.Kind
                    Case BoundKind.Lambda
                        Return True

                    Case BoundKind.Conversion
                        value = DirectCast(value, BoundConversion).Operand

                    Case BoundKind.DirectCast
                        value = DirectCast(value, BoundDirectCast).Operand

                    Case BoundKind.TryCast
                        value = DirectCast(value, BoundTryCast).Operand

                    Case BoundKind.Parenthesized
                        value = DirectCast(value, BoundParenthesized).Expression

                    Case Else
                        Return False
                End Select
            Loop
        End Function

        Friend Overridable Sub AssignLocalOnDeclaration(local As LocalSymbol, node As BoundLocalDeclaration)
            If node.InitializerOpt IsNot Nothing OrElse node.InitializedByAsNew Then
                Assign(node, node.InitializerOpt)
            End If
        End Sub

        Public Overrides Function VisitBlock(node As BoundBlock) As BoundNode
            ' Implicitly declared local variables don't have LocalDeclarations nodes for them. Thus,
            ' we need to create slots for any implicitly declared local variables in this block.
            ' For flow analysis purposes, we treat an implicit declared local as equivalent to a variable declared at the 
            ' start of the method body (this block) with no initializer.

            Dim localVariables = node.Locals
            For Each local In localVariables
                If local.IsImplicitlyDeclared Then
                    SetSlotState(GetOrCreateSlot(local), ConsiderLocalInitiallyAssigned(local))
                End If
            Next

            Return MyBase.VisitBlock(node)
        End Function

        Public Overrides Function VisitLambda(node As BoundLambda) As BoundNode
            Dim oldPending As SavedPending = SavePending()
            Dim oldSymbol = Me.symbol
            Me.symbol = node.LambdaSymbol
            Dim finalState As LocalState = Me.State
            Me.SetState(If(finalState.Reachable, finalState.Clone(), Me.AllBitsSet()))
            Me.State.Assigned(SlotKind.FunctionValue) = False
            Dim save_alreadyReportedFunctionValue = _alreadyReported(SlotKind.FunctionValue)
            _alreadyReported(SlotKind.FunctionValue) = False
            EnterParameters(node.LambdaSymbol.Parameters)
            VisitBlock(node.Body)
            LeaveParameters(node.LambdaSymbol.Parameters)
            Me.symbol = oldSymbol
            _alreadyReported(SlotKind.FunctionValue) = save_alreadyReportedFunctionValue
            Me.State.Assigned(SlotKind.FunctionValue) = True
            RestorePending(oldPending)
            Me.IntersectWith(finalState, Me.State)
            Me.SetState(finalState)
            Return Nothing
        End Function

        Public Overrides Function VisitQueryLambda(node As BoundQueryLambda) As BoundNode
            Dim oldSymbol = Me.symbol
            Me.symbol = node.LambdaSymbol
            Dim finalState As LocalState = Me.State.Clone()

            VisitRvalue(node.Expression)

            Me.symbol = oldSymbol
            Me.IntersectWith(finalState, Me.State)
            Me.SetState(finalState)
            Return Nothing
        End Function

        Public Overrides Function VisitQueryExpression(node As BoundQueryExpression) As BoundNode
            VisitRvalue(node.LastOperator)
            Return Nothing
        End Function

        Public Overrides Function VisitQuerySource(node As BoundQuerySource) As BoundNode
            VisitRvalue(node.Expression)
            Return Nothing
        End Function

        Public Overrides Function VisitQueryableSource(node As BoundQueryableSource) As BoundNode
            VisitRvalue(node.Source)
            Return Nothing
        End Function

        Public Overrides Function VisitToQueryableCollectionConversion(node As BoundToQueryableCollectionConversion) As BoundNode
            VisitRvalue(node.ConversionCall)
            Return Nothing
        End Function

        Public Overrides Function VisitQueryClause(node As BoundQueryClause) As BoundNode
            VisitRvalue(node.UnderlyingExpression)
            Return Nothing
        End Function

        Public Overrides Function VisitAggregateClause(node As BoundAggregateClause) As BoundNode
            If node.CapturedGroupOpt IsNot Nothing Then
                VisitRvalue(node.CapturedGroupOpt)
            End If

            VisitRvalue(node.UnderlyingExpression)
            Return Nothing
        End Function

        Public Overrides Function VisitOrdering(node As BoundOrdering) As BoundNode
            VisitRvalue(node.UnderlyingExpression)
            Return Nothing
        End Function

        Public Overrides Function VisitRangeVariableAssignment(node As BoundRangeVariableAssignment) As BoundNode
            VisitRvalue(node.Value)
            Return Nothing
        End Function

        Public Overrides Function VisitMeReference(node As BoundMeReference) As BoundNode
            CheckAssigned(MeParameter, node.Syntax)
            Return Nothing
        End Function

        Public Overrides Function VisitParameter(node As BoundParameter) As BoundNode
            If Not node.WasCompilerGenerated Then
                CheckAssigned(node.ParameterSymbol, node.Syntax)
            End If
            Return Nothing
        End Function

        Public Overrides Function VisitFieldAccess(node As BoundFieldAccess) As BoundNode
            Dim result = MyBase.VisitFieldAccess(node)

            If FieldAccessMayRequireTracking(node) Then
                CheckAssigned(node, node.Syntax, ReadWriteContext.None)
            End If

            Return result
        End Function

        Protected Overrides Sub VisitFieldAccessInReadWriteContext(node As BoundFieldAccess, rwContext As ReadWriteContext)
            MyBase.VisitFieldAccessInReadWriteContext(node, rwContext)

            If FieldAccessMayRequireTracking(node) Then
                CheckAssigned(node, node.Syntax, rwContext)
            End If
        End Sub

        Public Overrides Function VisitAssignmentOperator(node As BoundAssignmentOperator) As BoundNode
            Dim left As BoundExpression = node.Left

            'If right expression is a lambda, we need to treat the left side as assigned within the lambda.
            Dim treatLocalAsAssigned As Boolean = False
            If left.Kind = BoundKind.Local Then
                treatLocalAsAssigned = TreatTheLocalAsAssignedWithinTheLambda(
                                            DirectCast(left, BoundLocal).LocalSymbol, node.Right)
            End If

            If treatLocalAsAssigned Then
                Assign(left, node.Right)
            End If

            MyBase.VisitAssignmentOperator(node)

            If Not treatLocalAsAssigned Then
                Assign(left, node.Right)
            End If

            Return Nothing
        End Function

        Protected Overrides ReadOnly Property SuppressRedimOperandRvalueOnPreserve As Boolean
            Get
                Return True
            End Get
        End Property

        Public Overrides Function VisitRedimClause(node As BoundRedimClause) As BoundNode
            MyBase.VisitRedimClause(node)
            Assign(node.Operand, Nothing)
            Return Nothing
        End Function

        Public Overrides Function VisitReferenceAssignment(node As BoundReferenceAssignment) As BoundNode
            MyBase.VisitReferenceAssignment(node)
            Assign(node.ByRefLocal, node.LValue)
            Return Nothing
        End Function

        Protected Overrides Sub VisitForControlInitialization(node As BoundForToStatement)
            MyBase.VisitForControlInitialization(node)
            Assign(node.ControlVariable, node.InitialValue)
        End Sub

        Protected Overrides Sub VisitForControlInitialization(node As BoundForEachStatement)
            MyBase.VisitForControlInitialization(node)
            Assign(node.ControlVariable, Nothing)
        End Sub

        Protected Overrides Sub VisitForStatementVariableDeclaration(node As BoundForStatement)
            ' if the for each statement declared a variable explicitly or by using local type inference we'll
            ' need to create a new slot for the new variable that is initially unassigned.
            If node.DeclaredOrInferredLocalOpt IsNot Nothing Then
                Dim slot As Integer = GetOrCreateSlot(node.DeclaredOrInferredLocalOpt) 'not initially assigned
                Assign(node, Nothing, ConsiderLocalInitiallyAssigned(node.DeclaredOrInferredLocalOpt))
            End If
        End Sub

        Public Overrides Function VisitAnonymousTypeCreationExpression(node As BoundAnonymousTypeCreationExpression) As BoundNode
            MyBase.VisitAnonymousTypeCreationExpression(node)
            Return Nothing
        End Function

        Public Overrides Function VisitWithStatement(node As BoundWithStatement) As BoundNode
            Me.SetPlaceholderSubstitute(node.ExpressionPlaceholder, node.DraftPlaceholderSubstitute)
            MyBase.VisitWithStatement(node)
            Me.RemovePlaceholderSubstitute(node.ExpressionPlaceholder)
            Return Nothing
        End Function

        Public Overrides Function VisitUsingStatement(node As BoundUsingStatement) As BoundNode

            If node.ResourceList.IsDefaultOrEmpty Then
                ' only an expression. Let the base class' implementation deal with digging into the expression
                Return MyBase.VisitUsingStatement(node)
            End If

            ' all locals are considered initially assigned, even if the initialization expression is missing in source (error case)
            For Each variableDeclarations In node.ResourceList
                If variableDeclarations.Kind = BoundKind.AsNewLocalDeclarations Then
                    For Each variableDeclaration In DirectCast(variableDeclarations, BoundAsNewLocalDeclarations).LocalDeclarations
                        Dim local = variableDeclaration.LocalSymbol
                        Dim slot = GetOrCreateSlot(local)
                        If slot >= 0 Then
                            SetSlotAssigned(slot)
                            NoteWrite(local, Nothing)
                        Else
                            Debug.Assert(IsEmptyStructType(local.Type))
                        End If
                    Next
                Else
                    Dim local = DirectCast(variableDeclarations, BoundLocalDeclaration).LocalSymbol
                    Dim slot = GetOrCreateSlot(local)
                    If slot >= 0 Then
                        SetSlotAssigned(slot)
                        NoteWrite(local, Nothing)
                    Else
                        Debug.Assert(IsEmptyStructType(local.Type))
                    End If
                End If
            Next

            Dim result = MyBase.VisitUsingStatement(node)

            ' all locals are being read in the finally block.
            For Each variableDeclarations In node.ResourceList
                If variableDeclarations.Kind = BoundKind.AsNewLocalDeclarations Then
                    For Each variableDeclaration In DirectCast(variableDeclarations, BoundAsNewLocalDeclarations).LocalDeclarations
                        NoteRead(variableDeclaration.LocalSymbol)
                    Next
                Else
                    NoteRead(DirectCast(variableDeclarations, BoundLocalDeclaration).LocalSymbol)
                End If
            Next

            Return result
        End Function

        Protected Overridable ReadOnly Property IgnoreOutSemantics As Boolean
            Get
                Return True
            End Get
        End Property

        Protected NotOverridable Overrides Sub VisitArgument(arg As BoundExpression, p As ParameterSymbol)
            Debug.Assert(arg IsNot Nothing)
            If p.IsByRef Then
                If p.IsOut And Not Me.IgnoreOutSemantics Then
                    VisitLvalue(arg)
                Else
                    VisitRvalue(arg, rwContext:=ReadWriteContext.ByRefArgument)
                End If
            Else
                VisitRvalue(arg)
            End If
        End Sub

        Protected Overrides Sub WriteArgument(arg As BoundExpression, isOut As Boolean)
            If Not isOut Then
                CheckAssignedFromArgumentWrite(arg, arg.Syntax)
            End If

            Assign(arg, Nothing)
            MyBase.WriteArgument(arg, isOut)
        End Sub

        Protected Sub CheckAssignedFromArgumentWrite(expr As BoundExpression, node As SyntaxNode)
            If Not Me.State.Reachable Then
                Return
            End If

            Select Case expr.Kind
                Case BoundKind.Local
                    CheckAssigned(DirectCast(expr, BoundLocal).LocalSymbol, node)

                Case BoundKind.Parameter
                    CheckAssigned(DirectCast(expr, BoundParameter).ParameterSymbol, node)

                Case BoundKind.FieldAccess
                    Dim fieldAccess = DirectCast(expr, BoundFieldAccess)
                    Dim field As FieldSymbol = fieldAccess.FieldSymbol
                    If FieldAccessMayRequireTracking(fieldAccess) Then
                        CheckAssigned(fieldAccess, node, ReadWriteContext.ByRefArgument)
                    End If

                Case BoundKind.EventAccess
                    Throw ExceptionUtilities.UnexpectedValue(expr.Kind) ' TODO: is this reachable at all?

                Case BoundKind.MeReference,
                     BoundKind.MyClassReference,
                     BoundKind.MyBaseReference
                    CheckAssigned(MeParameter, node)

            End Select
        End Sub

        Protected Overrides Sub VisitLateBoundArgument(arg As BoundExpression, isByRef As Boolean)
            If isByRef Then
                VisitRvalue(arg, rwContext:=ReadWriteContext.ByRefArgument)
            Else
                VisitRvalue(arg)
            End If
        End Sub

        Public Overrides Function VisitReturnStatement(node As BoundReturnStatement) As BoundNode
            If Not node.IsEndOfMethodReturn Then
                ' We should mark it as set disregarding whether or not there is an expression; if there 
                ' is no expression, error 30654 will be generated which in Dev10 suppresses this error
                SetSlotState(SlotKind.FunctionValue, True)

            Else
                Dim functionLocal = TryCast(node.ExpressionOpt, BoundLocal)
                If functionLocal IsNot Nothing Then
                    CheckAssignedFunctionValue(functionLocal.LocalSymbol, node.Syntax)
                End If
            End If

            MyBase.VisitReturnStatement(node)
            Return Nothing
        End Function

        Public Overrides Function VisitMyBaseReference(node As BoundMyBaseReference) As BoundNode
            CheckAssigned(MeParameter, node.Syntax)
            Return Nothing
        End Function

        Public Overrides Function VisitMyClassReference(node As BoundMyClassReference) As BoundNode
            CheckAssigned(MeParameter, node.Syntax)
            Return Nothing
        End Function

        ''' <summary>
        ''' A variable declared with As New can be considered assigned before the initializer is executed in case the variable
        ''' is a value type. The reason is that in this case the initialization happens in place (not in a temporary) and 
        ''' the variable already got the object creation expression assigned.
        ''' </summary>
        Private Function DeclaredVariableIsAlwaysAssignedBeforeInitializer(syntax As SyntaxNode, boundInitializer As BoundExpression,
                                                                           <Out> ByRef placeholder As BoundValuePlaceholderBase) As Boolean
            placeholder = Nothing
            If boundInitializer IsNot Nothing AndAlso
                (boundInitializer.Kind = BoundKind.ObjectCreationExpression OrElse boundInitializer.Kind = BoundKind.NewT) Then
                Dim boundInitializerBase = DirectCast(boundInitializer, BoundObjectCreationExpressionBase).InitializerOpt

                If boundInitializerBase IsNot Nothing AndAlso boundInitializerBase.Kind = BoundKind.ObjectInitializerExpression Then
                    Dim objectInitializer = DirectCast(boundInitializerBase, BoundObjectInitializerExpression)

                    If syntax IsNot Nothing AndAlso syntax.Kind = SyntaxKind.VariableDeclarator Then
                        Dim declarator = DirectCast(syntax, VariableDeclaratorSyntax)

                        If declarator.AsClause IsNot Nothing AndAlso declarator.AsClause.Kind = SyntaxKind.AsNewClause Then
                            placeholder = objectInitializer.PlaceholderOpt
                            Return Not objectInitializer.CreateTemporaryLocalForInitialization
                        End If
                    End If
                End If
            End If

            Return False
        End Function

        Public Overrides Function VisitAsNewLocalDeclarations(node As BoundAsNewLocalDeclarations) As BoundNode
            Dim placeholder As BoundValuePlaceholderBase = Nothing
            Dim variableIsUsedDirectlyAndIsAlwaysAssigned =
                DeclaredVariableIsAlwaysAssignedBeforeInitializer(node.Syntax, node.Initializer, placeholder)

            Dim declarations As ImmutableArray(Of BoundLocalDeclaration) = node.LocalDeclarations
            If declarations.IsEmpty Then
                Return Nothing
            End If

            ' An initializer might access the declared variables in the initializer and therefore need to create a slot to 
            ' track them
            For Each localDeclaration In declarations
                GetOrCreateSlot(localDeclaration.LocalSymbol)
            Next

            ' NOTE: in case 'variableIsUsedDirectlyAndIsAlwaysAssigned' is set it must be 
            '           Dim a[, b, ...] As New Struct(...) With { ... }. 
            '
            '       In this case Roslyn (following Dev10/Dev11) consecutively calls constructors 
            '       and executes initializers for all declared variables. Thus, when the first 
            '       initializer is being executed, all other locals are not assigned yet.
            '
            '       This behavior is emulated below by assigning the first local and visiting
            '       the initializer before assigning all the other locals. We don't really need
            '       to revisit initializer after because all errors/warnings will be property 
            '       reported in the first visit.
            Dim localToUseAsSubstitute As LocalSymbol = CreateLocalSymbolForVariables(declarations)

            ' process the first local declaration once
            Dim localDecl As BoundLocalDeclaration = declarations(0)

            ' A valuetype variable declared with AsNew and initialized by an object initializer is considered
            ' assigned when visiting the initializer, because the initialization happens inplace and the
            ' variable already got the object creation expression assigned (this assignment will be created 
            ' in the local rewriter).
            Assign(localDecl, node.Initializer, ConsiderLocalInitiallyAssigned(localDecl.LocalSymbol) OrElse variableIsUsedDirectlyAndIsAlwaysAssigned)

            ' if needed, replace the placeholders with the bound local for the current declared variable.
            If variableIsUsedDirectlyAndIsAlwaysAssigned Then
                Me.SetPlaceholderSubstitute(placeholder, New BoundLocal(localDecl.Syntax, localToUseAsSubstitute, localToUseAsSubstitute.Type))
            End If

            VisitRvalue(node.Initializer)
            Visit(localDecl)  ' TODO: do we really need that?

            ' Visit all other declarations
            For index = 1 To declarations.Length - 1
                localDecl = declarations(index)

                ' A valuetype variable declared with AsNew and initialized by an object initializer is considered
                ' assigned when visiting the initializer, because the initialization happens inplace and the
                ' variable already got the object creation expression assigned (this assignment will be created 
                'in the local rewriter).
                Assign(localDecl, node.Initializer, ConsiderLocalInitiallyAssigned(localDecl.LocalSymbol) OrElse variableIsUsedDirectlyAndIsAlwaysAssigned)

                Visit(localDecl) ' TODO: do we really need that?
            Next

            ' We also need to mark all 

            If variableIsUsedDirectlyAndIsAlwaysAssigned Then
                Me.RemovePlaceholderSubstitute(placeholder)
            End If

            Return Nothing
        End Function

        Protected Overridable Function CreateLocalSymbolForVariables(declarations As ImmutableArray(Of BoundLocalDeclaration)) As LocalSymbol
            Debug.Assert(declarations.Length > 0)
            Return declarations(0).LocalSymbol
        End Function

        Protected Overrides Sub VisitObjectCreationExpressionInitializer(node As BoundObjectInitializerExpressionBase)
            Dim resetPlaceholder As Boolean = node IsNot Nothing AndAlso
                                              node.Kind = BoundKind.ObjectInitializerExpression AndAlso
                                              DirectCast(node, BoundObjectInitializerExpression).CreateTemporaryLocalForInitialization

            If resetPlaceholder Then
                Me.SetPlaceholderSubstitute(DirectCast(node, BoundObjectInitializerExpression).PlaceholderOpt, Nothing) ' Override substitute
            End If

            MyBase.VisitObjectCreationExpressionInitializer(node)

            If resetPlaceholder Then
                Me.RemovePlaceholderSubstitute(DirectCast(node, BoundObjectInitializerExpression).PlaceholderOpt)
            End If
        End Sub

        Public Overrides Function VisitOnErrorStatement(node As BoundOnErrorStatement) As BoundNode
            _seenOnErrorOrResume = True
            Return MyBase.VisitOnErrorStatement(node)
        End Function

        Public Overrides Function VisitResumeStatement(node As BoundResumeStatement) As BoundNode
            _seenOnErrorOrResume = True
            Return MyBase.VisitResumeStatement(node)
        End Function
    End Class

End Namespace
