File: Lowering\Diagnostics\DiagnosticsPass.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Linq
Imports System.Runtime.InteropServices
Imports System.Text
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Roslyn.Utilities
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend NotInheritable Class DiagnosticsPass
        Inherits BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
 
        Private ReadOnly _diagnostics As DiagnosticBag
        Private ReadOnly _compilation As VisualBasicCompilation
        Private _containingSymbol As MethodSymbol
        Private _withExpressionPlaceholderMap As Dictionary(Of BoundValuePlaceholderBase, BoundWithStatement)
        Private _expressionsBeingVisited As Stack(Of BoundExpression)
        Private _insideNameof As Boolean = False
 
        Private _inExpressionLambda As Boolean
 
        Public Shared Sub IssueDiagnostics(node As BoundNode, diagnostics As DiagnosticBag, containingSymbol As MethodSymbol)
            Debug.Assert(node IsNot Nothing)
            Debug.Assert(containingSymbol IsNot Nothing)
 
            Try
                Dim diagnosticPass As New DiagnosticsPass(containingSymbol.DeclaringCompilation, diagnostics, containingSymbol)
                diagnosticPass.Visit(node)
            Catch ex As CancelledByStackGuardException
                ex.AddAnError(diagnostics)
            End Try
        End Sub
 
        Private Sub New(compilation As VisualBasicCompilation, diagnostics As DiagnosticBag, containingSymbol As MethodSymbol)
            Me._compilation = compilation
            Me._diagnostics = diagnostics
            Me._containingSymbol = containingSymbol
 
            Me._inExpressionLambda = False
        End Sub
 
        Private ReadOnly Property IsInExpressionLambda As Boolean
            Get
                Return Me._inExpressionLambda
            End Get
        End Property
 
        Public Overrides Function VisitQueryLambda(node As BoundQueryLambda) As BoundNode
            Dim save_containingSymbol = Me._containingSymbol
            Me._containingSymbol = node.LambdaSymbol
            Me.Visit(node.Expression)
            Me._containingSymbol = save_containingSymbol
            Return Nothing
        End Function
 
        Public Overrides Function VisitParameter(node As BoundParameter) As BoundNode
            Dim parameterSymbol As ParameterSymbol = node.ParameterSymbol
 
            If parameterSymbol.IsByRef Then
                ' cannot access ByRef parameters in Lambdas
                Dim parameterSymbolContainingSymbol As Symbol = parameterSymbol.ContainingSymbol
 
                If _containingSymbol IsNot parameterSymbolContainingSymbol AndAlso Not _insideNameof Then
                    ' Need to go up the chain of containers and see if the last lambda we see
                    ' is a QueryLambda, before we reach parameter's container. 
                    If Binder.IsTopMostEnclosingLambdaAQueryLambda(_containingSymbol, parameterSymbolContainingSymbol) Then
                        Binder.ReportDiagnostic(Me._diagnostics, node.Syntax, ERRID.ERR_CannotLiftByRefParamQuery1, parameterSymbol.Name)
                    Else
                        Binder.ReportDiagnostic(Me._diagnostics, node.Syntax, ERRID.ERR_CannotLiftByRefParamLambda1, parameterSymbol.Name)
                    End If
                End If
            End If
 
            Return MyBase.VisitParameter(node)
        End Function
 
        Public Overrides Function VisitMeReference(node As BoundMeReference) As BoundNode
            Dim errorId As ERRID = GetMeAccessError()
 
            If errorId <> ERRID.ERR_None Then
                Binder.ReportDiagnostic(Me._diagnostics, node.Syntax, errorId)
            End If
 
            Return MyBase.VisitMeReference(node)
        End Function
 
        Public Overrides Function VisitMyClassReference(node As BoundMyClassReference) As BoundNode
            Dim errorId As ERRID = GetMeAccessError()
 
            If errorId <> ERRID.ERR_None Then
                Binder.ReportDiagnostic(Me._diagnostics, node.Syntax, errorId)
            End If
 
            Return MyBase.VisitMyClassReference(node)
        End Function
 
        Private Function GetMeAccessError() As ERRID
            Dim meParameter As ParameterSymbol = Me._containingSymbol.MeParameter
            If meParameter IsNot Nothing AndAlso meParameter.IsByRef Then
                If _containingSymbol.MethodKind = MethodKind.LambdaMethod Then
                    ' Need to go up the chain of containers and see if the last lambda we see
                    ' is a QueryLambda, before we reach a type member. 
                    If Binder.IsTopMostEnclosingLambdaAQueryLambda(_containingSymbol, Nothing) Then
                        Return ERRID.ERR_CannotLiftStructureMeQuery
                    Else
                        Return ERRID.ERR_CannotLiftStructureMeLambda
                    End If
                End If
            End If
 
            Return ERRID.ERR_None
        End Function
 
        Public Overrides Function VisitWithRValueExpressionPlaceholder(node As BoundWithRValueExpressionPlaceholder) As BoundNode
            CheckMeAccessInWithExpression(node)
            Return MyBase.VisitWithRValueExpressionPlaceholder(node)
        End Function
 
        Private Sub CheckMeAccessInWithExpression(node As BoundValuePlaceholderBase)
            Dim trackedWithStatement As BoundWithStatement = Nothing
 
            If _withExpressionPlaceholderMap IsNot Nothing AndAlso _withExpressionPlaceholderMap.TryGetValue(node, trackedWithStatement) Then
                Dim withBlockBinder As WithBlockBinder = trackedWithStatement.Binder
 
                If withBlockBinder.ContainingMember IsNot _containingSymbol Then
                    ' If With statement expression is accessed from a different symbol,
                    ' we need to check for value-typed Me reference being lifted
                    If Not withBlockBinder.IsInLambda Then
                        Debug.Assert(_containingSymbol.IsLambdaMethod) ' What else can it be?
 
                        Dim containingType = withBlockBinder.ContainingType
                        If containingType IsNot Nothing AndAlso containingType.IsValueType AndAlso withBlockBinder.Info.ExpressionHasByRefMeReference(RecursionDepth) Then
                            Dim errorId As ERRID = GetMeAccessError()
                            If errorId <> ERRID.ERR_None Then
                                ' The placeholder node will actually use syntax from With statement and it might have some wrappers and conversions on top of it with the same syntax.
                                ' Let's use syntax of parent node instead. 
                                Dim errorSyntax As SyntaxNode = node.Syntax
 
                                For Each expr In _expressionsBeingVisited
                                    If expr.Syntax IsNot errorSyntax Then
                                        errorSyntax = expr.Syntax
                                        Exit For
                                    End If
                                Next
 
                                Binder.ReportDiagnostic(Me._diagnostics, errorSyntax, errorId)
                            End If
                        End If
                    End If
                End If
            End If
        End Sub
 
        Public Overrides Function VisitWithStatement(node As BoundWithStatement) As BoundNode
            Me.Visit(node.OriginalExpression)
 
            Dim info As WithBlockBinder.WithBlockInfo = node.Binder.Info
 
            If info IsNot Nothing AndAlso info.ExpressionIsAccessedFromNestedLambda Then
                If _withExpressionPlaceholderMap Is Nothing Then
                    _withExpressionPlaceholderMap = New Dictionary(Of BoundValuePlaceholderBase, BoundWithStatement)()
                    _expressionsBeingVisited = New Stack(Of BoundExpression)()
                End If
 
                Debug.Assert(info.ExpressionPlaceholder IsNot Nothing)
                _withExpressionPlaceholderMap.Add(info.ExpressionPlaceholder, node)
            Else
                info = Nothing
            End If
 
            Me.Visit(node.Body)
 
            If info IsNot Nothing Then
                _withExpressionPlaceholderMap.Remove(info.ExpressionPlaceholder)
            End If
 
            Return Nothing
        End Function
 
        Public Overrides Function VisitNameOfOperator(node As BoundNameOfOperator) As BoundNode
            _insideNameof = True
            Dim r = MyBase.VisitNameOfOperator(node)
            _insideNameof = False
            Return r
        End Function
 
        Public Overrides Function Visit(node As BoundNode) As BoundNode
            Dim pop As Boolean = False
 
            If _withExpressionPlaceholderMap IsNot Nothing AndAlso _withExpressionPlaceholderMap.Count > 0 Then
                Dim expr = TryCast(node, BoundExpression)
 
                If expr IsNot Nothing Then
                    _expressionsBeingVisited.Push(expr)
                    pop = True
                End If
            End If
 
            Dim result = MyBase.Visit(node)
 
            If pop Then
                Debug.Assert(_expressionsBeingVisited.Peek Is node)
                _expressionsBeingVisited.Pop()
            End If
 
            Return result
        End Function
 
    End Class
 
End Namespace