|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic
'This portion of the binder converts StatementSyntax nodes into BoundStatements
Partial Friend Class Binder
' !!! PLEASE KEEP BindStatement FUNCTION AT THE TOP !!!
''' <summary>
''' The dispatcher method that handles syntax nodes for all stand-alone statements.
''' </summary>
Public Overridable Function BindStatement(node As StatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Debug.Assert(node IsNot Nothing)
Select Case node.Kind
Case SyntaxKind.SimpleAssignmentStatement,
SyntaxKind.AddAssignmentStatement,
SyntaxKind.SubtractAssignmentStatement,
SyntaxKind.MultiplyAssignmentStatement,
SyntaxKind.DivideAssignmentStatement,
SyntaxKind.IntegerDivideAssignmentStatement,
SyntaxKind.ExponentiateAssignmentStatement,
SyntaxKind.LeftShiftAssignmentStatement,
SyntaxKind.RightShiftAssignmentStatement,
SyntaxKind.ConcatenateAssignmentStatement
Return BindAssignmentStatement(DirectCast(node, AssignmentStatementSyntax), diagnostics)
Case SyntaxKind.MidAssignmentStatement
Return BindMidAssignmentStatement(DirectCast(node, AssignmentStatementSyntax), diagnostics)
Case SyntaxKind.AddHandlerStatement,
SyntaxKind.RemoveHandlerStatement
Return BindAddRemoveHandlerStatement(DirectCast(node, AddRemoveHandlerStatementSyntax), diagnostics)
Case SyntaxKind.RaiseEventStatement
Return BindRaiseEventStatement(DirectCast(node, RaiseEventStatementSyntax), diagnostics)
Case SyntaxKind.PrintStatement
Return BindPrintStatement(DirectCast(node, PrintStatementSyntax), diagnostics)
Case SyntaxKind.ExpressionStatement
Return BindExpressionStatement(DirectCast(node, ExpressionStatementSyntax), diagnostics)
Case SyntaxKind.CallStatement
Return BindCallStatement(DirectCast(node, CallStatementSyntax), diagnostics)
Case SyntaxKind.GoToStatement
Return BindGoToStatement(DirectCast(node, GoToStatementSyntax), diagnostics)
Case SyntaxKind.LabelStatement
Return BindLabelStatement(DirectCast(node, LabelStatementSyntax), diagnostics)
Case SyntaxKind.SingleLineIfStatement
Return BindSingleLineIfStatement(DirectCast(node, SingleLineIfStatementSyntax), diagnostics)
Case SyntaxKind.MultiLineIfBlock
Return BindMultiLineIfBlock(DirectCast(node, MultiLineIfBlockSyntax), diagnostics)
Case SyntaxKind.ElseIfStatement
' ElseIf without a preceding If.
Debug.Assert(IsSemanticModelBinder OrElse node.ContainsDiagnostics)
Dim condition = BindBooleanExpression(DirectCast(node, ElseIfStatementSyntax).Condition, diagnostics)
Return New BoundBadStatement(node, ImmutableArray.Create(Of BoundNode)(condition), hasErrors:=True)
Case SyntaxKind.SelectBlock
Return BindSelectBlock(DirectCast(node, SelectBlockSyntax), diagnostics)
Case SyntaxKind.CaseStatement
Return BindStandAloneCaseStatement(DirectCast(node, CaseStatementSyntax), diagnostics)
Case SyntaxKind.LocalDeclarationStatement
Return BindLocalDeclaration(DirectCast(node, LocalDeclarationStatementSyntax), diagnostics)
Case SyntaxKind.SimpleDoLoopBlock,
SyntaxKind.DoWhileLoopBlock,
SyntaxKind.DoUntilLoopBlock,
SyntaxKind.DoLoopWhileBlock,
SyntaxKind.DoLoopUntilBlock
Return BindDoLoop(DirectCast(node, DoLoopBlockSyntax), diagnostics)
Case SyntaxKind.WhileBlock
Return BindWhileBlock(DirectCast(node, WhileBlockSyntax), diagnostics)
Case SyntaxKind.ForBlock
Return BindForToBlock(DirectCast(node, ForOrForEachBlockSyntax), diagnostics)
Case SyntaxKind.ForEachBlock
Return BindForEachBlock(DirectCast(node, ForOrForEachBlockSyntax), diagnostics)
Case SyntaxKind.WithBlock
Return BindWithBlock(DirectCast(node, WithBlockSyntax), diagnostics)
Case SyntaxKind.UsingBlock
Return BindUsingBlock(DirectCast(node, UsingBlockSyntax), diagnostics)
Case SyntaxKind.SyncLockBlock
Return BindSyncLockBlock(DirectCast(node, SyncLockBlockSyntax), diagnostics)
Case SyntaxKind.TryBlock
Return BindTryBlock(DirectCast(node, TryBlockSyntax), diagnostics)
Case SyntaxKind.ExitDoStatement,
SyntaxKind.ExitForStatement,
SyntaxKind.ExitSelectStatement,
SyntaxKind.ExitTryStatement,
SyntaxKind.ExitWhileStatement,
SyntaxKind.ExitFunctionStatement,
SyntaxKind.ExitSubStatement,
SyntaxKind.ExitPropertyStatement
Return BindExitStatement(DirectCast(node, ExitStatementSyntax), diagnostics)
Case SyntaxKind.ContinueDoStatement, SyntaxKind.ContinueForStatement, SyntaxKind.ContinueWhileStatement
Return BindContinueStatement(DirectCast(node, ContinueStatementSyntax), diagnostics)
Case SyntaxKind.ReturnStatement
Return BindReturn(DirectCast(node, ReturnStatementSyntax), diagnostics)
Case SyntaxKind.YieldStatement
Return BindYield(DirectCast(node, YieldStatementSyntax), diagnostics)
Case SyntaxKind.ThrowStatement
Return BindThrow(DirectCast(node, ThrowStatementSyntax), diagnostics)
Case SyntaxKind.ErrorStatement
Return BindError(DirectCast(node, ErrorStatementSyntax), diagnostics)
Case SyntaxKind.EmptyStatement
Return New BoundNoOpStatement(node)
Case SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.ConstructorBlock,
SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.AddHandlerAccessorBlock,
SyntaxKind.RemoveHandlerAccessorBlock,
SyntaxKind.RaiseEventAccessorBlock,
SyntaxKind.OperatorBlock
Return BindMethodBlock(DirectCast(node, MethodBlockBaseSyntax), diagnostics)
Case SyntaxKind.ReDimStatement, SyntaxKind.ReDimPreserveStatement
Return BindRedimStatement(DirectCast(node, ReDimStatementSyntax), diagnostics)
Case SyntaxKind.EraseStatement
Return BindEraseStatement(DirectCast(node, EraseStatementSyntax), diagnostics)
Case SyntaxKind.NextStatement,
SyntaxKind.EndIfStatement,
SyntaxKind.EndSelectStatement,
SyntaxKind.EndTryStatement,
SyntaxKind.EndUsingStatement,
SyntaxKind.EndWhileStatement,
SyntaxKind.EndWithStatement,
SyntaxKind.EndSyncLockStatement,
SyntaxKind.EndNamespaceStatement,
SyntaxKind.EndModuleStatement,
SyntaxKind.EndClassStatement,
SyntaxKind.EndStructureStatement,
SyntaxKind.EndInterfaceStatement,
SyntaxKind.EndEnumStatement,
SyntaxKind.EndSubStatement,
SyntaxKind.EndFunctionStatement,
SyntaxKind.EndOperatorStatement,
SyntaxKind.EndPropertyStatement,
SyntaxKind.EndGetStatement,
SyntaxKind.EndSetStatement,
SyntaxKind.EndEventStatement,
SyntaxKind.EndAddHandlerStatement,
SyntaxKind.EndRemoveHandlerStatement,
SyntaxKind.EndRaiseEventStatement,
SyntaxKind.FinallyStatement,
SyntaxKind.IncompleteMember
' This can happen for two reasons:
' 1. if there are more end block statements than block statements in source
' 2. we have nested blocks where the inner one contains a statement that closes a block above the
' nested block (e.g. two nested SyncLocks + end sub in the inner one). Then there will be parser
' generated "End XXX" statements which are only marked as "missing", because the diagnostic will be
' attached to the begin of the block. Both of these missing end statements have a 0 width which causes
' them to be in the same region of a textspan. In that situation the inner end statement may be bound
' separately (outside of the block context) and we need to relax the assertion below to allow missing
' end statements as well (the list of statements was taken from the parser method CreateMissingEnd,
' where only the ones that can appear in a method body have been selected).
'
' We simply need to ignore this, the error is already created by the parser.
Debug.Assert(IsSemanticModelBinder OrElse node.ContainsDiagnostics OrElse
(node.IsMissing AndAlso
(node.Parent.Kind = SyntaxKind.MultiLineSubLambdaExpression OrElse
node.Parent.Kind = SyntaxKind.MultiLineFunctionLambdaExpression OrElse
node.Parent.Kind = SyntaxKind.AddHandlerAccessorBlock OrElse
node.Parent.Kind = SyntaxKind.RemoveHandlerAccessorBlock OrElse
node.Parent.Kind = SyntaxKind.RaiseEventAccessorBlock OrElse
node.Parent.Kind = SyntaxKind.MultiLineIfBlock OrElse
node.Parent.Kind = SyntaxKind.ElseIfBlock OrElse
node.Parent.Kind = SyntaxKind.ElseBlock OrElse
node.Parent.Kind = SyntaxKind.SimpleDoLoopBlock OrElse
node.Parent.Kind = SyntaxKind.DoWhileLoopBlock OrElse
node.Parent.Kind = SyntaxKind.DoUntilLoopBlock OrElse
node.Parent.Kind = SyntaxKind.WhileBlock OrElse
node.Parent.Kind = SyntaxKind.WithBlock OrElse
node.Parent.Kind = SyntaxKind.ForBlock OrElse
node.Parent.Kind = SyntaxKind.ForEachBlock OrElse
node.Parent.Kind = SyntaxKind.SyncLockBlock OrElse
node.Parent.Kind = SyntaxKind.SelectBlock OrElse
node.Parent.Kind = SyntaxKind.TryBlock OrElse
node.Parent.Kind = SyntaxKind.UsingBlock)))
Return New BoundBadStatement(node, ImmutableArray(Of BoundNode).Empty, hasErrors:=True)
Case SyntaxKind.SimpleLoopStatement,
SyntaxKind.LoopWhileStatement,
SyntaxKind.LoopUntilStatement
' a loop statement is legal as long as it is part of a do loop block
If Not SyntaxFacts.IsDoLoopBlock(node.Parent.Kind) Then
Debug.Assert(node.ContainsDiagnostics)
Dim whileOrUntilClause = DirectCast(node, LoopStatementSyntax).WhileOrUntilClause
Dim childNodes = If(whileOrUntilClause Is Nothing,
ImmutableArray(Of BoundNode).Empty,
ImmutableArray.Create(Of BoundNode)(BindBooleanExpression(whileOrUntilClause.Condition, diagnostics)))
Return New BoundBadStatement(node, childNodes, hasErrors:=True)
End If
Case SyntaxKind.CatchStatement
' a catch statement is legal as long as it is part of a catch block
If Not node.Parent.Kind = SyntaxKind.CatchBlock Then
Debug.Assert(node.ContainsDiagnostics)
Dim whenClause = DirectCast(node, CatchStatementSyntax).WhenClause
Dim childNodes = If(whenClause Is Nothing,
ImmutableArray(Of BoundNode).Empty,
ImmutableArray.Create(Of BoundNode)(BindBooleanExpression(whenClause.Filter, diagnostics)))
Return New BoundBadStatement(node, childNodes, hasErrors:=True)
End If
Case SyntaxKind.ResumeStatement, SyntaxKind.ResumeNextStatement, SyntaxKind.ResumeLabelStatement
Return BindResumeStatement(DirectCast(node, ResumeStatementSyntax), diagnostics)
Case SyntaxKind.OnErrorGoToZeroStatement, SyntaxKind.OnErrorGoToMinusOneStatement,
SyntaxKind.OnErrorGoToLabelStatement, SyntaxKind.OnErrorResumeNextStatement
Return BindOnErrorStatement(node, diagnostics)
Case SyntaxKind.StopStatement
Return BindStopStatement(DirectCast(node, StopOrEndStatementSyntax))
Case SyntaxKind.EndStatement
Return BindEndStatement(DirectCast(node, StopOrEndStatementSyntax), diagnostics)
End Select
' NOTE: Our normal pattern would be to add cases for all of the SyntaxKinds that we know we're
' not handling here and then throwing ExceptionUtilities.UnexpectedValue in the else case, but
' there are just too many statement SyntaxKinds in VB (e.g. declarations, statements corresponding
' to blocks handled above, etc).
Debug.Assert(IsSemanticModelBinder OrElse node.ContainsDiagnostics)
Return New BoundBadStatement(node, ImmutableArray(Of BoundNode).Empty, hasErrors:=True)
End Function
Private Function BindStandAloneCaseStatement(caseStatement As CaseStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundBadStatement
' Valid Case statement within Select Case statement is handled in BindSelectBlock.
' We should reach here only for invalid Case statements which are not inside any SelectBlock.
' Parser must have already reported error ERRID.ERR_CaseNoSelect or ERRID.ERR_SubRequiresSingleStatement.
Debug.Assert(caseStatement.ContainsDiagnostics)
Dim statement As BoundCaseStatement = BindCaseStatement(caseStatement, selectExpressionOpt:=Nothing, convertCaseElements:=False, diagnostics:=diagnostics)
Dim children = ArrayBuilder(Of BoundNode).GetInstance(statement.CaseClauses.Length)
For Each clause As BoundCaseClause In statement.CaseClauses
Select Case clause.Kind
Case BoundKind.SimpleCaseClause, BoundKind.RelationalCaseClause
children.Add(DirectCast(clause, BoundSingleValueCaseClause).ValueOpt)
Case BoundKind.RangeCaseClause
Dim range = DirectCast(clause, BoundRangeCaseClause)
children.Add(range.LowerBoundOpt)
children.Add(range.UpperBoundOpt)
Case Else
Throw ExceptionUtilities.UnexpectedValue(clause.Kind)
End Select
Next
Return New BoundBadStatement(caseStatement, children.ToImmutableAndFree(), hasErrors:=True)
End Function
Private Function BindMethodBlock(methodBlock As MethodBlockBaseSyntax, diagnostics As BindingDiagnosticBag) As BoundBlock
Dim statements As ArrayBuilder(Of BoundStatement) = ArrayBuilder(Of BoundStatement).GetInstance
Dim locals As ImmutableArray(Of LocalSymbol) = ImmutableArray(Of LocalSymbol).Empty
Dim methodSymbol = DirectCast(ContainingMember, MethodSymbol)
Dim localForFunctionValue As LocalSymbol
If methodSymbol.IsIterator OrElse (methodSymbol.IsAsync AndAlso methodSymbol.ReturnType.Equals(Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task))) Then
' We are actually not using FunctionValue of such method in Return statements and referencing it explicitly is an error.
localForFunctionValue = Nothing
Else
localForFunctionValue = Me.GetLocalForFunctionValue()
End If
If localForFunctionValue IsNot Nothing Then
' Declare local variable for function return
Dim localDeclaration = New BoundLocalDeclaration(methodBlock.BlockStatement,
localForFunctionValue,
Nothing)
localDeclaration.SetWasCompilerGenerated()
statements.Add(localDeclaration)
End If
Dim blockBinder = Me.GetBinder(DirectCast(methodBlock, VisualBasicSyntaxNode))
Dim body = blockBinder.BindBlock(methodBlock, methodBlock.Statements, diagnostics)
' Implicit label to branch to for Exit Sub/Exit Function statements.
Dim exitLabelStatement = New BoundLabelStatement(methodBlock.EndBlockStatement, blockBinder.GetReturnLabel())
If body IsNot Nothing Then
' See if we have to generate OnError handler
Dim containsAwait As Boolean
Dim containsOnError As Boolean ' The block contains an [On Error] statement.
Dim containsResume As Boolean ' The block contains a [Resume [...]] or an [On Error Resume Next] statement.
Dim resumeWithoutLabel As StatementSyntax = Nothing ' The first [Resume], [Resume Next] or [On Error Resume Next] statement, if any.
Dim containsLineNumberLabel As Boolean
Dim containsCatch As Boolean
Dim reportedAnError As Boolean
CheckOnErrorAndAwaitWalker.VisitBlock(blockBinder, body, diagnostics,
containsAwait, containsOnError, containsResume, resumeWithoutLabel,
containsLineNumberLabel, containsCatch,
reportedAnError)
If blockBinder.IsInAsyncContext() AndAlso Not blockBinder.IsInIteratorContext() AndAlso
Not containsAwait AndAlso Not body.HasErrors AndAlso
TypeOf methodBlock.BlockStatement Is MethodStatementSyntax Then
ReportDiagnostic(diagnostics, DirectCast(methodBlock.BlockStatement, MethodStatementSyntax).Identifier, ERRID.WRN_AsyncLacksAwaits)
End If
If Not reportedAnError AndAlso
(containsOnError OrElse containsResume OrElse (containsCatch AndAlso containsLineNumberLabel)) Then
' This method uses Unstructured Exception-Handling or needs to track line number
' Note that in constructors this handler does not extend over the call to New at the beginning
' of the constructor.
If methodSymbol.MethodKind = MethodKind.Constructor Then
Dim hasMyBaseConstructorCall As Boolean = False
If InitializerRewriter.HasExplicitMeConstructorCall(body, ContainingMember.ContainingType, hasMyBaseConstructorCall) OrElse hasMyBaseConstructorCall Then
' Move the explicit constructor call out of the block
statements.Add(body.Statements(0))
body = body.Update(body.StatementListSyntax, body.Locals, body.Statements.RemoveAt(0))
End If
End If
' The implicit exitLabelStatement should be the last statement inside BoundUnstructuredExceptionHandlingStatement
' in order to make sure that explicit returns do not bypass a call to Microsoft.VisualBasic.CompilerServices.ProjectData.ClearProjectError.
body = body.Update(body.StatementListSyntax, body.Locals, body.Statements.Add(exitLabelStatement))
statements.Add(New BoundUnstructuredExceptionHandlingStatement(methodBlock,
containsOnError,
containsResume,
resumeWithoutLabel,
containsLineNumberLabel,
body.MakeCompilerGenerated()).MakeCompilerGenerated())
Else
locals = body.Locals
statements.AddRange(body.Statements)
statements.Add(exitLabelStatement)
End If
' Don't allow any further declaration of implicit variables (by speculative binding, say).
DisallowFurtherImplicitVariableDeclaration(diagnostics)
' Add implicitly declared variables, if any.
Dim implicitLocals = Me.ImplicitlyDeclaredVariables
If implicitLocals.Length > 0 Then
If locals.IsEmpty Then
locals = implicitLocals
Else
locals = implicitLocals.Concat(locals)
End If
End If
' Report conflicts between Static variables.
ReportNameConflictsBetweenStaticLocals(blockBinder, diagnostics)
Else
statements.Add(exitLabelStatement)
End If
' Add a Return statement at the end of the function, with a label to branch to for Exit Sub/Exit Function statements.
' The code rewriter turns all returns inside the method body to a jump to the exit label. These returns are the only
' ones that will become real returns in the method body.
' add indirect return sequence
' and maybe an indirect result local (if this is a function)
If localForFunctionValue IsNot Nothing Then
If locals.IsEmpty Then
locals = ImmutableArray.Create(localForFunctionValue)
Else
Dim localBuilder = ArrayBuilder(Of LocalSymbol).GetInstance()
localBuilder.Add(localForFunctionValue)
localBuilder.AddRange(locals)
locals = localBuilder.ToImmutableAndFree()
End If
statements.Add(New BoundReturnStatement(methodBlock.EndBlockStatement,
New BoundLocal(methodBlock.EndBlockStatement, localForFunctionValue, isLValue:=False, type:=localForFunctionValue.Type).MakeCompilerGenerated(),
Nothing, Nothing))
Else
statements.Add(New BoundReturnStatement(methodBlock.EndBlockStatement, Nothing, Nothing, Nothing))
End If
' Inject implicit base constructor call, if appropriate
If Not methodSymbol.IsImplicitlyDeclared AndAlso methodSymbol.MethodKind = MethodKind.Constructor Then
Dim referencedConstructor As MethodSymbol = Nothing
Dim injectDefaultConstructorCall As Boolean = False
MethodCompiler.GetExplicitlyOrImplicitlyReferencedConstructor(methodSymbol, statements(0), Me,
diagnostics, referencedConstructor, injectDefaultConstructorCall)
' If we didn't find explicitly referenced constructor, use implicitly generated call
If injectDefaultConstructorCall Then
If referencedConstructor IsNot Nothing Then
Dim initializer As BoundExpressionStatement = MethodCompiler.BindDefaultConstructorInitializer(methodSymbol, referencedConstructor, diagnostics, Me)
Debug.Assert(initializer.Expression.Kind = BoundKind.Call OrElse
(initializer.HasErrors AndAlso
(Not diagnostics.AccumulatesDiagnostics OrElse diagnostics.HasAnyResolvedErrors)))
statements.Insert(0, initializer)
Else
Debug.Assert(Not diagnostics.AccumulatesDiagnostics OrElse diagnostics.HasAnyResolvedErrors OrElse If(methodSymbol.ContainingType.BaseTypeNoUseSiteDiagnostics?.IsErrorType(), False))
' Insert statement with an error to prevent more attempts to inject the initializer which will cause duplicate diagnostics
statements.Insert(0,
New BoundExpressionStatement(methodSymbol.Syntax,
New BoundBadExpression(methodSymbol.Syntax, LookupResultKind.OverloadResolutionFailure,
ImmutableArray(Of Symbol).Empty,
ImmutableArray(Of BoundExpression).Empty,
ErrorTypeSymbol.UnknownResultType, hasErrors:=True).
MakeCompilerGenerated()).
MakeCompilerGenerated())
End If
End If
End If
Return New BoundBlock(methodBlock, If(methodBlock IsNot Nothing, methodBlock.Statements, Nothing), locals, statements.ToImmutableAndFree())
End Function
''' <summary>
''' Check presence of [On Error]/[Resume] statements and report diagnostics based on presence of other
''' "incompatible" statements.
''' Report Async/Await diagnostics, which depends on surrounding context.
''' </summary>
Private Class CheckOnErrorAndAwaitWalker
Inherits BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
Private ReadOnly _binder As Binder
Private ReadOnly _diagnostics As BindingDiagnosticBag
Private _containsOnError As Boolean ' The block contains an [On Error] statement.
Private _containsTry As Boolean ' The block contains a Try block.
Private _containsResume As Boolean ' The block contains a [Resume [...]] or an [On Error Resume Next] statement. And this is a syntax node for the first of them.
Private _resumeWithoutLabel As StatementSyntax ' The first [Resume], [Resume Next] or [On Error Resume Next] statement, if any.
Private _containsLineNumberLabel As Boolean
Private _containsCatch As Boolean
Private _reportedAnError As Boolean
Private _enclosingSyncLockOrUsing As BoundStatement
Private _isInCatchFinallyOrSyncLock As Boolean
Private _containsAwait As Boolean
Private ReadOnly _tryOnErrorResume As New ArrayBuilder(Of BoundStatement)
Private Sub New(binder As Binder, diagnostics As BindingDiagnosticBag)
_diagnostics = diagnostics
_binder = binder
End Sub
Public Shared Shadows Sub VisitBlock(
binder As Binder,
block As BoundBlock,
diagnostics As BindingDiagnosticBag,
<Out> ByRef containsAwait As Boolean,
<Out> ByRef containsOnError As Boolean,
<Out> ByRef containsResume As Boolean,
<Out> ByRef resumeWithoutLabel As StatementSyntax,
<Out> ByRef containsLineNumberLabel As Boolean,
<Out> ByRef containsCatch As Boolean,
<Out> ByRef reportedAnError As Boolean
)
Dim walker As New CheckOnErrorAndAwaitWalker(binder, diagnostics)
Try
walker.Visit(block)
Debug.Assert(walker._enclosingSyncLockOrUsing Is Nothing)
Debug.Assert(Not walker._isInCatchFinallyOrSyncLock)
Catch ex As CancelledByStackGuardException
ex.AddAnError(diagnostics)
reportedAnError = True
End Try
containsAwait = walker._containsAwait
containsOnError = walker._containsOnError
containsResume = walker._containsResume
reportedAnError = walker._reportedAnError
resumeWithoutLabel = walker._resumeWithoutLabel
containsLineNumberLabel = walker._containsLineNumberLabel
containsCatch = walker._containsCatch
If (containsOnError OrElse containsResume) AndAlso walker._containsTry Then
For Each node In walker._tryOnErrorResume
Binder.ReportDiagnostic(diagnostics, node.Syntax, ERRID.ERR_TryAndOnErrorDoNotMix)
Next
reportedAnError = True
End If
End Sub
Public Overrides Function Visit(node As BoundNode) As BoundNode
' Do not dive into expressions, unless we are in Async context
If Not _binder.IsInAsyncContext() AndAlso TypeOf node Is BoundExpression Then
Return Nothing
End If
Return MyBase.Visit(node)
End Function
Public Overrides Function VisitTryStatement(node As BoundTryStatement) As BoundNode
Debug.Assert(Not node.WasCompilerGenerated)
_containsTry = True
_tryOnErrorResume.Add(node)
Visit(node.TryBlock)
Dim save_m_isInCatchFinallyOrSyncLock As Boolean = _isInCatchFinallyOrSyncLock
_isInCatchFinallyOrSyncLock = True
VisitList(node.CatchBlocks)
Visit(node.FinallyBlockOpt)
_isInCatchFinallyOrSyncLock = save_m_isInCatchFinallyOrSyncLock
Return Nothing
End Function
Public Overrides Function VisitOnErrorStatement(node As BoundOnErrorStatement) As BoundNode
Debug.Assert(Not node.WasCompilerGenerated)
_containsOnError = True
_tryOnErrorResume.Add(node)
If node.OnErrorKind = OnErrorStatementKind.ResumeNext Then
_containsResume = True
If _resumeWithoutLabel Is Nothing Then
_resumeWithoutLabel = DirectCast(node.Syntax, StatementSyntax)
End If
End If
If _enclosingSyncLockOrUsing IsNot Nothing Then
ReportDiagnostic(_diagnostics, node.Syntax,
If(_enclosingSyncLockOrUsing.Kind = BoundKind.UsingStatement,
ERRID.ERR_OnErrorInUsing,
ERRID.ERR_OnErrorInSyncLock))
_reportedAnError = True
End If
Return Nothing
End Function
Public Overrides Function VisitResumeStatement(node As BoundResumeStatement) As BoundNode
Debug.Assert(Not node.WasCompilerGenerated)
_containsResume = True
If node.ResumeKind <> ResumeStatementKind.Label AndAlso _resumeWithoutLabel Is Nothing Then
_resumeWithoutLabel = DirectCast(node.Syntax, StatementSyntax)
End If
_tryOnErrorResume.Add(node)
Return Nothing
End Function
Public Overrides Function VisitSyncLockStatement(node As BoundSyncLockStatement) As BoundNode
Debug.Assert(Not node.WasCompilerGenerated)
Dim save = _enclosingSyncLockOrUsing
Dim save_m_isInCatchFinallyOrSyncLock As Boolean = _isInCatchFinallyOrSyncLock
_enclosingSyncLockOrUsing = node
_isInCatchFinallyOrSyncLock = True
MyBase.VisitSyncLockStatement(node)
_enclosingSyncLockOrUsing = save
_isInCatchFinallyOrSyncLock = save_m_isInCatchFinallyOrSyncLock
Return Nothing
End Function
Public Overrides Function VisitUsingStatement(node As BoundUsingStatement) As BoundNode
Debug.Assert(Not node.WasCompilerGenerated)
Dim save = _enclosingSyncLockOrUsing
_enclosingSyncLockOrUsing = node
MyBase.VisitUsingStatement(node)
_enclosingSyncLockOrUsing = save
Return Nothing
End Function
Public Overrides Function VisitAwaitOperator(node As BoundAwaitOperator) As BoundNode
Debug.Assert(_binder.IsInAsyncContext())
_containsAwait = True
If _isInCatchFinallyOrSyncLock Then
ReportDiagnostic(_diagnostics, node.Syntax, ERRID.ERR_BadAwaitInTryHandler)
_reportedAnError = True
End If
Return MyBase.VisitAwaitOperator(node)
End Function
Public Overrides Function VisitLambda(node As BoundLambda) As BoundNode
Debug.Assert(_binder.IsInAsyncContext())
' Do not dive into the lambdas.
Return Nothing
End Function
Public Overrides Function VisitCatchBlock(node As BoundCatchBlock) As BoundNode
_containsCatch = True
Return MyBase.VisitCatchBlock(node)
End Function
Public Overrides Function VisitLabelStatement(node As BoundLabelStatement) As BoundNode
If Not node.WasCompilerGenerated AndAlso node.Syntax.Kind = SyntaxKind.LabelStatement AndAlso
DirectCast(node.Syntax, LabelStatementSyntax).LabelToken.Kind = SyntaxKind.IntegerLiteralToken Then
_containsLineNumberLabel = True
End If
Return MyBase.VisitLabelStatement(node)
End Function
End Class
Private Shared Sub ReportNameConflictsBetweenStaticLocals(methodBlockBinder As Binder, diagnostics As BindingDiagnosticBag)
Dim currentBinder As Binder = methodBlockBinder
Dim bodyBinder As MethodBodyBinder
Do
bodyBinder = TryCast(currentBinder, MethodBodyBinder)
If bodyBinder IsNot Nothing Then
Exit Do
End If
currentBinder = currentBinder.ContainingBinder
Loop While currentBinder IsNot Nothing
Debug.Assert(bodyBinder IsNot Nothing)
If bodyBinder IsNot Nothing Then
Dim staticLocals As Dictionary(Of String, ArrayBuilder(Of LocalSymbol)) = Nothing
For Each binder As BlockBaseBinder In bodyBinder.StmtListToBinderMap.Values
For Each local In binder.Locals
If local.IsStatic Then
Dim array As ArrayBuilder(Of LocalSymbol) = Nothing
If staticLocals Is Nothing Then
staticLocals = New Dictionary(Of String, ArrayBuilder(Of LocalSymbol))(CaseInsensitiveComparison.Comparer)
array = New ArrayBuilder(Of LocalSymbol)()
staticLocals.Add(local.Name, array)
ElseIf Not staticLocals.TryGetValue(local.Name, array) Then
array = New ArrayBuilder(Of LocalSymbol)()
staticLocals.Add(local.Name, array)
End If
array.Add(local)
End If
Next
Next
If staticLocals IsNot Nothing Then
For Each nameToArray In staticLocals
Dim array = nameToArray.Value
If array.Count > 1 Then
Dim lexicallyFirst As LocalSymbol = array(0)
For i As Integer = 1 To array.Count - 1
If lexicallyFirst.IdentifierToken.Position > array(i).IdentifierToken.Position Then
lexicallyFirst = array(i)
End If
Next
For Each local In array
If lexicallyFirst IsNot local Then
ReportDiagnostic(diagnostics, local.IdentifierToken, ERRID.ERR_DuplicateLocalStatic1, local.Name)
End If
Next
End If
Next
End If
End If
End Sub
''' <summary> Defines max allowed rank of the array </summary>
''' <remarks> Currently set to 32 because of COM+ array type limits </remarks>
Public Const ArrayRankLimit = 32
Private Function BindRedimStatement(node As ReDimStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim operands = ArrayBuilder(Of BoundRedimClause).GetInstance()
Dim hasPreserveClause = node.Kind = SyntaxKind.ReDimPreserveStatement
For Each redimOperand As RedimClauseSyntax In node.Clauses
Dim redimClauseHasErrors As Boolean = False
' bind operand expression as an assignment target
Dim redimTarget = BindAssignmentTarget(redimOperand.Expression, diagnostics)
If redimTarget.HasErrors Then
redimClauseHasErrors = True
End If
' check for validity of an assignment target, report diagnostics, discard the result
AdjustAssignmentTarget(redimOperand.Expression, redimTarget, diagnostics, redimClauseHasErrors)
' in case it is a Redim Preserve we will make an r-value of it in rewrite phase;
' in initial binding we report diagnostics, but throw away the result
If Not redimClauseHasErrors AndAlso hasPreserveClause Then
Dim temp = MakeRValue(redimTarget, diagnostics)
If temp.HasErrors Then
redimClauseHasErrors = True
End If
End If
' bind arguments/indices
Dim boundIndices As ImmutableArray(Of BoundExpression) = ImmutableArray(Of BoundExpression).Empty
If redimOperand.ArrayBounds IsNot Nothing Then
boundIndices = BindArrayBounds(redimOperand.ArrayBounds, diagnostics, errorOnEmptyBound:=True)
For Each arg In boundIndices
If arg.HasErrors Then
redimClauseHasErrors = True
End If
Next
End If
' check for the resulting type of the redim target expression: it should be either an array or an object
Dim arrayType As ArrayTypeSymbol = Nothing
If Not redimClauseHasErrors Then
Dim redimTargetType = redimTarget.Type
Debug.Assert(redimTargetType IsNot Nothing)
If redimTargetType.IsArrayType Then
arrayType = DirectCast(redimTargetType, ArrayTypeSymbol)
ElseIf redimTargetType.IsObjectType() Then
If boundIndices.Length > 0 Then ' missing redim size error will be reported later
arrayType = ArrayTypeSymbol.CreateVBArray(redimTargetType, Nothing, boundIndices.Length, Compilation)
End If
Else
ReportDiagnostic(diagnostics, redimOperand.Expression, ERRID.ERR_ExpectedArray1, "Redim")
redimClauseHasErrors = True
End If
End If
' check redim array rank
If Not redimClauseHasErrors Then
If boundIndices.Length = 0 Then
' redim rank cannot be 0
ReportDiagnostic(diagnostics, redimOperand, ERRID.ERR_RedimNoSizes)
redimClauseHasErrors = True
Else
' otherwise the number of indices should match the array rank
If arrayType.Rank <> boundIndices.Length Then
ReportDiagnostic(diagnostics, redimOperand, ERRID.ERR_RedimRankMismatch)
redimClauseHasErrors = True
End If
End If
End If
Debug.Assert(redimClauseHasErrors OrElse arrayType IsNot Nothing)
' check for an array rank limit value
If Not redimClauseHasErrors AndAlso boundIndices.Length > ArrayRankLimit Then
ReportDiagnostic(diagnostics, redimOperand, ERRID.ERR_ArrayRankLimit)
redimClauseHasErrors = True
End If
operands.Add(
New BoundRedimClause(
redimOperand,
redimTarget,
boundIndices,
arrayType,
hasPreserveClause,
redimClauseHasErrors)
)
Next
' done with all clauses, build a bound redim statement
Return New BoundRedimStatement(node, operands.ToImmutableAndFree())
End Function
Private Function BindEraseStatement(node As EraseStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim clauses = ArrayBuilder(Of BoundAssignmentOperator).GetInstance()
For Each operand As ExpressionSyntax In node.Expressions
Dim target As BoundExpression = BindAssignmentTarget(operand, diagnostics)
Debug.Assert(target IsNot Nothing)
Dim nothingLiteral = New BoundLiteral(operand, ConstantValue.Nothing, Nothing).MakeCompilerGenerated()
Dim clause As BoundAssignmentOperator
If target.HasErrors Then
clause = New BoundAssignmentOperator(operand, target, nothingLiteral, False, target.Type, hasErrors:=True).MakeCompilerGenerated()
ElseIf Not target.Type.IsErrorType() AndAlso
Not target.Type.IsArrayType() AndAlso
target.Type.SpecialType <> SpecialType.System_Array AndAlso
target.Type.SpecialType <> SpecialType.System_Object Then
ReportDiagnostic(diagnostics, operand, ERRID.ERR_ExpectedArray1, "Erase")
clause = New BoundAssignmentOperator(operand, target, nothingLiteral, False, target.Type, hasErrors:=True).MakeCompilerGenerated()
Else
clause = BindAssignment(operand, target,
ApplyImplicitConversion(operand, target.Type, nothingLiteral, diagnostics).MakeCompilerGenerated(),
diagnostics).MakeCompilerGenerated()
End If
clauses.Add(clause)
Next
Return New BoundEraseStatement(node, clauses.ToImmutableAndFree())
End Function
Private Function BindGoToStatement(node As GoToStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim symbol As LabelSymbol = Nothing
Dim boundLabelExpression As BoundExpression = BindExpression(node.Label, diagnostics)
If boundLabelExpression.Kind = BoundKind.Label Then
Dim boundLabel = DirectCast(boundLabelExpression, boundLabel)
symbol = boundLabel.Label
Dim hasErrors As Boolean = boundLabel.HasErrors
' Found label now verify that it is OK to jump to the location.
hasErrors = hasErrors OrElse
Not IsValidLabelForGoto(symbol, node.Label, diagnostics)
Return New BoundGotoStatement(node, symbol, boundLabel, hasErrors:=hasErrors)
Else
' if the bound label is e.g. a bad bound expression because of a non-existent label,
' make this a bad statement.
Return New BoundBadStatement(node, ImmutableArray.Create(Of BoundNode)(boundLabelExpression), hasErrors:=True)
End If
End Function
Private Function IsValidLabelForGoto(label As LabelSymbol, labelSyntax As LabelSyntax, diagnostics As BindingDiagnosticBag) As Boolean
Dim hasError As Boolean = False
Dim labelParent = DirectCast(label.LabelName.Parent, VisualBasicSyntaxNode)
' Determine if the reference is a branch that crosses
' into a Try/Catch/Finally or With statement.
' once a method or lambda block is found we can stop searching
Dim errorID = ERRID.ERR_None
While labelParent IsNot Nothing
Select Case labelParent.Kind
Case SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression
Exit While
Case SyntaxKind.TryBlock,
SyntaxKind.CatchBlock,
SyntaxKind.FinallyBlock
errorID = ERRID.ERR_GotoIntoTryHandler
Case SyntaxKind.UsingBlock
errorID = ERRID.ERR_GotoIntoUsing
Case SyntaxKind.SyncLockBlock
errorID = ERRID.ERR_GotoIntoSyncLock
Case SyntaxKind.WithBlock
errorID = ERRID.ERR_GotoIntoWith
Case SyntaxKind.ForBlock,
SyntaxKind.ForEachBlock
errorID = ERRID.ERR_GotoIntoFor
End Select
If errorID <> ERRID.ERR_None Then
If Not IsValidBranchTarget(labelParent, labelSyntax) Then
ReportDiagnostic(diagnostics, labelSyntax, ErrorFactory.ErrorInfo(errorID, label.Name))
hasError = True
End If
Exit While
End If
labelParent = labelParent.Parent
End While
Return Not hasError
End Function
Private Shared Function IsValidBranchTarget(block As VisualBasicSyntaxNode, labelSyntax As LabelSyntax) As Boolean
Debug.Assert(block.Kind = SyntaxKind.TryBlock OrElse
block.Kind = SyntaxKind.CatchBlock OrElse
block.Kind = SyntaxKind.FinallyBlock OrElse
block.Kind = SyntaxKind.UsingBlock OrElse
block.Kind = SyntaxKind.SyncLockBlock OrElse
block.Kind = SyntaxKind.WithBlock OrElse
block.Kind = SyntaxKind.ForBlock OrElse
block.Kind = SyntaxKind.ForEachBlock)
Dim parent = labelSyntax.Parent
While parent IsNot Nothing
If parent Is block Then
Return True
End If
parent = parent.Parent
End While
Return False
End Function
Private Function BindLabelStatement(node As LabelStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim labelToken As SyntaxToken = node.LabelToken
Dim labelName = labelToken.ValueText
' A label symbol will always be found because all labels without syntax errors are put into the
' label map in the blockbasebinder.
Dim result = LookupResult.GetInstance()
Lookup(result, labelName, 0, LookupOptions.LabelsOnly, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
Debug.Assert(result.HasSingleSymbol AndAlso result.IsGood)
Dim symbol = DirectCast(result.SingleSymbol, SourceLabelSymbol)
' Check for duplicate goto label
Dim hasError = False
If symbol.LabelName <> labelToken Then
' If symbol's token does not match the node's token then this is not the label target but a duplicate definition
ReportDiagnostic(diagnostics, labelToken, ERRID.ERR_MultiplyDefined1, labelName)
hasError = True
End If
result.Free()
Return New BoundLabelStatement(node, symbol, hasErrors:=hasError)
End Function
''' <summary>
''' Decodes a set of local declaration modifier flags and reports any errors with the flags.
''' </summary>
''' <param name="syntax">The syntax list of the modifiers.</param>
''' <param name="diagBag">returns True if any errors are reported</param>
Private Sub DecodeLocalModifiersAndReportErrors(syntax As SyntaxTokenList, diagBag As BindingDiagnosticBag)
Const localModifiersMask = SourceMemberFlags.Const Or SourceMemberFlags.Dim Or SourceMemberFlags.Static
Dim foundModifiers As SourceMemberFlags = Nothing
' Go through each modifiers, accumulating flags of what we've seen and reporting errors.
Dim firstDim As SyntaxToken = Nothing
Dim firstStatic As SyntaxToken = Nothing
For Each keywordSyntax In syntax
Dim currentModifier As SourceMemberFlags = MapKeywordToFlag(keywordSyntax)
If currentModifier = SourceMemberFlags.None Then
Continue For
End If
' Report errors with the modifier
If (currentModifier And localModifiersMask) = 0 Then
ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_BadLocalDimFlags1, keywordSyntax.ToString())
ElseIf (currentModifier And foundModifiers) <> 0 Then
ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_DuplicateSpecifier)
Else
Select Case currentModifier
Case SourceMemberFlags.Dim
firstDim = keywordSyntax
Case SourceMemberFlags.Static
firstStatic = keywordSyntax
End Select
foundModifiers = foundModifiers Or currentModifier
End If
Next
If (foundModifiers And SourceMemberFlags.Const) <> 0 Then
' Const incompatible with Dim or Static
If (foundModifiers And SourceMemberFlags.Dim) <> 0 Then
ReportDiagnostic(diagBag, firstDim, ERRID.ERR_BadLocalConstFlags1, firstDim.ToString())
ElseIf (foundModifiers And SourceMemberFlags.Static) <> 0 Then
ReportDiagnostic(diagBag, firstStatic, ERRID.ERR_BadLocalConstFlags1, firstStatic.ToString())
End If
ElseIf (foundModifiers And SourceMemberFlags.Static) <> 0 Then
' 'Static' keyword is only allowed in class methods, but not in structure methods
If Me.ContainingType IsNot Nothing AndAlso Me.ContainingType.TypeKind = TYPEKIND.Structure Then
' Local variables within methods of structures cannot be declared 'Static'
ReportDiagnostic(diagBag, firstStatic, ERRID.ERR_BadStaticLocalInStruct)
ElseIf Me.IsInLambda Then
ReportDiagnostic(diagBag, firstStatic, ERRID.ERR_StaticInLambda)
ElseIf Me.ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(Me.ContainingMember, MethodSymbol).IsGenericMethod Then
ReportDiagnostic(diagBag, firstStatic, ERRID.ERR_BadStaticLocalInGenericMethod)
End If
End If
End Sub
Private Function BindLocalDeclaration(node As LocalDeclarationStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
DecodeLocalModifiersAndReportErrors(node.Modifiers, diagnostics)
Dim boundLocalDeclarations As ImmutableArray(Of BoundLocalDeclarationBase) = BindVariableDeclarators(node.Declarators, diagnostics)
' NOTE: Always create bound Dim statement to make
' sure syntax node has a bound node associated with it
Return New BoundDimStatement(node, boundLocalDeclarations, Nothing)
End Function
Private Function BindVariableDeclarators(
declarators As SeparatedSyntaxList(Of VariableDeclaratorSyntax),
diagnostics As BindingDiagnosticBag
) As ImmutableArray(Of BoundLocalDeclarationBase)
Dim builder = ArrayBuilder(Of BoundLocalDeclarationBase).GetInstance()
For Each varDecl In declarators
Dim asClauseOpt = varDecl.AsClause
Dim initializerOpt As EqualsValueSyntax = varDecl.Initializer
If initializerOpt IsNot Nothing Then
If varDecl.Names.Count > 1 Then
' Can't combine an initializer with multiple variables
ReportDiagnostic(diagnostics, varDecl, ERRID.ERR_InitWithMultipleDeclarators)
End If
End If
Dim names = varDecl.Names
Dim asNewVariablePlaceholder As BoundWithLValueExpressionPlaceholder = Nothing
If names.Count = 1 Then
' Dim x as integer = 1 OR Dim x as New Integer
builder.Add(BindVariableDeclaration(varDecl, names(0), asClauseOpt, initializerOpt, diagnostics))
ElseIf asClauseOpt Is Nothing OrElse asClauseOpt.Kind <> SyntaxKind.AsNewClause Then
' Dim x,y,z as integer
For i = 0 To names.Count - 1
Dim var = BindVariableDeclaration(varDecl, names(i), asClauseOpt, If(i = names.Count - 1, initializerOpt, Nothing), diagnostics)
builder.Add(var)
Next
Else
' Dim x,y,z as New C
Dim nameCount = names.Count
Dim locals = ArrayBuilder(Of BoundLocalDeclaration).GetInstance(nameCount)
For i = 0 To nameCount - 1
' Pass the asClause to each local declaration so local knows its type and for error reporting.
Dim var = BindVariableDeclaration(varDecl, names(i), asClauseOpt, Nothing, diagnostics, i > 0)
locals.Add(var)
Next
' At this point all of the local declarations have an initializer. Remove the initializers from the individual local declarations
' and put the initializer on the BoundAsNewDeclaration. The local declarations are marked as initialized by the as-new.
Dim var0 As BoundLocalDeclaration = locals(0)
Dim asNewInitializer = var0.InitializerOpt
locals(0) = var0.Update(var0.LocalSymbol, Nothing, var0.IdentifierInitializerOpt, True)
#If DEBUG Then
For i = 0 To names.Count - 1
Debug.Assert(locals(i).InitializedByAsNew)
' The assert below is disabled due to https://github.com/dotnet/roslyn/issues/27533, need to follow up
'Debug.Assert(locals(i).InitializerOpt Is Nothing OrElse locals(i).InitializerOpt.Kind = BoundKind.BadExpression OrElse locals(i).InitializerOpt.Kind = BoundKind.ArrayCreation)
Next
#End If
builder.Add(New BoundAsNewLocalDeclarations(varDecl, locals.ToImmutableAndFree(), asNewInitializer, Me))
End If
Next
Return builder.ToImmutableAndFree()
End Function
Private Function GetLocalForDeclaration(identifier As SyntaxToken) As LocalSymbol
' We cannot rely on lookup to find the local because there could be locals with duplicate names.
Dim current As Binder = Me
Dim blockBinder As BlockBaseBinder
Do
blockBinder = TryCast(current, BlockBaseBinder)
If blockBinder IsNot Nothing Then
Exit Do
End If
current = current.ContainingBinder
Loop
For Each local In blockBinder.Locals
If local.IdentifierToken = identifier Then
Return local
End If
Next
Throw ExceptionUtilities.Unreachable
End Function
Friend Overridable Function BindVariableDeclaration(
tree As VisualBasicSyntaxNode,
name As ModifiedIdentifierSyntax,
asClauseOpt As AsClauseSyntax,
equalsValueOpt As EqualsValueSyntax,
diagnostics As BindingDiagnosticBag,
Optional skipAsNewInitializer As Boolean = False
) As BoundLocalDeclaration
Dim symbol As LocalSymbol = GetLocalForDeclaration(name.Identifier)
Dim declarationInitializer As BoundExpression = Nothing
Dim declType As TypeSymbol = Nothing
Dim boundArrayBounds As ImmutableArray(Of BoundExpression) = Nothing
If name.ArrayBounds IsNot Nothing Then
' So as not to trigger order of simple name binding checks, must bind array bounds before initializer.
boundArrayBounds = BindArrayBounds(name.ArrayBounds, diagnostics)
End If
' We don't bind the value here and pass it into ComputeVariableType because there is a chicken and egg problem.
' If the symbol has a type either from the type character or the as clause then we must first set the symbol
' to that type before binding the expression. For example,
'
' Dim i% = i
' is valid. If "i" were bound before the "i%" was resolved then i would not have a type and the expression
' would have an error. ComputeVariableType will only bind the value if the symbol does not have an explicit
' type.
Dim type As TypeSymbol = ComputeVariableType(symbol,
name,
asClauseOpt,
equalsValueOpt,
declarationInitializer,
declType,
diagnostics)
' Now that we know the type go ahead and set it.
VerifyLocalSymbolNameAndSetType(symbol, type, name, name.Identifier, diagnostics)
Debug.Assert(type IsNot Nothing)
Dim isInitializedByAsNew As Boolean = asClauseOpt IsNot Nothing AndAlso asClauseOpt.Kind = SyntaxKind.AsNewClause
Dim errSyntax = If(asClauseOpt Is Nothing, DirectCast(equalsValueOpt, VisualBasicSyntaxNode), asClauseOpt.Type)
Dim restrictedType As TypeSymbol = Nothing
If type.IsRestrictedArrayType(restrictedType) Then
If Not isInitializedByAsNew OrElse Not skipAsNewInitializer Then
ReportDiagnostic(diagnostics, errSyntax, ERRID.ERR_RestrictedType1, restrictedType)
End If
ElseIf symbol.IsStatic Then
If type.IsRestrictedType() Then
If Not isInitializedByAsNew OrElse Not skipAsNewInitializer Then
ReportDiagnostic(diagnostics, errSyntax, ERRID.ERR_RestrictedType1, type)
End If
ElseIf IsInAsyncContext() OrElse IsInIteratorContext() Then
ReportDiagnostic(diagnostics, name, ERRID.ERR_BadStaticInitializerInResumable)
End If
ElseIf IsInAsyncContext() OrElse IsInIteratorContext() Then
If type.IsRestrictedType() Then
If Not isInitializedByAsNew OrElse Not skipAsNewInitializer Then
ReportDiagnostic(diagnostics, errSyntax, ERRID.ERR_CannotLiftRestrictedTypeResumable1, type)
End If
End If
End If
If declarationInitializer Is Nothing Then
' We computed the type without needing to do type inference so bind the expression now.
' Because this symbol has a type, there is no danger of infinite recursion so we don't need
' a special binder.
If symbol.IsConst Then
declarationInitializer = symbol.GetConstantExpression(Me)
ElseIf equalsValueOpt IsNot Nothing Then
Dim valueSyntax = equalsValueOpt.Value
declarationInitializer = BindValue(valueSyntax, diagnostics)
End If
End If
If declarationInitializer IsNot Nothing AndAlso Not symbol.IsConst Then
' Only apply the conversion for non constants. Conversions for constants are handled in GetConstantExpression.
declarationInitializer = ApplyImplicitConversion(declarationInitializer.Syntax, type, declarationInitializer, diagnostics)
End If
If isInitializedByAsNew Then
Dim asNew = DirectCast(asClauseOpt, AsNewClauseSyntax)
If symbol.IsConst Then
ReportDiagnostic(diagnostics, asNew.NewExpression.NewKeyword, ERRID.ERR_BadLocalConstFlags1, asNew.NewExpression.NewKeyword.ToString())
Else
' If there is an AsNew clause then create the object as well.
Select Case asNew.NewExpression.Kind
Case SyntaxKind.ObjectCreationExpression
Debug.Assert(declarationInitializer Is Nothing)
If Not skipAsNewInitializer Then
DisallowNewOnTupleType(asNew.Type, diagnostics)
Dim objectCreationExpressionSyntax = DirectCast(asNew.NewExpression, ObjectCreationExpressionSyntax)
Dim asNewVariablePlaceholder As New BoundWithLValueExpressionPlaceholder(asClauseOpt, symbol.Type)
asNewVariablePlaceholder.SetWasCompilerGenerated()
declarationInitializer = BindObjectCreationExpression(asNew.Type,
objectCreationExpressionSyntax.ArgumentList,
declType,
objectCreationExpressionSyntax,
diagnostics,
asNewVariablePlaceholder)
Debug.Assert(declarationInitializer.Type.IsSameTypeIgnoringAll(declType))
End If
Case SyntaxKind.AnonymousObjectCreationExpression
' Is supposed to be already bound by ComputeVariableType
Debug.Assert(declarationInitializer IsNot Nothing)
Case Else
Throw ExceptionUtilities.UnexpectedValue(asNew.NewExpression.Kind)
End Select
If type.IsArrayType Then
' Arrays cannot be declared with AsNew syntax
ReportDiagnostic(diagnostics, asNew.NewExpression.NewKeyword, ERRID.ERR_AsNewArray)
declarationInitializer = BadExpression(asNew, declarationInitializer, type).MakeCompilerGenerated()
ElseIf declarationInitializer IsNot Nothing AndAlso Not declarationInitializer.HasErrors AndAlso
Not type.IsSameTypeIgnoringAll(declarationInitializer.Type) Then
' An error must have been reported elsewhere.
declarationInitializer = BadExpression(asNew, declarationInitializer, declarationInitializer.Type).MakeCompilerGenerated()
End If
End If
End If
Dim identifierInitializer As BoundArrayCreation = Nothing
If name.ArrayBounds IsNot Nothing Then
' It is an error to have both array bounds and an initializer expression
identifierInitializer = New BoundArrayCreation(name, boundArrayBounds, Nothing, type).MakeCompilerGenerated()
If declarationInitializer IsNot Nothing Then
If Not isInitializedByAsNew Then
ReportDiagnostic(diagnostics, name, ERRID.ERR_InitWithExplicitArraySizes)
Else
' Must have reported ERR_AsNewArray already.
Debug.Assert(declarationInitializer.Kind = BoundKind.BadExpression)
End If
End If
End If
If symbol.IsConst Then
If Not type.IsErrorType() Then
If Not type.IsValidTypeForConstField() Then
' "Constants must be of an intrinsic or enumerated type, not a class, structure, type parameter, or array type."
' arrays get the squiggles under the identifier name
' other data types get the squiggles under the type part of the as clause
errSyntax = If(asClauseOpt IsNot Nothing AndAlso Not type.IsArrayType, DirectCast(asClauseOpt.Type, VisualBasicSyntaxNode), name)
ReportDiagnostic(diagnostics, errSyntax, ERRID.ERR_ConstAsNonConstant)
Else
Dim bag = symbol.GetConstantValueDiagnostics(Me)
diagnostics.AddRange(bag, allowMismatchInDependencyAccumulation:=True)
End If
End If
End If
Return New BoundLocalDeclaration(name, symbol, declarationInitializer, identifierInitializer, isInitializedByAsNew)
End Function
''' <summary>
''' Compute the type of a local symbol using the type character, as clause and equals value expression.
''' 1. Try to compute the type based on the identifier/modified identifier and as clause. If there is a type then we're done.
''' 2. If OptionInfer is on then evaluate the expression and use that to infer the type.
'''
''' ComputeVariableType will only bind the value if the symbol does not have an explicit type.
''' </summary>
''' <param name="symbol">The local symbol</param>
''' <param name="modifiedIdentifierOpt">The symbols modified identifier is there is one</param>
''' <param name="asClauseOpt">The optional as clause</param>
''' <param name="equalsValueOpt">The optional initializing expression</param>
''' <param name="valueExpression">The bound initializing expression</param>
''' <param name="asClauseType">The bound as clause type</param>
Friend Function ComputeVariableType(symbol As LocalSymbol,
modifiedIdentifierOpt As ModifiedIdentifierSyntax,
asClauseOpt As AsClauseSyntax,
equalsValueOpt As EqualsValueSyntax,
<Out()> ByRef valueExpression As BoundExpression,
<Out()> ByRef asClauseType As TypeSymbol,
diagnostics As BindingDiagnosticBag) As TypeSymbol
valueExpression = Nothing
Dim typeDiagnostic As Func(Of DiagnosticInfo) = Nothing
If symbol.IsStatic Then
If OptionStrict = OptionStrict.On Then
typeDiagnostic = ErrorFactory.GetErrorInfo_ERR_StrictDisallowImplicitObject
ElseIf OptionStrict = OptionStrict.Custom Then
typeDiagnostic = ErrorFactory.GetErrorInfo_WRN_ObjectAssumedVar1_WRN_StaticLocalNoInference
End If
ElseIf Not (OptionInfer AndAlso equalsValueOpt IsNot Nothing) Then
If OptionStrict = OptionStrict.On Then
typeDiagnostic = ErrorFactory.GetErrorInfo_ERR_StrictDisallowImplicitObject
ElseIf OptionStrict = OptionStrict.Custom Then
typeDiagnostic = ErrorFactory.GetErrorInfo_WRN_ObjectAssumedVar1_WRN_MissingAsClauseinVarDecl
End If
End If
Dim type As TypeSymbol
Dim hasExplicitType As Boolean
If modifiedIdentifierOpt IsNot Nothing Then
If asClauseOpt IsNot Nothing AndAlso asClauseOpt.Kind = SyntaxKind.AsNewClause Then
Dim asNewClause = DirectCast(asClauseOpt, AsNewClauseSyntax)
Dim newExpression As NewExpressionSyntax = asNewClause.NewExpression
Debug.Assert(newExpression IsNot Nothing)
If newExpression.Kind = SyntaxKind.AnonymousObjectCreationExpression Then
' Bind anonymous type creation to define it's type
Dim binder = New LocalInProgressBinder(Me, symbol)
valueExpression = binder.BindAnonymousObjectCreationExpression(
DirectCast(newExpression, AnonymousObjectCreationExpressionSyntax), diagnostics)
asClauseType = valueExpression.Type
Return asClauseType
End If
End If
' Adjust type because the modified identifier can change the type to array or make it nullable.
' DecodeModifiedIdentifierType returns the explicit type or the default type based on object. i.e. object or object()
type = DecodeModifiedIdentifierType(modifiedIdentifierOpt, asClauseOpt, equalsValueOpt,
typeDiagnostic,
asClauseType,
diagnostics,
If(symbol.IsStatic,
ModifiedIdentifierTypeDecoderContext.StaticLocalType Or ModifiedIdentifierTypeDecoderContext.LocalType,
ModifiedIdentifierTypeDecoderContext.LocalType))
hasExplicitType = Not HasDefaultType(modifiedIdentifierOpt, asClauseOpt)
Else
Dim identifier = symbol.IdentifierToken
type = DecodeIdentifierType(identifier, asClauseOpt,
typeDiagnostic,
asClauseType,
diagnostics)
hasExplicitType = Not HasDefaultType(identifier, asClauseOpt)
End If
If hasExplicitType AndAlso Not (symbol.IsConst AndAlso type.SpecialType = SpecialType.System_Object) Then
' There is an explicit type or TypeCharacter. Return the type.
' Constants are special. Don't return here when a constant is typed as object.
Return type
End If
' The default type is Object. Infer a type if OptionInfer is on.
' Don't infer types for static locals.
' Always infer type of constant.
If OptionInfer AndAlso Not symbol.IsStatic AndAlso Not symbol.IsConst Then
If equalsValueOpt IsNot Nothing Then
Dim valueSyntax As ExpressionSyntax = equalsValueOpt.Value
' Use a LocalInProgressBinder to detect cycles using locals.
Dim binder = New LocalInProgressBinder(Me, symbol)
valueExpression = binder.BindValue(valueSyntax, diagnostics)
Dim inferFrom As BoundExpression = valueExpression
' Dig through parenthesized in case this expression is one of the special expressions
' that does not have a type such as lambda's and array literals.
If Not inferFrom.IsNothingLiteral Then
inferFrom = inferFrom.GetMostEnclosedParenthesizedExpression()
End If
Dim inferredType As TypeSymbol = Nothing
Dim arrayLiteral As BoundArrayLiteral = Nothing
Select Case inferFrom.Kind
Case BoundKind.UnboundLambda
inferredType = DirectCast(inferFrom, UnboundLambda).InferredAnonymousDelegate.Key
Case BoundKind.ArrayLiteral
arrayLiteral = DirectCast(inferFrom, BoundArrayLiteral)
inferredType = arrayLiteral.InferredType
Case BoundKind.TupleLiteral
Dim tupleLiteral = DirectCast(inferFrom, BoundTupleLiteral)
inferredType = tupleLiteral.InferredType
Case Else
inferredType = inferFrom.Type
End Select
If inferredType IsNot Nothing Then
' Infer the type from the expression. When the identifier has modifiers, the expression type
' and the modifiers need to be compatible. Without modifiers just use the expression type.
Dim localDiagnostics = If(inferFrom.HasErrors, BindingDiagnosticBag.Discarded, diagnostics)
If modifiedIdentifierOpt IsNot Nothing Then
type = InferVariableType(type, modifiedIdentifierOpt, valueSyntax, inferredType, inferFrom, typeDiagnostic, localDiagnostics)
If type IsNot inferredType AndAlso arrayLiteral IsNot Nothing Then
' Normally ReportArrayLiteralInferredElementTypeDiagnostics is handled in ReclassifyArrayLiteralExpression.
' ReportArrayLiteralInferredElementTypeDiagnostics depends on being able to compare the variable type with
' the inferred type. When the symbols are the same, it assumes the variable got its type from the expression.
' Because the modified identifier created a new array type symbol, we report errors now.
ReportArrayLiteralInferredTypeDiagnostics(arrayLiteral, localDiagnostics)
End If
Else
type = inferredType
End If
End If
End If
ElseIf symbol.IsConst Then
' If we arrive here it is because the constant does not have an explicit type or the type is object.
' In either case, the type will always be the type of the expression.
valueExpression = symbol.GetConstantExpression(Me)
Dim valueType = valueExpression.Type
If valueType IsNot Nothing AndAlso valueType.GetEnumUnderlyingTypeOrSelf.IsIntrinsicType Then
type = valueExpression.Type
End If
ElseIf Not symbol.IsStatic AndAlso OptionStrict <> OptionStrict.On AndAlso
Not hasExplicitType AndAlso type.IsObjectType() AndAlso
modifiedIdentifierOpt IsNot Nothing AndAlso
modifiedIdentifierOpt.Nullable.Node IsNot Nothing AndAlso
equalsValueOpt IsNot Nothing Then
Debug.Assert(Not symbol.IsConst AndAlso Not symbol.IsStatic AndAlso Not OptionInfer)
ReportDiagnostic(diagnostics, modifiedIdentifierOpt, ERRID.ERR_NullableTypeInferenceNotSupported)
End If
' Return the inferred type or the default type
Return type
End Function
''' <summary>
''' Infer the type of a for-from-to control variable.
''' </summary>
Friend Function InferForFromToVariableType(symbol As LocalSymbol,
fromValueSyntax As ExpressionSyntax,
toValueSyntax As ExpressionSyntax,
stepClauseSyntaxOpt As ForStepClauseSyntax,
<Out()> ByRef fromValueExpression As BoundExpression,
<Out()> ByRef toValueExpression As BoundExpression,
<Out()> ByRef stepValueExpression As BoundExpression,
diagnostics As BindingDiagnosticBag) As TypeSymbol
fromValueExpression = Nothing
toValueExpression = Nothing
stepValueExpression = Nothing
Dim identifier = symbol.IdentifierToken
Dim type = DecodeIdentifierType(identifier, Nothing,
Nothing,
Nothing,
diagnostics)
Dim hasExplicitType = Not HasDefaultType(identifier, Nothing)
If hasExplicitType Then
Return type
End If
' Use an ImplicitlyTypedLocalBinder to detect cycles using locals.
Dim binder = New LocalInProgressBinder(Me, symbol)
fromValueExpression = binder.BindRValue(fromValueSyntax, diagnostics)
toValueExpression = binder.BindRValue(toValueSyntax, diagnostics)
If stepClauseSyntaxOpt IsNot Nothing Then
stepValueExpression = binder.BindRValue(stepClauseSyntaxOpt.StepValue, diagnostics)
End If
If toValueExpression.HasErrors OrElse fromValueExpression.HasErrors OrElse
(stepValueExpression IsNot Nothing AndAlso stepValueExpression.HasErrors) Then
Return type
End If
Dim numCandidates As Integer = 0
Dim array = ArrayBuilder(Of BoundExpression).GetInstance(2)
array.Add(fromValueExpression)
array.Add(toValueExpression)
If stepValueExpression IsNot Nothing Then
array.Add(stepValueExpression)
End If
Dim identifierName = DirectCast(identifier.Parent, IdentifierNameSyntax)
Dim errorReasons As InferenceErrorReasons = InferenceErrorReasons.Other
Dim dominantType = InferDominantTypeOfExpressions(identifierName, array, diagnostics, numCandidates, errorReasons)
array.Free()
' check the resulting type
If numCandidates = 0 OrElse (numCandidates > 1 AndAlso (errorReasons And InferenceErrorReasons.Ambiguous) = 0) Then
ReportDiagnostic(diagnostics, identifierName, ERRID.ERR_NoSuitableWidestType1, identifierName.Identifier.ValueText)
Return type
ElseIf numCandidates > 1 Then
ReportDiagnostic(diagnostics, identifierName, ERRID.ERR_AmbiguousWidestType3, identifierName.Identifier.ValueText)
Return type
End If
If dominantType IsNot Nothing Then
Return dominantType
End If
Return type
End Function
''' <summary>
''' Infer the type of a for-each control variable.
''' </summary>
Friend Function InferForEachVariableType(symbol As LocalSymbol,
collectionSyntax As ExpressionSyntax,
<Out()> ByRef collectionExpression As BoundExpression,
<Out()> ByRef currentType As TypeSymbol,
<Out()> ByRef elementType As TypeSymbol,
<Out()> ByRef isEnumerable As Boolean,
<Out()> ByRef boundGetEnumeratorCall As BoundExpression,
<Out()> ByRef boundEnumeratorPlaceholder As BoundLValuePlaceholder,
<Out()> ByRef boundMoveNextCall As BoundExpression,
<Out()> ByRef boundCurrentAccess As BoundExpression,
<Out()> ByRef collectionPlaceholder As BoundRValuePlaceholder,
<Out()> ByRef needToDispose As Boolean,
<Out()> ByRef isOrInheritsFromOrImplementsIDisposable As Boolean,
diagnostics As BindingDiagnosticBag) As TypeSymbol
collectionExpression = Nothing
currentType = Nothing
elementType = Nothing
isEnumerable = False
boundGetEnumeratorCall = Nothing
boundEnumeratorPlaceholder = Nothing
boundMoveNextCall = Nothing
boundCurrentAccess = Nothing
collectionPlaceholder = Nothing
needToDispose = False
isOrInheritsFromOrImplementsIDisposable = False
Dim identifier = symbol.IdentifierToken
Dim type = DecodeIdentifierType(identifier, Nothing,
Nothing,
Nothing,
diagnostics)
Dim hasExplicitType = Not HasDefaultType(identifier, Nothing)
If hasExplicitType Then
Return type
End If
' Use an ImplicitlyTypedLocalBinder to detect cycles using locals.
Dim binder = New LocalInProgressBinder(Me, symbol)
' bind the expression that describes the collection to iterate over
collectionExpression = binder.BindValue(collectionSyntax, diagnostics)
' collection will be a target to a GetEnumerator call, so we need to adjust
' it to RValue if it is not an LValue already.
If Not collectionExpression.IsLValue AndAlso Not collectionExpression.IsNothingLiteral Then
collectionExpression = MakeRValue(collectionExpression, diagnostics)
End If
' check if the collection is valid for a for each
collectionExpression = InterpretForEachStatementCollection(collectionExpression,
currentType,
elementType,
isEnumerable,
boundGetEnumeratorCall,
boundEnumeratorPlaceholder,
boundMoveNextCall,
boundCurrentAccess,
collectionPlaceholder,
needToDispose,
isOrInheritsFromOrImplementsIDisposable,
diagnostics)
If elementType IsNot Nothing Then
Return elementType
End If
Return type
End Function
''' <summary>
''' Infer the type of a variable declared with an initializing expression.
''' </summary>
Private Function InferVariableType(defaultType As TypeSymbol,
name As ModifiedIdentifierSyntax,
valueSyntax As ExpressionSyntax,
valueType As TypeSymbol,
valueExpression As BoundExpression,
getRequireTypeDiagnosticInfoFunc As Func(Of DiagnosticInfo),
diagnostics As BindingDiagnosticBag) As TypeSymbol
Dim nameIsArrayType = IsArrayType(name)
Dim nameHasNullable = name.Nullable.Node IsNot Nothing
Dim identifier = name.Identifier
If nameHasNullable AndAlso
valueType.IsArrayType AndAlso
Not nameIsArrayType Then
' if name is nullable and expression is an array then the name must also be an array
ReportDiagnostic(diagnostics, identifier, ERRID.ERR_CannotInferNullableForVariable1, name.Identifier.ToString())
ElseIf nameIsArrayType AndAlso Not valueType.IsArrayType Then
' if name is an array then the expression must be an array
ReportDiagnostic(diagnostics, identifier, ERRID.ERR_InferringNonArrayType1, valueType)
ElseIf Not nameIsArrayType Then
If nameHasNullable Then
' name is nullable but not an array therefore the value must be a value type.
If Not valueType.IsValueType Then
ReportDiagnostic(diagnostics, identifier, ERRID.ERR_CannotInferNullableForVariable1, identifier.ToString())
ElseIf valueType.IsNullableType Then
Return valueType
Else
Dim modifiedValueType = DecodeModifiedIdentifierType(name,
valueType,
Nothing,
valueSyntax,
getRequireTypeDiagnosticInfoFunc,
diagnostics)
Return modifiedValueType
End If
Else
' name does not add array or nullable so return the expression type.
Return valueType
End If
Else
Debug.Assert(nameIsArrayType AndAlso valueType.IsArrayType, "both the name and the value should be arrays")
' Both the name and the expression are arrays
' Check that the name's array is compatible with the expression array type
If nameHasNullable Then
' Verify that the rhs element type is also a nullable type
Dim rhsElementType As TypeSymbol = DirectCast(valueType, ArrayTypeSymbol).ElementType
Do
If Not rhsElementType.IsArrayType Then
Exit Do
End If
rhsElementType = DirectCast(rhsElementType, ArrayTypeSymbol).ElementType
Loop
If Not rhsElementType.IsNullableType Then
ReportDiagnostic(diagnostics, identifier, ERRID.ERR_CannotInferNullableForVariable1, identifier.ToString())
Return defaultType
End If
End If
If DirectCast(defaultType, ArrayTypeSymbol).Rank <> DirectCast(valueType, ArrayTypeSymbol).Rank Then
Dim arrayLiteral = TryCast(valueExpression, BoundArrayLiteral)
' Arrays on both sides but ranks are not the same. This is an error unless rhs is an empty literal.
If (arrayLiteral Is Nothing OrElse Not arrayLiteral.IsEmptyArrayLiteral) Then
ReportDiagnostic(diagnostics, name, ERRID.ERR_TypeInferenceArrayRankMismatch1, name.Identifier.ToString())
End If
Else
' For arrays, if both the LHS and RHS specify an array shape, we
' have to allow inference to infer "arrays" when possible. That is, if
' we consider matching the shape from left to right, if we "consume" the arrays
' of the RHS and have "extra" arrays left over, we infer that.
'
' For example:
' dim x() = new Integer()() - we infer integer()()
' dim x(,)() = new Integer(,)()(,) - we infer integer(,)()(,)
'
' The algorithm used is to match the array rank until we exhaust the LHS,
' and if the RHS has more arrays, simply infer the RHS into the LHS. If the RHS
' "runs out of arrays" before the LHS, then we just infer the base type and give
' a conversion error.
Dim lhsType As TypeSymbol = defaultType
Dim rhsType As TypeSymbol = valueType
Do
If lhsType.IsArrayType AndAlso rhsType.IsArrayType Then
Dim lhsArrayType = DirectCast(lhsType, ArrayTypeSymbol)
Dim rhsArrayType = DirectCast(rhsType, ArrayTypeSymbol)
If lhsArrayType.Rank = rhsArrayType.Rank Then
lhsType = lhsArrayType.ElementType
rhsType = rhsArrayType.ElementType
Else
Exit Do
End If
ElseIf rhsType.IsArrayType OrElse Not lhsType.IsArrayType Then
Return valueType
Else
Exit Do
End If
Loop
Dim modifiedValueType = DecodeModifiedIdentifierType(name,
rhsType,
Nothing,
valueSyntax,
getRequireTypeDiagnosticInfoFunc,
diagnostics)
Return modifiedValueType
End If
End If
Return defaultType
End Function
'TODO: override in MethodBodySemanticModel similarly to BindVariableDeclaration.
Friend Overridable Function BindCatchVariableDeclaration(name As IdentifierNameSyntax,
asClause As AsClauseSyntax,
diagnostics As BindingDiagnosticBag) As BoundLocal
Dim identifier = name.Identifier
Dim symbol As LocalSymbol = GetLocalForDeclaration(identifier)
' Attempt to bind the type
Dim type As TypeSymbol = BindTypeSyntax(asClause.Type, diagnostics)
VerifyLocalSymbolNameAndSetType(symbol, type, name, identifier, diagnostics)
Return New BoundLocal(name, symbol, symbol.Type)
End Function
''' <summary>
''' Verifies that declaration of a local symbol does not cause name clashes.
''' </summary>
Private Sub VerifyLocalSymbolNameAndSetType(local As LocalSymbol,
type As TypeSymbol,
nameSyntax As VisualBasicSyntaxNode,
identifier As SyntaxToken,
diagnostics As BindingDiagnosticBag)
Dim localForFunctionValue = GetLocalForFunctionValue()
Dim name = identifier.ValueText
' Set the type of the symbol, so we don't have to re-compute it later.
local.SetType(type)
If localForFunctionValue IsNot Nothing AndAlso CaseInsensitiveComparison.Equals(local.Name, localForFunctionValue.Name) Then
' Does name conflict with the function name?
ReportDiagnostic(diagnostics, nameSyntax, ERRID.ERR_LocalSameAsFunc)
Else
Dim result = LookupResult.GetInstance()
Lookup(result, identifier.ValueText, 0, Nothing, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
' A local symbol will always be found because all local declarations are put into the
' local map in the blockbasebinder.
Debug.Assert(result.HasSingleSymbol AndAlso result.IsGood)
Dim lookupSymbol = DirectCast(result.SingleSymbol, LocalSymbol)
result.Free()
If lookupSymbol.IdentifierToken.FullSpan <> identifier.FullSpan Then
If lookupSymbol.CanBeReferencedByName Then
' Static Locals have there own diagnostic. ERR_DuplicateLocalStatic1
If Not lookupSymbol.IsStatic OrElse Not local.IsStatic Then
' If location does not match then this is a duplicate local, unless it has an invalid name (the syntax was bad)
ReportDiagnostic(diagnostics, nameSyntax, ERRID.ERR_DuplicateLocals1, name)
' Difference to Dev10:
' For r as Integer in New Integer() {}
' Dim r as Integer = 23
' next
' Does not give a BC30288 "Local variable 'r' is already declared in the current block.", but a
' BC30616 "Variable 'r' hides a variable in an enclosing block." with the same location.
' The reason is the binder hierarchy, where the ForOrForEachBlockBinder and the StatementListBinder both have
' r in their locals set. When looking up the symbol one gets the inner symbol and then the FullSpans match
' which then does not trigger the condition above.
End If
End If
Else
' Look up in container binders for name clashes with other locals, parameters and type parameters.
' Stop at the named type binder
Dim container = Me.ContainingBinder
If container IsNot Nothing Then
container.VerifyNameShadowingInMethodBody(local, nameSyntax, identifier, diagnostics)
End If
End If
End If
End Sub
''' <summary>
''' Should be called on the binder, at which the check should begin.
''' </summary>
Private Sub VerifyNameShadowingInMethodBody(
symbol As Symbol,
nameSyntax As SyntaxNodeOrToken,
identifier As SyntaxNodeOrToken,
diagnostics As BindingDiagnosticBag
)
Debug.Assert(symbol.Kind = SymbolKind.Local OrElse symbol.Kind = SymbolKind.RangeVariable OrElse
(symbol.Kind = SymbolKind.Parameter AndAlso
TypeOf symbol Is UnboundLambdaParameterSymbol))
Dim name As String = symbol.Name
' Look up in container binders for name clashes with other locals, parameters and type parameters.
' Stop at the named type binder
Dim container = Me
Dim result = LookupResult.GetInstance
Dim err As ERRID
Do
Dim namedTypeBinder = TryCast(container, NamedTypeBinder)
If namedTypeBinder IsNot Nothing Then
Exit Do
End If
result.Clear()
container.LookupInSingleBinder(result, name, 0, Nothing, Me, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
If result.HasSingleSymbol Then
Dim altSymbol = result.SingleSymbol
If altSymbol IsNot symbol Then
Select Case altSymbol.Kind
Case SymbolKind.Local, SymbolKind.RangeVariable
If symbol.Kind = SymbolKind.Parameter Then
err = ERRID.ERR_LambdaParamShadowLocal1
ElseIf symbol.Kind <> SymbolKind.RangeVariable Then
err = ERRID.ERR_BlockLocalShadowing1
ElseIf Me.ImplicitVariableDeclarationAllowed Then
err = ERRID.ERR_IterationVariableShadowLocal2
Else
err = ERRID.ERR_IterationVariableShadowLocal1
End If
ReportDiagnostic(diagnostics, nameSyntax, err, name)
Case SymbolKind.Parameter
If symbol.Kind = SymbolKind.Parameter Then
err = ERRID.ERR_LambdaParamShadowLocal1
ElseIf symbol.Kind <> SymbolKind.RangeVariable Then
If DirectCast(altSymbol.ContainingSymbol, MethodSymbol).MethodKind = MethodKind.LambdaMethod Then
err = ERRID.ERR_LocalNamedSameAsParamInLambda1
Else
err = ERRID.ERR_LocalNamedSameAsParam1
End If
ElseIf Me.ImplicitVariableDeclarationAllowed Then
err = ERRID.ERR_IterationVariableShadowLocal2
Else
err = ERRID.ERR_IterationVariableShadowLocal1
' Change from Dev10: we're now also correctly reporting this error for control variables
' of for/for each loops when they shadow variables that are parameters.
End If
ReportDiagnostic(diagnostics, nameSyntax, err, name)
Case SymbolKind.TypeParameter
' this diagnostic will not be shown if the symbol is for or for each local has an inferred type
Dim local = TryCast(symbol, LocalSymbol)
If local Is Nothing OrElse
Not (local.IsFor OrElse local.IsForEach) OrElse
Not local.HasInferredType Then
ReportDiagnostic(diagnostics, nameSyntax, ERRID.ERR_NameSameAsMethodTypeParam1, name)
End If
End Select
End If
Exit Do
End If
Dim implicitVariableBinder = TryCast(container, ImplicitVariableBinder)
If implicitVariableBinder IsNot Nothing AndAlso Not implicitVariableBinder.AllImplicitVariableDeclarationsAreHandled Then
' If an implicit local comes into being later, then report an error here.
If symbol.Kind = SymbolKind.Parameter Then
err = ERRID.ERR_LambdaParamShadowLocal1
ElseIf symbol.Kind <> SymbolKind.RangeVariable Then
err = ERRID.ERR_BlockLocalShadowing1
Else
err = ERRID.ERR_IterationVariableShadowLocal2
End If
implicitVariableBinder.RememberPossibleShadowingVariable(name, identifier, err)
End If
container = container.ContainingBinder
Loop While container IsNot Nothing
result.Free()
End Sub
Friend Function AdjustAssignmentTarget(node As SyntaxNode, op1 As BoundExpression, diagnostics As BindingDiagnosticBag, ByRef isError As Boolean) As BoundExpression
Select Case op1.Kind
Case BoundKind.XmlMemberAccess
Dim memberAccess = DirectCast(op1, BoundXmlMemberAccess)
Dim expr = AdjustAssignmentTarget(node, memberAccess.MemberAccess, diagnostics, isError)
Return memberAccess.Update(expr)
Case BoundKind.PropertyAccess
Dim propertyAccess As BoundPropertyAccess = DirectCast(op1, BoundPropertyAccess)
Dim propertySymbol As PropertySymbol = propertyAccess.PropertySymbol
If propertyAccess.IsLValue Then
Debug.Assert(propertySymbol.ReturnsByRef)
WarnOnRecursiveAccess(propertyAccess, PropertyAccessKind.Get, diagnostics)
Return propertyAccess.SetAccessKind(PropertyAccessKind.Get)
End If
Debug.Assert(propertyAccess.AccessKind <> PropertyAccessKind.Get)
Dim setMethod = propertySymbol.GetMostDerivedSetMethod()
If Not propertyAccess.IsWriteable Then
If setMethod Is Nothing Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_NoSetProperty1, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
Else
Debug.Assert(setMethod.IsInitOnly)
ReportDiagnostic(diagnostics, node, ERRID.ERR_AssignmentInitOnly, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
End If
isError = True
End If
' NOTE: the setMethod could not be present, while it would still be
' possible to write to the property in a case
' where the property is a getter-only autoproperty
' and the writing is happening in the corresponding constructor or initializer
If setMethod IsNot Nothing Then
If propertyAccess.IsWriteable AndAlso setMethod.IsInitOnly Then
InternalSyntax.Parser.CheckFeatureAvailability(diagnostics,
node.Location,
DirectCast(node.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion,
InternalSyntax.Feature.InitOnlySettersUsage)
End If
ReportDiagnosticsIfObsoleteOrNotSupported(diagnostics, setMethod, node)
If ReportUseSite(diagnostics, op1.Syntax, setMethod) Then
isError = True
Else
Dim accessThroughType = GetAccessThroughType(propertyAccess.ReceiverOpt)
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If Not IsAccessible(setMethod, useSiteInfo, accessThroughType) AndAlso
IsAccessible(propertySymbol, useSiteInfo, accessThroughType) Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_NoAccessibleSet, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
isError = True
End If
diagnostics.Add(node, useSiteInfo)
End If
End If
WarnOnRecursiveAccess(propertyAccess, PropertyAccessKind.Set, diagnostics)
Return propertyAccess.SetAccessKind(PropertyAccessKind.Set)
Case BoundKind.LateMemberAccess
Debug.Assert((DirectCast(op1, BoundLateMemberAccess).AccessKind And (LateBoundAccessKind.Get Or LateBoundAccessKind.Call)) = 0)
Return DirectCast(op1, BoundLateMemberAccess).SetAccessKind(LateBoundAccessKind.Set)
Case BoundKind.LateInvocation
Debug.Assert((DirectCast(op1, BoundLateInvocation).AccessKind And (LateBoundAccessKind.Get Or LateBoundAccessKind.Call)) = 0)
Return DirectCast(op1, BoundLateInvocation).SetAccessKind(LateBoundAccessKind.Set)
Case Else
Return op1
End Select
End Function
Private Function BindAssignment(node As SyntaxNode, op1 As BoundExpression, op2 As BoundExpression, diagnostics As BindingDiagnosticBag) As BoundAssignmentOperator
Dim isError As Boolean = False
op1 = AdjustAssignmentTarget(node, op1, diagnostics, isError)
Dim targetType As TypeSymbol = op1.Type
Debug.Assert(targetType IsNot Nothing OrElse isError)
If targetType IsNot Nothing Then
op2 = ApplyImplicitConversion(op2.Syntax, targetType, op2, diagnostics)
Else
' Try to reclassify op2 if we still can.
op2 = MakeRValueAndIgnoreDiagnostics(op2)
End If
Return New BoundAssignmentOperator(node, op1, op2, False, hasErrors:=isError)
End Function
Private Function BindCompoundAssignment(
node As VisualBasicSyntaxNode,
left As BoundExpression,
right As BoundExpression,
operatorTokenKind As SyntaxKind,
operatorKind As BinaryOperatorKind,
diagnostics As BindingDiagnosticBag
) As BoundAssignmentOperator
Dim isError As Boolean = False
' We should be able to use the 'left' as an assignment target and as an RValue.
' Let's verify that
Dim assignmentTarget As BoundExpression = AdjustAssignmentTarget(node, left, diagnostics, isError)
Dim rValue As BoundExpression
If isError Then
rValue = MakeRValueAndIgnoreDiagnostics(left)
Else
rValue = MakeRValue(left, diagnostics)
isError = rValue.HasErrors
End If
Dim targetType As TypeSymbol = assignmentTarget.Type
Debug.Assert(targetType IsNot Nothing OrElse isError)
Dim placeholder As BoundCompoundAssignmentTargetPlaceholder = Nothing
If isError Then
' Suppress all additional diagnostics. This ensures that we still generate the appropriate tree shape
' even in error scenarios
diagnostics = BindingDiagnosticBag.Discarded
End If
placeholder = New BoundCompoundAssignmentTargetPlaceholder(left.Syntax, targetType).MakeCompilerGenerated()
right = BindBinaryOperator(node, placeholder, right, operatorTokenKind, operatorKind, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics)
right.SetWasCompilerGenerated()
right = ApplyImplicitConversion(node, targetType, right, diagnostics)
left = left.SetGetSetAccessKindIfAppropriate()
Return New BoundAssignmentOperator(node, left, placeholder, right, False, hasErrors:=isError)
End Function
''' <summary>
''' Binds a list of statements and puts in a scope.
''' </summary>
Friend Function BindBlock(syntax As SyntaxNode, stmtList As SyntaxList(Of StatementSyntax), diagnostics As BindingDiagnosticBag) As BoundBlock
Dim stmtListBinder = Me.GetBinder(stmtList)
Return BindBlock(syntax, stmtList, diagnostics, stmtListBinder)
End Function
''' <summary>
''' Binds a list of statements and puts in a scope.
''' </summary>
Friend Function BindBlock(syntax As SyntaxNode, stmtList As SyntaxList(Of StatementSyntax), diagnostics As BindingDiagnosticBag, stmtListBinder As Binder) As BoundBlock
Dim boundStatements(stmtList.Count - 1) As BoundStatement
Dim locals As ArrayBuilder(Of LocalSymbol) = Nothing
For i = 0 To boundStatements.Length - 1
Dim boundStatement As BoundStatement = stmtListBinder.BindStatement(stmtList(i), diagnostics)
boundStatements(i) = boundStatement
Select Case boundStatement.Kind
Case BoundKind.LocalDeclaration
Dim localDecl As BoundLocalDeclaration = DirectCast(boundStatement, BoundLocalDeclaration)
DeclareLocal(locals, localDecl)
Case BoundKind.AsNewLocalDeclarations
Dim asNewLocalDecls As BoundAsNewLocalDeclarations = DirectCast(boundStatement, BoundAsNewLocalDeclarations)
DeclareLocal(locals, asNewLocalDecls)
Case BoundKind.DimStatement
Dim multipleDecl As BoundDimStatement = DirectCast(boundStatement, BoundDimStatement)
For Each localDecl In multipleDecl.LocalDeclarations
DeclareLocal(locals, localDecl)
Next
End Select
Next i
If locals Is Nothing Then
Return New BoundBlock(syntax, stmtList, ImmutableArray(Of LocalSymbol).Empty, boundStatements.AsImmutableOrNull())
End If
Return New BoundBlock(syntax, stmtList, locals.ToImmutableAndFree, boundStatements.AsImmutableOrNull())
End Function
Private Shared Sub DeclareLocal(ByRef locals As ArrayBuilder(Of LocalSymbol), localDecl As BoundLocalDeclarationBase)
If locals Is Nothing Then
locals = ArrayBuilder(Of LocalSymbol).GetInstance
End If
Select Case localDecl.Kind
Case BoundKind.LocalDeclaration
locals.Add(DirectCast(localDecl, BoundLocalDeclaration).LocalSymbol)
Case BoundKind.AsNewLocalDeclarations
For Each decl In DirectCast(localDecl, BoundAsNewLocalDeclarations).LocalDeclarations
locals.Add(DirectCast(decl, BoundLocalDeclaration).LocalSymbol)
Next
Case Else
Throw ExceptionUtilities.UnexpectedValue(localDecl.Kind)
End Select
End Sub
Private Function BindAssignmentStatement(node As AssignmentStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundExpressionStatement
Debug.Assert(node IsNot Nothing)
Dim op1 As BoundExpression = Me.BindAssignmentTarget(node.Left, diagnostics)
Dim op2 As BoundExpression = Me.BindValue(node.Right, diagnostics)
Debug.Assert(op1 IsNot Nothing)
Debug.Assert(op2 IsNot Nothing)
Dim expr As BoundAssignmentOperator
If (op1.HasErrors OrElse op2.HasErrors) Then
If Not op1.HasErrors AndAlso node.Kind = SyntaxKind.SimpleAssignmentStatement Then
expr = New BoundAssignmentOperator(node, op1,
ApplyImplicitConversion(node.Right, op1.Type, op2, diagnostics, False),
False,
op1.Type,
hasErrors:=True)
Else
' Try to reclassify op2 if we still can.
op2 = MakeRValueAndIgnoreDiagnostics(op2)
expr = New BoundAssignmentOperator(node, op1, op2, False, op1.Type, hasErrors:=True)
End If
ElseIf node.Kind = SyntaxKind.SimpleAssignmentStatement Then
expr = BindAssignment(node, op1, op2, diagnostics)
Else
Dim operatorKind As BinaryOperatorKind
Dim binaryTokenKind As SyntaxKind
Select Case node.Kind
Case SyntaxKind.AddAssignmentStatement
operatorKind = BinaryOperatorKind.Add
binaryTokenKind = SyntaxKind.PlusToken
Case SyntaxKind.SubtractAssignmentStatement
operatorKind = BinaryOperatorKind.Subtract
binaryTokenKind = SyntaxKind.MinusToken
Case SyntaxKind.MultiplyAssignmentStatement
operatorKind = BinaryOperatorKind.Multiply
binaryTokenKind = SyntaxKind.AsteriskToken
Case SyntaxKind.DivideAssignmentStatement
operatorKind = BinaryOperatorKind.Divide
binaryTokenKind = SyntaxKind.SlashToken
Case SyntaxKind.IntegerDivideAssignmentStatement
operatorKind = BinaryOperatorKind.IntegerDivide
binaryTokenKind = SyntaxKind.BackslashToken
Case SyntaxKind.ExponentiateAssignmentStatement
operatorKind = BinaryOperatorKind.Power
binaryTokenKind = SyntaxKind.CaretToken
Case SyntaxKind.LeftShiftAssignmentStatement
operatorKind = BinaryOperatorKind.LeftShift
binaryTokenKind = SyntaxKind.LessThanLessThanToken
Case SyntaxKind.RightShiftAssignmentStatement
operatorKind = BinaryOperatorKind.RightShift
binaryTokenKind = SyntaxKind.GreaterThanGreaterThanToken
Case SyntaxKind.ConcatenateAssignmentStatement
operatorKind = BinaryOperatorKind.Concatenate
binaryTokenKind = SyntaxKind.AmpersandToken
Case Else
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
expr = BindCompoundAssignment(node, op1, op2, binaryTokenKind, operatorKind, diagnostics)
End If
Return New BoundExpressionStatement(node, expr.MakeCompilerGenerated())
End Function
Private Function BindMidAssignmentStatement(node As AssignmentStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundExpressionStatement
Debug.Assert(node IsNot Nothing AndAlso node.Kind = SyntaxKind.MidAssignmentStatement AndAlso node.Left.Kind = SyntaxKind.MidExpression)
Dim midExpression = DirectCast(node.Left, MidExpressionSyntax)
Dim midArguments As SeparatedSyntaxList(Of ArgumentSyntax) = midExpression.ArgumentList.Arguments
Debug.Assert(midArguments.Count = 2 OrElse midArguments.Count = 3)
Dim targetSyntax = DirectCast(midArguments(0), SimpleArgumentSyntax).Expression
Dim target As BoundExpression = BindAssignmentTarget(targetSyntax, diagnostics)
Dim int32 As NamedTypeSymbol = GetSpecialType(SpecialType.System_Int32, midExpression, diagnostics)
Dim startSyntax = DirectCast(midArguments(1), SimpleArgumentSyntax).Expression
Dim start As BoundExpression = ApplyImplicitConversion(startSyntax, int32, BindValue(startSyntax, diagnostics), diagnostics)
Dim lengthOpt As BoundExpression = Nothing
If midArguments.Count > 2 Then
Dim lengthSyntax = DirectCast(midArguments(2), SimpleArgumentSyntax).Expression
lengthOpt = ApplyImplicitConversion(lengthSyntax, int32, BindValue(lengthSyntax, diagnostics), diagnostics)
End If
Dim stringType As NamedTypeSymbol = GetSpecialType(SpecialType.System_String, midExpression, diagnostics)
VerifyTypeCharacterConsistency(midExpression.Mid, stringType, midExpression.Mid.GetTypeCharacter(), diagnostics)
Dim source As BoundExpression = ApplyImplicitConversion(node.Right, stringType, BindValue(node.Right, diagnostics), diagnostics)
' We should be able to use the 'target' as an assignment target and as an RValue.
' Let's verify that
Dim isError As Boolean
Dim assignmentTarget As BoundExpression = AdjustAssignmentTarget(targetSyntax, target, diagnostics, isError)
If Not isError Then
isError = MakeRValue(target, diagnostics).HasErrors
End If
Dim targetType As TypeSymbol = assignmentTarget.Type
Debug.Assert(targetType IsNot Nothing OrElse isError)
Dim placeholder As BoundCompoundAssignmentTargetPlaceholder
Dim original As BoundExpression
If Not isError Then
placeholder = New BoundCompoundAssignmentTargetPlaceholder(targetSyntax, targetType).MakeCompilerGenerated()
original = ApplyImplicitConversion(targetSyntax, stringType, placeholder, diagnostics)
Else
placeholder = Nothing
original = BadExpression(targetSyntax, stringType).MakeCompilerGenerated()
End If
' SemanticModel should be able to find node corresponding to the MidExpressionSyntax, which has type String and no associated symbols.
' Wrapping 'original' with BoundParenthesized node tied to MidExpressionSyntax should suffice for this purpose.
Dim right As BoundExpression = New BoundMidResult(node,
New BoundParenthesized(midExpression, original, original.Type),
start, lengthOpt, source, stringType).MakeCompilerGenerated()
If Not isError Then
right = ApplyImplicitConversion(node, targetType, right, diagnostics).MakeCompilerGenerated()
' Either we have both "in" and "out" conversions or none.
Debug.Assert((original.Kind = BoundKind.CompoundAssignmentTargetPlaceholder) = (right.Kind = BoundKind.MidResult) OrElse original.HasErrors OrElse right.HasErrors)
End If
target = target.SetGetSetAccessKindIfAppropriate()
Return New BoundExpressionStatement(node, New BoundAssignmentOperator(node, target, placeholder, right, False,
Compilation.GetSpecialType(SpecialType.System_Void),
hasErrors:=isError).MakeCompilerGenerated())
End Function
Private Function BindAddRemoveHandlerStatement(node As AddRemoveHandlerStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundAddRemoveHandlerStatement
Dim eventSymbol As EventSymbol = Nothing
Dim actualEventAccess As BoundEventAccess = Nothing
Dim eventAccess As BoundExpression = BindEventAccess(node.EventExpression, diagnostics, actualEventAccess, eventSymbol)
Dim handlerExpression = BindValue(node.DelegateExpression, diagnostics)
Dim isRemoveHandler As Boolean = node.Kind = SyntaxKind.RemoveHandlerStatement
If isRemoveHandler Then
Dim toCheck As BoundExpression = handlerExpression
' Dig through parenthesized.
toCheck = toCheck.GetMostEnclosedParenthesizedExpression()
If toCheck.Kind = BoundKind.UnboundLambda Then
ReportDiagnostic(diagnostics, node.DelegateExpression, ERRID.WRN_LambdaPassedToRemoveHandler)
End If
End If
' we may need to convert handler to the accessor parameter type.
' in a case where handler is a lambda or AddressOf we definitely need a conversion
' NOTE that handler may not be a delegate, but could have a user defined conversion to a delegate.
Dim hasErrors As Boolean = True
If eventSymbol IsNot Nothing Then
Dim method = If(node.Kind = SyntaxKind.AddHandlerStatement, eventSymbol.AddMethod, eventSymbol.RemoveMethod)
If method Is Nothing Then
If eventSymbol.DeclaringCompilation IsNot Me.Compilation Then
ReportDiagnostic(diagnostics, node.EventExpression, ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedEvent1, eventSymbol))
End If
Else
Dim targetType = eventSymbol.Type
handlerExpression = ApplyImplicitConversion(node.DelegateExpression, targetType, handlerExpression, diagnostics)
If isRemoveHandler AndAlso
handlerExpression.Kind = BoundKind.DelegateCreationExpression AndAlso
node.DelegateExpression.Kind = SyntaxKind.AddressOfExpression Then
Dim delCreation = DirectCast(handlerExpression, BoundDelegateCreationExpression)
If delCreation.RelaxationLambdaOpt IsNot Nothing Then
Dim addrOffExpr = DirectCast(node.DelegateExpression, UnaryExpressionSyntax)
ReportDiagnostic(diagnostics, addrOffExpr.Operand, ERRID.WRN_RelDelegatePassedToRemoveHandler)
End If
End If
hasErrors = False
If method = Me.ContainingMember Then
If method.IsShared OrElse actualEventAccess.ReceiverOpt.IsMeReference Then
'Statement recursively calls the containing '{0}' for event '{1}'.
ReportDiagnostic(diagnostics,
node,
ERRID.WRN_RecursiveAddHandlerCall,
node.AddHandlerOrRemoveHandlerKeyword.ToString,
eventSymbol.Name)
End If
End If
If Not targetType.IsDelegateType() Then
If eventSymbol.DeclaringCompilation IsNot Me.Compilation AndAlso TypeOf targetType IsNot MissingMetadataTypeSymbol Then
ReportDiagnostic(diagnostics, node.EventExpression, ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedEvent1, eventSymbol))
End If
hasErrors = True
Else
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim accessThroughType = GetAccessThroughType(actualEventAccess.ReceiverOpt)
If Not Me.IsAccessible(method, useSiteInfo, accessThroughType) Then
Debug.Assert(eventSymbol.DeclaringCompilation IsNot Me.Compilation)
ReportDiagnostic(diagnostics, node.EventExpression, GetInaccessibleErrorInfo(method))
End If
diagnostics.Add(node.EventExpression, useSiteInfo)
Dim badShape As Boolean = False
' Decrease noise in diagnostics, if event is "bad", we already complained about it.
If eventSymbol.GetUseSiteInfo().DiagnosticInfo Is Nothing AndAlso
ReportUseSite(diagnostics, node.EventExpression, method.GetUseSiteInfo()) Then
Debug.Assert(eventSymbol.DeclaringCompilation IsNot Me.Compilation)
hasErrors = True
ElseIf method.ParameterCount <> 1 OrElse method.Parameters(0).IsByRef Then
badShape = True
ElseIf eventSymbol.IsWindowsRuntimeEvent Then
Dim tokenType As NamedTypeSymbol = Me.Compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationToken)
If node.Kind = SyntaxKind.AddHandlerStatement Then
If Not method.Parameters(0).Type.IsSameTypeIgnoringAll(targetType) OrElse
Not method.ReturnType.IsSameTypeIgnoringAll(tokenType) Then
badShape = True
End If
ElseIf Not method.Parameters(0).Type.IsSameTypeIgnoringAll(tokenType) OrElse Not method.IsSub Then
badShape = True
End If
ElseIf Not method.Parameters(0).Type.IsSameTypeIgnoringAll(targetType) Then
badShape = True
End If
If badShape Then
If eventSymbol.DeclaringCompilation IsNot Me.Compilation Then
ReportDiagnostic(diagnostics, node.EventExpression, ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedMethod1, method))
End If
hasErrors = True
End If
End If
End If
End If
If node.Kind = SyntaxKind.AddHandlerStatement Then
Return New BoundAddHandlerStatement(node, eventAccess, handlerExpression, hasErrors:=hasErrors)
Else
Return New BoundRemoveHandlerStatement(node, eventAccess, handlerExpression, hasErrors:=hasErrors)
End If
End Function
Private Function BindEventAccess(node As ExpressionSyntax,
diagnostics As BindingDiagnosticBag,
<Out()> ByRef actualEventAccess As BoundEventAccess,
<Out()> ByRef eventSymbol As EventSymbol) As BoundExpression
' event must be a simple name that could be qualified and perhaps parenthesized
' Examples: goo , (goo) , (bar.goo) , baz.moo(of T).goo
Dim notParenthesizedSyntax = node
While notParenthesizedSyntax.Kind = SyntaxKind.ParenthesizedExpression
notParenthesizedSyntax = DirectCast(notParenthesizedSyntax, ParenthesizedExpressionSyntax).Expression
End While
Dim notQualifiedSyntax = notParenthesizedSyntax
If notQualifiedSyntax.Kind = SyntaxKind.SimpleMemberAccessExpression Then
notQualifiedSyntax = DirectCast(notQualifiedSyntax, MemberAccessExpressionSyntax).Name
End If
If notQualifiedSyntax.Kind <> SyntaxKind.IdentifierName Then
ReportDiagnostic(diagnostics, notQualifiedSyntax, ERRID.ERR_AddOrRemoveHandlerEvent)
Dim errorRecovery As BoundExpression = BindRValue(node, BindingDiagnosticBag.Discarded)
Return errorRecovery
End If
'NOTE this may bind to extension methods.
'It could be unnecessary perf hit, but it will happen only in error cases (event not found)
'so is not a big concern.
Dim result As BoundExpression
' when binding a simple name event we know that it must be a member since methods do not declare events
If notParenthesizedSyntax.Kind = SyntaxKind.IdentifierName Then
Dim simpleNameSyntax As SimpleNameSyntax = DirectCast(notParenthesizedSyntax, IdentifierNameSyntax)
result = BindSimpleName(simpleNameSyntax, False, diagnostics, skipLocalsAndParameters:=True)
Else
result = BindExpression(node, isInvocationOrAddressOf:=False, isOperandOfConditionalBranch:=False, eventContext:=True, diagnostics:=diagnostics)
End If
Debug.Assert(result IsNot Nothing)
' Dev10 allows parenthesizing of the event
Dim notParenthesized = result.GetMostEnclosedParenthesizedExpression()
If notParenthesized.Kind = BoundKind.EventAccess Then
actualEventAccess = DirectCast(notParenthesized, BoundEventAccess)
eventSymbol = actualEventAccess.EventSymbol
Else
' TODO: Should try to report ERR_NameNotEvent2 without making notParenthesized an RValue,
' it might cause errors for write-only property, etc.
notParenthesized = MakeRValue(notParenthesized, diagnostics)
' this is not an event. Add diagnostics if node is not already bad
' NOTE that we are not marking the node as bad if it is not
' in such case there is nothing wrong with the event access node.
' However since we cannot provide event symbol, the AddRemovehandler
' node will be marked as HasErrors.
If Not notParenthesized.HasErrors Then
Dim name = DirectCast(notQualifiedSyntax, IdentifierNameSyntax).Identifier.ValueText
Dim exprSymbol = notParenthesized.ExpressionSymbol
Dim container = If(exprSymbol IsNot Nothing, exprSymbol.ContainingSymbol, Compilation.GetSpecialType(SpecialType.System_Object))
ReportDiagnostic(diagnostics, notQualifiedSyntax, ERRID.ERR_NameNotEvent2, name, container)
End If
End If
Return result
End Function
Private Function BindRaiseEventStatement(node As RaiseEventStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim hasErrors = False
Dim target As BoundExpression = BindSimpleName(node.Name, False, diagnostics, skipLocalsAndParameters:=True)
If target.Kind <> BoundKind.EventAccess Then
If Not target.HasErrors Then
ReportDiagnostic(diagnostics, node.Name, ERRID.ERR_NameNotEvent2, node.Name.ToString, Me.ContainingType)
End If
Return New BoundBadStatement(node, ImmutableArray.Create(Of BoundNode)(target), True)
End If
Dim targetAsEvent = DirectCast(target, BoundEventAccess)
' Get the bound arguments and the argument names.
Dim boundArguments As ImmutableArray(Of BoundExpression) = Nothing
Dim argumentNames As ImmutableArray(Of String) = Nothing
Dim argumentNamesLocations As ImmutableArray(Of Location) = Nothing
BindArgumentsAndNames(node.ArgumentList, boundArguments, argumentNames, argumentNamesLocations, diagnostics)
Dim eventSym = targetAsEvent.EventSymbol
Dim receiver As BoundExpression
Dim fireMethod As MethodSymbol
If eventSym.HasAssociatedField Then
' field is not nothing when event IsFieldLike
Dim eventField = eventSym.AssociatedField
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If Not IsAccessible(eventField, useSiteInfo, Me.ContainingType) Then
ReportDiagnostic(diagnostics, node.Name, ERRID.ERR_CantRaiseBaseEvent)
hasErrors = True
End If
diagnostics.Add(node.Name, useSiteInfo)
Debug.Assert(TypeSymbol.Equals(targetAsEvent.Type, eventField.Type, TypeCompareKind.ConsiderEverything) OrElse eventSym.IsWindowsRuntimeEvent, "non-WinRT event should have the same type as its backing field")
receiver = New BoundFieldAccess(node.Name,
targetAsEvent.ReceiverOpt,
eventField,
False,
eventField.Type).MakeCompilerGenerated
Dim eventType = TryCast(eventSym.Type, NamedTypeSymbol)
' Detect circumstances where we can't continue.
If eventType Is Nothing OrElse eventType.DelegateInvokeMethod Is Nothing Then
' If we have a delegate type and it has no Invoke method, then we should give a diagnostic.
' Dev10 gives no diagnostics here, but we should.
' If something else went wrong (i.e. we don't have a delegate type), then a diagnostic was reported elsewhere.
If eventType IsNot Nothing AndAlso eventType.IsDelegateType Then
ReportDiagnostic(diagnostics, node.Name, ERRID.ERR_EventTypeNotDelegate)
End If
Return New BoundBadStatement(node, StaticCast(Of BoundNode).From(boundArguments).Add(target), True)
End If
fireMethod = eventType.DelegateInvokeMethod
If ReportUseSite(diagnostics, node.Name, fireMethod) Then
hasErrors = True
End If
If Not fireMethod.IsSub Then
' // Something is bad somewhere.
' Dev10 gives no diagnostics here, but we should.
ReportDiagnostic(diagnostics, node.Name, ERRID.ERR_EventsCantBeFunctions)
Return New BoundBadStatement(node, StaticCast(Of BoundNode).From(boundArguments).Add(target), True)
End If
Else
receiver = targetAsEvent.ReceiverOpt
fireMethod = eventSym.RaiseMethod
If fireMethod Is Nothing Then
' // Something is bad somewhere.
' Dev10 gives no diagnostics here, but we should.
ReportDiagnostic(diagnostics, node.Name, ERRID.ERR_MissingRaiseEventDef1, node.Name.ToString)
Return New BoundBadStatement(node, StaticCast(Of BoundNode).From(boundArguments).Add(target), True)
End If
If ReportUseSite(diagnostics, node.Name, fireMethod) Then
hasErrors = True
End If
If Not fireMethod.IsSub Then
' // Something is bad somewhere.
' Dev10 gives no diagnostics here, but we should.
ReportDiagnostic(diagnostics, node.Name, ERRID.ERR_EventsCantBeFunctions)
Return New BoundBadStatement(node, StaticCast(Of BoundNode).From(boundArguments).Add(target), True)
End If
If Not TypeSymbol.Equals(fireMethod.ContainingType, Me.ContainingType, TypeCompareKind.ConsiderEverything) Then
' Re: Dev10
' // UNDONE: harishk - note that this is different from the check for an
' // accessible event field for non-block events. This is because there
' // is a bug for non-block events which contrary to the spec does allow
' // base class events to be raised in some scenarios. Sent email to
' // paulv and amandas to check if we can update all of this according
' // to spec. After a final answer is received, we will need to make
' // both consistent. More work is required if we need to make block
' // events' behavior be the same as that of non-block events. Also for
' // both cases, it would be hard to get the raising of base events
' // working for metadata events.
'
' 'RaiseEvent' definition missing for event '{0}'.
ReportDiagnostic(diagnostics, node.Name, ERRID.ERR_CantRaiseBaseEvent, node.Name.ToString)
Return New BoundBadStatement(node, StaticCast(Of BoundNode).From(boundArguments).Add(target), True)
End If
End If
Debug.Assert(fireMethod.IsSub, "we got this far, we must have a valid fireMethod")
If fireMethod = Me.ContainingMember Then
If fireMethod.IsShared OrElse receiver.IsMeReference Then
'Statement recursively calls the containing '{0}' for event '{1}'.
ReportDiagnostic(diagnostics,
node,
ERRID.WRN_RecursiveAddHandlerCall,
node.RaiseEventKeyword.ToString,
eventSym.Name)
End If
End If
' eventDelegateField.Invoke
Dim methodGroup = New BoundMethodGroup(node,
Nothing,
ImmutableArray.Create(fireMethod),
LookupResultKind.Good,
receiver,
QualificationKind.QualifiedViaValue).MakeCompilerGenerated()
'NOTE: Dev10 allows and ignores type characters on the event here.
Dim invocation = BindInvocationExpression(node,
node.Name,
TypeCharacter.None,
methodGroup,
boundArguments,
argumentNames,
diagnostics,
callerInfoOpt:=Nothing,
representCandidateInDiagnosticsOpt:=eventSym).MakeCompilerGenerated
Return New BoundRaiseEventStatement(node, eventSym, invocation, hasErrors)
End Function
Private Function BindExpressionStatement(statement As ExpressionStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim expression = statement.Expression
Dim boundExpression As BoundExpression
Select Case expression.Kind
Case SyntaxKind.InvocationExpression,
SyntaxKind.ConditionalAccessExpression
boundExpression = BindInvocationExpressionAsStatement(expression, diagnostics)
Case SyntaxKind.AwaitExpression
boundExpression = BindAwait(DirectCast(expression, AwaitExpressionSyntax), diagnostics, bindAsStatement:=True)
Case Else
' TODO(ADGreen): This case covers top-level expressions in interactive.
' Otherwise it should be an error.
boundExpression = BindRValue(expression, diagnostics)
End Select
WarnOnUnobservedCallThatReturnsAnAwaitable(statement, boundExpression, diagnostics)
Return New BoundExpressionStatement(statement, boundExpression)
End Function
Private Sub WarnOnUnobservedCallThatReturnsAnAwaitable(statement As ExpressionStatementSyntax, boundExpression As BoundExpression, diagnostics As BindingDiagnosticBag)
If boundExpression.Kind = BoundKind.ConditionalAccess Then
WarnOnUnobservedCallThatReturnsAnAwaitable(statement, DirectCast(boundExpression, BoundConditionalAccess).AccessExpression, diagnostics)
Return
End If
If Not boundExpression.HasErrors AndAlso
Not boundExpression.Kind = BoundKind.AwaitOperator AndAlso
Not boundExpression.Type.IsErrorType() AndAlso
Not boundExpression.Type.IsVoidType() AndAlso
Not boundExpression.Type.IsObjectType() Then
' Check if we should warn for an unobserved call that returns an awaitable value.
' We will show a warning:
' 1. In any method, when invoking a method that is marked
' "Async" and returns a type other than void. Note that
' this requires the caller and callee to be in the same
' compilation unit, as "Async" is not propagated into
' metadata.
' 2. In any method, when invoking a method that returns one
' of the Windows Runtime async types:
' IAsyncAction
' IAsyncActionWithProgress(Of T)
' IAsyncOperation(Of T)
' IAsyncOperationWithProgress(Of T, U)
' 3. In an async method, when invoking a method that returns
' any awaitable type.
Dim warn As Boolean = False
If boundExpression.Kind = BoundKind.Call Then
Dim [call] = DirectCast(boundExpression, BoundCall)
' 1.
warn = [call].Method.IsAsync AndAlso [call].Method.ContainingAssembly Is Me.Compilation.Assembly
End If
If Not warn Then
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
' 2.
If IsOrInheritsFromOrImplementsInterface(boundExpression.Type, WellKnownType.Windows_Foundation_IAsyncAction, useSiteInfo:=useSiteInfo) OrElse
IsOrInheritsFromOrImplementsInterface(boundExpression.Type, WellKnownType.Windows_Foundation_IAsyncActionWithProgress_T, useSiteInfo:=useSiteInfo) OrElse
IsOrInheritsFromOrImplementsInterface(boundExpression.Type, WellKnownType.Windows_Foundation_IAsyncOperation_T, useSiteInfo:=useSiteInfo) OrElse
IsOrInheritsFromOrImplementsInterface(boundExpression.Type, WellKnownType.Windows_Foundation_IAsyncOperationWithProgress_T2, useSiteInfo:=useSiteInfo) Then
diagnostics.AddDependencies(useSiteInfo)
warn = True
ElseIf IsInAsyncContext() Then
' 3.
Dim diagBag = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=diagnostics.AccumulatesDependencies)
If Not BindAwait(statement, boundExpression, diagBag, bindAsStatement:=True).HasErrors AndAlso Not diagBag.HasAnyErrors Then
diagnostics.AddDependencies(diagBag)
warn = True
End If
diagBag.Free()
End If
End If
If warn Then
ReportDiagnostic(diagnostics, statement, ERRID.WRN_UnobservedAwaitableExpression)
End If
End If
End Sub
Private Function IsOrInheritsFromOrImplementsInterface(derivedType As TypeSymbol, interfaceType As WellKnownType, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Dim type As NamedTypeSymbol = Compilation.GetWellKnownType(interfaceType)
Return Not type.IsErrorType() AndAlso type.IsInterfaceType() AndAlso
IsOrInheritsFromOrImplementsInterface(derivedType, type, useSiteInfo)
End Function
Private Function BindPrintStatement(printStmt As PrintStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim boundExpression As BoundExpression = BindRValue(printStmt.Expression, diagnostics)
Return New BoundExpressionStatement(printStmt, boundExpression)
End Function
Private Function BindCallStatement(callStmt As CallStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim boundInvocation As BoundExpression = BindInvocationExpressionAsStatement(callStmt.Invocation, diagnostics)
Return New BoundExpressionStatement(callStmt, boundInvocation)
End Function
Private Function BindInvocationExpressionAsStatement(expression As ExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Return ReclassifyInvocationExpressionAsStatement(BindExpression(expression, diagnostics), diagnostics)
End Function
Friend Function ReclassifyInvocationExpressionAsStatement(boundInvocation As BoundExpression, diagnostics As BindingDiagnosticBag) As BoundExpression
Select Case boundInvocation.Kind
Case BoundKind.PropertyAccess
boundInvocation = MakeRValue(boundInvocation, diagnostics)
' specially for properties being called in Call statement context
If Not boundInvocation.HasErrors Then
ReportDiagnostic(diagnostics, boundInvocation.Syntax, ERRID.ERR_PropertyAccessIgnored)
End If
Case BoundKind.LateMemberAccess
' matches setting ExprResultNotNeeded flag in StatementSemantics/Semantics::InterpretCall
boundInvocation = DirectCast(boundInvocation, BoundLateMemberAccess).SetAccessKind(LateBoundAccessKind.Call)
Case BoundKind.LateInvocation
' matches setting ExprResultNotNeeded flag in StatementSemantics/Semantics::InterpretCall
Dim lateInvocation = DirectCast(boundInvocation, BoundLateInvocation).SetAccessKind(LateBoundAccessKind.Call)
boundInvocation = lateInvocation
' specially for properties being called in Call statement context
If Not lateInvocation.HasErrors AndAlso
TryCast(lateInvocation.MethodOrPropertyGroupOpt, BoundPropertyGroup) IsNot Nothing Then
ReportDiagnostic(diagnostics, boundInvocation.Syntax, ERRID.ERR_PropertyAccessIgnored)
End If
Case BoundKind.ConditionalAccess
Debug.Assert(boundInvocation.Type Is Nothing)
Dim conditionalAccess = DirectCast(boundInvocation, BoundConditionalAccess)
boundInvocation = conditionalAccess.Update(conditionalAccess.Receiver,
conditionalAccess.Placeholder,
ReclassifyInvocationExpressionAsStatement(conditionalAccess.AccessExpression, diagnostics),
GetSpecialType(SpecialType.System_Void, conditionalAccess.Syntax, diagnostics))
End Select
Return boundInvocation
End Function
Private Function BindSingleLineIfStatement(node As SingleLineIfStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Debug.Assert(node IsNot Nothing)
Dim condition As BoundExpression
Dim consequence As BoundBlock
Dim alternative As BoundStatement = Nothing
condition = BindBooleanExpression(node.Condition, diagnostics)
consequence = BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
If node.ElseClause IsNot Nothing Then
alternative = BindBlock(node.ElseClause, node.ElseClause.Statements, diagnostics)
End If
Return New BoundIfStatement(node, condition, consequence, alternative)
End Function
Private Function BindMultiLineIfBlock(node As MultiLineIfBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Debug.Assert(node IsNot Nothing)
' We need to bind the conditions and blocks in lexical order (so that Option Explicit binding
' works right), but build the bound tree in reverse order.
Dim blocks As ArrayBuilder(Of BoundStatement) = ArrayBuilder(Of BoundStatement).GetInstance()
Dim conditions As ArrayBuilder(Of BoundExpression) = ArrayBuilder(Of BoundExpression).GetInstance()
conditions.Add(BindBooleanExpression(node.IfStatement.Condition, diagnostics))
blocks.Add(BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated())
For i = 0 To node.ElseIfBlocks.Count - 1
Dim elseIfBlock = node.ElseIfBlocks(i)
conditions.Add(BindBooleanExpression(elseIfBlock.ElseIfStatement.Condition, diagnostics))
blocks.Add(BindBlock(elseIfBlock, elseIfBlock.Statements, diagnostics).MakeCompilerGenerated())
Next
Dim currentAlternative As BoundStatement = Nothing
If node.ElseBlock IsNot Nothing Then
currentAlternative = BindBlock(node.ElseBlock, node.ElseBlock.Statements, diagnostics)
End If
For i = conditions.Count - 1 To 0 Step -1
Dim syntax As VisualBasicSyntaxNode
If i = 0 Then
syntax = node
Else
syntax = node.ElseIfBlocks(i - 1)
End If
currentAlternative = New BoundIfStatement(syntax, conditions(i), blocks(i), currentAlternative)
Next
blocks.Free()
conditions.Free()
Return currentAlternative
End Function
Private Function BindDoLoop(node As DoLoopBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Debug.Assert(node IsNot Nothing)
Dim topCondition As BoundExpression = Nothing
Dim bottomCondition As BoundExpression = Nothing
Dim isTopUntil As Boolean = False
Dim isBottomUntil As Boolean = False
' Bind the top condition, if any.
Dim topConditionSyntax = node.DoStatement.WhileOrUntilClause
If topConditionSyntax IsNot Nothing Then
topCondition = BindBooleanExpression(topConditionSyntax.Condition, diagnostics)
isTopUntil = (topConditionSyntax.Kind = SyntaxKind.UntilClause)
End If
' Get the binder for the body of the loop. This defines the break and continue labels.
Dim loopBodyBinder = GetBinder(DirectCast(node, VisualBasicSyntaxNode))
' Bind the body of the loop.
Dim loopBody As BoundBlock = loopBodyBinder.BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
' Bind the bottom condition, if any.
Dim bottomConditionSyntax = node.LoopStatement.WhileOrUntilClause
If bottomConditionSyntax IsNot Nothing Then
bottomCondition = BindBooleanExpression(bottomConditionSyntax.Condition, diagnostics)
isBottomUntil = (bottomConditionSyntax.Kind = SyntaxKind.UntilClause)
End If
' Create the bound node.
Return New BoundDoLoopStatement(node, topCondition, bottomCondition, isTopUntil, isBottomUntil, loopBody,
continueLabel:=loopBodyBinder.GetContinueLabel(SyntaxKind.ContinueDoStatement),
exitLabel:=loopBodyBinder.GetExitLabel(SyntaxKind.ExitDoStatement),
hasErrors:=topCondition IsNot Nothing AndAlso bottomCondition IsNot Nothing)
End Function
Private Function BindWhileBlock(node As WhileBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Debug.Assert(node IsNot Nothing)
' Bind condition
Dim condition As BoundExpression = BindBooleanExpression(node.WhileStatement.Condition, diagnostics)
' Get the binder for the body of the loop. This defines the break and continue labels.
Dim loopBodyBinder = GetBinder(node)
' Bind the body of the loop.
Dim loopBody As BoundBlock = loopBodyBinder.BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
' Create the bound node.
Return New BoundWhileStatement(node, condition, loopBody,
continueLabel:=loopBodyBinder.GetContinueLabel(SyntaxKind.ContinueWhileStatement),
exitLabel:=loopBodyBinder.GetExitLabel(SyntaxKind.ExitWhileStatement))
End Function
Public Function BindForToBlock(node As ForOrForEachBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
' For statement has its own binding scope since it may introduce iteration variable
' that is visible through the entire For block. It also needs to support Continue/Exit
' Interestingly, control variable is in scope when Limit and Step or the collection are bound,
' but initialized after Limit and Step or collection are evaluated...
Dim loopBinder = Me.GetBinder(node)
Debug.Assert(loopBinder IsNot Nothing)
Dim declaredOrInferredLocalOpt As LocalSymbol = Nothing
Dim isInferredLocal As Boolean = False
Dim controlVariable As BoundExpression = Nothing
Dim hasErrors As Boolean = False
' bind common parts of a for block
hasErrors = loopBinder.BindForBlockParts(node,
node.ForOrForEachStatement.ControlVariable,
declaredOrInferredLocalOpt,
controlVariable,
isInferredLocal,
diagnostics)
' return the specific BoundForToStatement
Return loopBinder.BindForToBlockParts(node,
declaredOrInferredLocalOpt,
controlVariable,
isInferredLocal,
hasErrors,
diagnostics)
End Function
Public Function BindForEachBlock(node As ForOrForEachBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
' For statement has its own binding scope since it may introduce iteration variable
' that is visible through the entire For block. It also needs to support Continue/Exit
' Interestingly, control variable is in scope when Limit and Step or the collection are bound,
' but initialized after Limit and Step or collection are evaluated...
Dim loopBinder = Me.GetBinder(node)
Debug.Assert(loopBinder IsNot Nothing)
Dim declaredOrInferredLocalOpt As LocalSymbol = Nothing
Dim isInferredLocal As Boolean = False
Dim controlVariable As BoundExpression = Nothing
Dim loopBody As BoundBlock = Nothing
Dim nextVariables As ImmutableArray(Of BoundExpression) = Nothing
Dim hasErrors As Boolean = False
' bind common parts of a for block
hasErrors = loopBinder.BindForBlockParts(node,
DirectCast(node.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable,
declaredOrInferredLocalOpt,
controlVariable,
isInferredLocal,
diagnostics)
' return the specific BoundForEachStatement
Return loopBinder.BindForEachBlockParts(node,
declaredOrInferredLocalOpt,
controlVariable,
isInferredLocal,
diagnostics)
End Function
''' <summary>
''' Binds all the common part for ForTo and ForEach loops except the loop body and the next variables.
''' </summary>
''' <param name="node">The node.</param>
''' <param name="controlVariableSyntax">The control variable syntax.</param>
''' <param name="declaredOrInferredLocalOpt">The declared or inferred local symbol.</param>
''' <param name="controlVariable">The control variable.</param>
''' <param name="diagnostics">The diagnostics.</param>
''' <returns>true if there were errors; otherwise false</returns>
Private Function BindForBlockParts(node As ForOrForEachBlockSyntax,
controlVariableSyntax As VisualBasicSyntaxNode,
<Out()> ByRef declaredOrInferredLocalOpt As LocalSymbol,
<Out()> ByRef controlVariable As BoundExpression,
<Out()> ByRef isInferredLocal As Boolean,
diagnostics As BindingDiagnosticBag) As Boolean
' Bind control variable
' There are two forms of control variables -
' 1) Control variable declared within the ForStatement
' 2) Expression reference that points to something (local, field...) declared outside of the loop.
Dim hasErrors As Boolean = False
declaredOrInferredLocalOpt = Nothing
isInferredLocal = False
If controlVariableSyntax.Kind = SyntaxKind.VariableDeclarator Then
' This case handles control variables for
' for x as integer = 0 to n
' for each x? in collection
Dim declaratorSyntax = DirectCast(controlVariableSyntax, VariableDeclaratorSyntax)
hasErrors = Not VerifyForControlVariableDeclaration(declaratorSyntax, diagnostics)
' 10.9.3
' 1. If the loop control variable is an identifier with an As clause, the identifier defines a new local variable of
' the type specified in the As clause, scoped to the entire For Each loop.
Dim asClauseOpt = declaratorSyntax.AsClause
Dim names = declaratorSyntax.Names
Debug.Assert(declaratorSyntax.Initializer Is Nothing, "should not have initializer")
Debug.Assert(names.Count = 1, "should be exactly one control variable")
Dim localDeclaration = BindVariableDeclaration(declaratorSyntax,
names(0),
asClauseOpt,
equalsValueOpt:=Nothing,
diagnostics:=diagnostics)
declaredOrInferredLocalOpt = localDeclaration.LocalSymbol
controlVariable = New BoundLocal(declaratorSyntax, declaredOrInferredLocalOpt, declaredOrInferredLocalOpt.Type)
Else
' This case handles control variables for
' for i = 0 to n
' for each Me.MyField in collection
' if it is a simple identifier and Option Infer is on, this might be new local declaration with an
' inferred type.
If controlVariableSyntax.Kind = SyntaxKind.IdentifierName Then
Dim identifier = DirectCast(controlVariableSyntax, IdentifierNameSyntax).Identifier
Dim name = identifier.ValueText
Dim result As LookupResult = LookupResult.GetInstance
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Lookup(result, name, 0, LookupOptions.AllMethodsOfAnyArity, useSiteInfo)
diagnostics.Add(node, useSiteInfo)
' If a local symbol is found then check whether this local corresponds to this identifier. If it does then
' this is local is an inferred local for the for block. This local does not have a type yet. Return the
' local and infer the type when the from/to or for-each collection expressions are available for binding.
If result.IsGood AndAlso
result.Symbols(0).Kind = SymbolKind.Local Then
Dim localSymbol = DirectCast(result.Symbols(0), LocalSymbol)
If localSymbol.IdentifierToken = identifier Then
' This is an inferred local, we will need to compute its type.
isInferredLocal = True
declaredOrInferredLocalOpt = localSymbol
End If
End If
result.Free()
End If
' it's something else than a simple name (e.g. qualified name ...), so we're going to bind it and
' check if the bound node meets the expectation (e.g. is a field or a local)
If Not isInferredLocal Then
If Not TryBindLoopControlVariable(controlVariableSyntax, controlVariable, diagnostics) Then
hasErrors = True
End If
Else
controlVariable = Nothing
End If
End If
' we cannot bind the loop body and the next variables here, although these nodes are shared between for and for each
' or we loose diagnostics.
Return hasErrors
End Function
''' <summary>
''' Binds loop body and the next variables for ForTo and ForEach loops.
''' </summary>
''' <remarks>
''' The binding of the loop body and the next variables cannot happen before the local type inference has
''' completed, which happens in the specialized binding functions for foreach and for loops. Otherwise we would
''' loose the diagnostics from the type inference.
''' </remarks>
''' <param name="loopBody">The loop body.</param>
''' <param name="nextVariables">The next variables.</param>
Private Sub BindForLoopBodyAndNextControlVariables(
node As ForOrForEachBlockSyntax,
<Out()> ByRef nextVariables As ImmutableArray(Of BoundExpression),
<Out()> ByRef loopBody As BoundBlock,
diagnostics As BindingDiagnosticBag)
' Bind the body of the loop.
loopBody = BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
' bind the variables of the next statement.
' if there is no next statement in this block, the array will be nothing,
' if there is a next statement without variables, the array will be empty, otherwise
' it contains all bound expression in declaration order.
nextVariables = Nothing
Dim endOptSyntax = node.NextStatement
If endOptSyntax IsNot Nothing Then
Dim controlVariableList As SeparatedSyntaxList(Of ExpressionSyntax) = endOptSyntax.ControlVariables
If controlVariableList.IsEmpty Then
' should be the most common case: a next without a variable
nextVariables = ImmutableArray(Of BoundExpression).Empty
ElseIf controlVariableList.Count = 1 Then
' avoid creating an ArrayBuilder for the second most common case, where the control variable
' of the current loop is used.
Dim boundVariable = BindExpression(controlVariableList(0), diagnostics)
boundVariable = ReclassifyAsValue(boundVariable, diagnostics)
nextVariables = ImmutableArray.Create(boundVariable)
Else
Dim nextVariableCount = controlVariableList.Count
Dim nextVariableBuilder As ArrayBuilder(Of BoundExpression) = ArrayBuilder(Of BoundExpression).GetInstance
Dim currentBinder = Me
For nextVariableIndex = 0 To nextVariableCount - 1
Dim boundVariable = currentBinder.BindExpression(controlVariableList(nextVariableIndex), diagnostics)
boundVariable = ReclassifyAsValue(boundVariable, diagnostics)
nextVariableBuilder.Add(boundVariable)
' due to incremental binding, there might be other binders in between Me and the containing
' StatementListBinder.
' each next variable will be bound with the responsible binder for this for loop
Do
currentBinder = currentBinder.ContainingBinder
Loop While currentBinder IsNot Nothing AndAlso Not (TypeOf (currentBinder) Is StatementListBinder)
If currentBinder IsNot Nothing Then
currentBinder = currentBinder.ContainingBinder
End If
If Not TypeOf currentBinder Is ForOrForEachBlockBinder Then
' this happens for broken code, e.g.
' for each a in arr1
' if goo() then
' for each b in arr2
' next b, a
' end if
Exit For
End If
Next
nextVariables = nextVariableBuilder.ToImmutableAndFree
End If
End If
End Sub
Private Function BindForToBlockParts(
node As ForOrForEachBlockSyntax,
declaredOrInferredLocalOpt As LocalSymbol,
controlVariableOpt As BoundExpression,
isInferredLocal As Boolean,
hasErrors As Boolean,
diagnostics As BindingDiagnosticBag
) As BoundForStatement
Dim forStatement = DirectCast(node.ForOrForEachStatement, ForStatementSyntax)
Dim initialValue As BoundExpression = Nothing
Dim limit As BoundExpression = Nothing
Dim stepValue As BoundExpression = Nothing
If isInferredLocal Then
Debug.Assert(declaredOrInferredLocalOpt IsNot Nothing)
Debug.Assert(controlVariableOpt Is Nothing)
Dim type = InferForFromToVariableType(declaredOrInferredLocalOpt,
forStatement.FromValue,
forStatement.ToValue,
forStatement.StepClause,
initialValue,
limit,
stepValue,
diagnostics)
' Now that we know the type go ahead and set it.
Dim identifier = declaredOrInferredLocalOpt.IdentifierToken
VerifyLocalSymbolNameAndSetType(declaredOrInferredLocalOpt, type, DirectCast(identifier.Parent, VisualBasicSyntaxNode), identifier, diagnostics)
controlVariableOpt = New BoundLocal(forStatement.ControlVariable, declaredOrInferredLocalOpt, type)
End If
Dim targetType = controlVariableOpt.Type
Dim targetTypeIsValid As Boolean = False
If targetType IsNot Nothing AndAlso Not targetType.IsErrorType Then
targetTypeIsValid = IsValidForControlVariableType(node, targetType.GetEnumUnderlyingTypeOrSelf(), diagnostics)
hasErrors = (Not targetTypeIsValid) Or hasErrors
End If
' Bind initial value
If initialValue Is Nothing Then
initialValue = BindValue(forStatement.FromValue, diagnostics)
End If
' Bind limit
If limit Is Nothing Then
limit = BindValue(forStatement.ToValue, diagnostics)
End If
' Bind step
If stepValue Is Nothing Then
Dim stepClause = forStatement.StepClause
If stepClause IsNot Nothing Then
stepValue = BindValue(stepClause.StepValue, diagnostics)
Else
' Spec: "If the step value is omitted, it is implicitly the literal 1, converted to the type of the loop control variable. "
'If there is an error, a special handling required to explain where 1 came from.
stepValue = New BoundLiteral(node,
ConstantValue.Create(1),
Me.GetSpecialType(SpecialType.System_Int32, forStatement, diagnostics))
stepValue.SetWasCompilerGenerated()
End If
End If
If targetTypeIsValid Then
initialValue = ApplyImplicitConversion(initialValue.Syntax, targetType, initialValue, diagnostics)
limit = ApplyImplicitConversion(limit.Syntax, targetType, limit, diagnostics)
Dim stepValueBeforeConversion = stepValue
stepValue = ApplyConversion(stepValue.Syntax, targetType, stepValue,
isExplicit:=forStatement.StepClause Is Nothing,
diagnostics:=diagnostics)
If stepValue IsNot stepValueBeforeConversion AndAlso stepValue.Kind = BoundKind.Conversion AndAlso
forStatement.StepClause Is Nothing Then
stepValue.MakeCompilerGenerated()
End If
Else
initialValue = MakeRValueAndIgnoreDiagnostics(initialValue)
limit = MakeRValueAndIgnoreDiagnostics(limit)
stepValue = MakeRValueAndIgnoreDiagnostics(stepValue)
End If
' get special types that are used by the rewriters later on to report use site errors
' TODO: update the types to the ones that get really used by the rewriter. In case of using an index of an user
' defined type there is no need to get the integer type.
Dim integerType = GetSpecialType(SpecialType.System_Int32, node, diagnostics)
Dim booleanType = GetSpecialType(SpecialType.System_Boolean, node, diagnostics)
Dim udfOperators As BoundForToUserDefinedOperators = Nothing
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
' normalize initValue, limit and step to the common type
If Not hasErrors Then
If Not (initialValue.HasErrors OrElse limit.HasErrors OrElse stepValue.HasErrors) AndAlso
targetType.CanContainUserDefinedOperators(useSiteInfo) Then
' Bind user-defined operators that we need.
Dim syntax As VisualBasicSyntaxNode = node.ForOrForEachStatement
Dim leftOperandPlaceholder = New BoundRValuePlaceholder(syntax, targetType).MakeCompilerGenerated()
Dim rightOperandPlaceholder = New BoundRValuePlaceholder(syntax, targetType).MakeCompilerGenerated()
Dim addition As BoundUserDefinedBinaryOperator = BindForLoopUserDefinedOperator(syntax, BinaryOperatorKind.Add, leftOperandPlaceholder, rightOperandPlaceholder, diagnostics)
Dim subtraction As BoundUserDefinedBinaryOperator = BindForLoopUserDefinedOperator(syntax, BinaryOperatorKind.Subtract, leftOperandPlaceholder, rightOperandPlaceholder, diagnostics)
Dim lessThanOrEqual As BoundExpression = BindForLoopUserDefinedOperator(syntax, BinaryOperatorKind.LessThanOrEqual, leftOperandPlaceholder, rightOperandPlaceholder, diagnostics)
If lessThanOrEqual IsNot Nothing Then
lessThanOrEqual = ApplyImplicitConversion(syntax, booleanType, lessThanOrEqual, diagnostics, isOperandOfConditionalBranch:=True).MakeCompilerGenerated()
End If
Dim greaterThanOrEqual As BoundExpression = BindForLoopUserDefinedOperator(syntax, BinaryOperatorKind.GreaterThanOrEqual, leftOperandPlaceholder, rightOperandPlaceholder, diagnostics)
If greaterThanOrEqual IsNot Nothing Then
' Suppress errors if we already reported them for LessThanOrEqual.
greaterThanOrEqual = ApplyImplicitConversion(syntax, booleanType, greaterThanOrEqual,
If(lessThanOrEqual IsNot Nothing AndAlso lessThanOrEqual.HasErrors, BindingDiagnosticBag.Discarded, diagnostics),
isOperandOfConditionalBranch:=True).MakeCompilerGenerated()
End If
If addition IsNot Nothing AndAlso subtraction IsNot Nothing AndAlso lessThanOrEqual IsNot Nothing AndAlso greaterThanOrEqual IsNot Nothing Then
udfOperators = New BoundForToUserDefinedOperators(syntax,
leftOperandPlaceholder, rightOperandPlaceholder,
addition, subtraction,
lessThanOrEqual, greaterThanOrEqual)
Else
hasErrors = True
End If
End If
End If
diagnostics.Add(node, useSiteInfo)
hasErrors = hasErrors OrElse
targetType.IsErrorType OrElse
initialValue.HasErrors OrElse
stepValue.HasErrors OrElse
controlVariableOpt.HasErrors
' Bind the loop body and the next variables
Dim loopBody As BoundBlock = Nothing
Dim nextVariables As ImmutableArray(Of BoundExpression) = Nothing
Me.BindForLoopBodyAndNextControlVariables(node, nextVariables, loopBody, diagnostics)
' Create the bound node.
Return New BoundForToStatement(
node,
initialValue,
limit,
stepValue,
CheckOverflow,
udfOperators,
declaredOrInferredLocalOpt,
controlVariableOpt,
loopBody,
nextVariables,
continueLabel:=GetContinueLabel(SyntaxKind.ContinueForStatement),
exitLabel:=GetExitLabel(SyntaxKind.ExitForStatement),
hasErrors:=hasErrors)
End Function
''' <summary>
''' Can return Nothing in case of failure.
''' </summary>
Private Function BindForLoopUserDefinedOperator(
syntax As VisualBasicSyntaxNode,
opCode As BinaryOperatorKind,
left As BoundExpression,
right As BoundExpression,
diagnostics As BindingDiagnosticBag
) As BoundUserDefinedBinaryOperator
Debug.Assert(opCode = BinaryOperatorKind.Add OrElse opCode = BinaryOperatorKind.Subtract OrElse opCode = BinaryOperatorKind.LessThanOrEqual OrElse opCode = BinaryOperatorKind.GreaterThanOrEqual)
Dim isRelational As Boolean = (opCode = BinaryOperatorKind.LessThanOrEqual OrElse opCode = BinaryOperatorKind.GreaterThanOrEqual)
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim userDefinedOperator As OverloadResolution.OverloadResolutionResult = OverloadResolution.ResolveUserDefinedBinaryOperator(left, right, opCode, Me, includeEliminatedCandidates:=False,
useSiteInfo:=useSiteInfo)
If diagnostics.Add(syntax, useSiteInfo) Then
' Suppress additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
End If
If userDefinedOperator.ResolutionIsLateBound OrElse Not userDefinedOperator.BestResult.HasValue Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_ForLoopOperatorRequired2, left.Type, SyntaxFacts.GetText(OverloadResolution.GetOperatorTokenKind(opCode)))
Return Nothing
End If
Dim bestCandidate As OverloadResolution.Candidate = userDefinedOperator.BestResult.Value.Candidate
If Not bestCandidate.Parameters(0).Type.IsSameTypeIgnoringAll(left.Type) OrElse
Not bestCandidate.Parameters(1).Type.IsSameTypeIgnoringAll(left.Type) OrElse
(Not isRelational AndAlso Not bestCandidate.ReturnType.IsSameTypeIgnoringAll(left.Type)) Then
If isRelational Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_UnacceptableForLoopRelOperator2, bestCandidate.UnderlyingSymbol,
If(bestCandidate.IsLifted,
left.Type.GetNullableUnderlyingTypeOrSelf(),
left.Type))
Else
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_UnacceptableForLoopOperator2, bestCandidate.UnderlyingSymbol,
If(bestCandidate.IsLifted,
left.Type.GetNullableUnderlyingTypeOrSelf(),
left.Type))
End If
Return Nothing
End If
Dim result As BoundUserDefinedBinaryOperator = BindUserDefinedNonShortCircuitingBinaryOperator(syntax, opCode, left, right, userDefinedOperator, diagnostics).MakeCompilerGenerated()
result.UnderlyingExpression.MakeCompilerGenerated()
Return result
End Function
' Verify that control variable can actually be used as a control variable
Private Function IsValidForControlVariableType(node As ForOrForEachBlockSyntax,
targetType As TypeSymbol,
diagnostics As BindingDiagnosticBag) As Boolean
' if it's a nullable type, simply unwrap it (no recursion needed because nullables cannot be nested)
If targetType.IsNullableType Then
targetType = targetType.GetNullableUnderlyingType.GetEnumUnderlyingTypeOrSelf
End If
If targetType.IsNumericType Then
Return True
End If
If targetType.IsObjectType Then
Return True
End If
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If targetType.IsIntrinsicOrEnumType OrElse Not targetType.CanContainUserDefinedOperators(useSiteInfo) Then
diagnostics.Add(DirectCast(node.ForOrForEachStatement, ForStatementSyntax).ControlVariable, useSiteInfo)
ReportDiagnostic(diagnostics, DirectCast(node.ForOrForEachStatement, ForStatementSyntax).ControlVariable, ERRID.ERR_ForLoopType1, targetType)
Return False
End If
diagnostics.Add(DirectCast(node.ForOrForEachStatement, ForStatementSyntax).ControlVariable, useSiteInfo)
Return True
End Function
Private Function BindForEachBlockParts(
node As ForOrForEachBlockSyntax,
declaredOrInferredLocalOpt As LocalSymbol,
controlVariableOpt As BoundExpression,
isInferredLocal As Boolean,
diagnostics As BindingDiagnosticBag
) As BoundForEachStatement
Dim forEachStatement = DirectCast(node.ForOrForEachStatement, ForEachStatementSyntax)
Dim currentType As TypeSymbol = Nothing
Dim elementType As TypeSymbol = Nothing
Dim isEnumerable As Boolean = False
Dim needToDispose As Boolean = False
Dim isOrInheritsFromOrImplementsIDisposable As Boolean = False
Dim boundGetEnumeratorCall As BoundExpression = Nothing
Dim boundEnumeratorPlaceholder As BoundLValuePlaceholder = Nothing
Dim boundMoveNextCall As BoundExpression = Nothing
Dim boundCurrentAccess As BoundExpression = Nothing
Dim collectionPlaceholder As BoundRValuePlaceholder = Nothing
Dim boundDisposeCondition As BoundExpression = Nothing
Dim boundDisposeCast As BoundExpression = Nothing
Dim boundCurrentConversion As BoundExpression = Nothing
Dim boundCurrentPlaceholder As BoundRValuePlaceholder = Nothing
Dim collection As BoundExpression = Nothing
If isInferredLocal Then
Debug.Assert(declaredOrInferredLocalOpt IsNot Nothing)
Debug.Assert(controlVariableOpt Is Nothing)
Dim type = InferForEachVariableType(declaredOrInferredLocalOpt,
forEachStatement.Expression,
collection,
currentType,
elementType,
isEnumerable,
boundGetEnumeratorCall,
boundEnumeratorPlaceholder,
boundMoveNextCall,
boundCurrentAccess,
collectionPlaceholder,
needToDispose,
isOrInheritsFromOrImplementsIDisposable,
diagnostics)
' Now that we know the type go ahead and set it.
Dim identifier = declaredOrInferredLocalOpt.IdentifierToken
VerifyLocalSymbolNameAndSetType(declaredOrInferredLocalOpt, type, DirectCast(identifier.Parent, VisualBasicSyntaxNode), identifier, diagnostics)
controlVariableOpt = New BoundLocal(forEachStatement.ControlVariable, declaredOrInferredLocalOpt, type)
End If
If collection Is Nothing Then
' bind the expression that describes the collection to iterate over
collection = BindValue(forEachStatement.Expression, diagnostics)
If Not collection.IsLValue AndAlso Not collection.IsNothingLiteral Then
collection = MakeRValue(collection, diagnostics)
End If
' check if the collection is valid for a for each statement
collection = InterpretForEachStatementCollection(collection,
currentType,
elementType,
isEnumerable,
boundGetEnumeratorCall,
boundEnumeratorPlaceholder,
boundMoveNextCall,
boundCurrentAccess,
collectionPlaceholder,
needToDispose,
isOrInheritsFromOrImplementsIDisposable,
diagnostics)
End If
'
' Now we're already creating some bound nodes that will be used in the local rewriter to report possible
' diagnostics early and to reuse the code that exists in the binder.
'
Dim collectionSyntax = collection.Syntax
' Note: the conversion is from the array's element type (if rank = 1), char, or the return type of
' the current's get method
If currentType IsNot Nothing AndAlso Not controlVariableOpt.HasErrors Then
Dim controlVariableType = controlVariableOpt.Type
If Not (controlVariableType.IsErrorType OrElse currentType.IsErrorType OrElse elementType.IsErrorType) Then
Dim boundElement As BoundExpression
' "Current" is converted to the type of the control variable as if
' it were an explicit cast. This language rule exists because there is
' no way to write a cast, and the type of the IEnumerator.Current is Object,
' which would make
'
' Dim I, A() As Integer : For Each I in A : Next
'
' invalid in strict mode.
If Conversions.IsIdentityConversion(Conversions.ClassifyConversion(elementType, currentType, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded).Key) Then
boundCurrentPlaceholder = New BoundRValuePlaceholder(collectionSyntax, elementType)
boundElement = boundCurrentPlaceholder
Else
boundCurrentPlaceholder = New BoundRValuePlaceholder(collectionSyntax, currentType)
boundElement = ApplyConversion(collectionSyntax,
elementType,
boundCurrentPlaceholder,
isExplicit:=True,
diagnostics:=diagnostics)
boundElement.SetWasCompilerGenerated()
End If
If boundElement Is boundCurrentPlaceholder OrElse
Not Conversions.IsIdentityConversion(Conversions.ClassifyConversion(controlVariableType, elementType, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded).Key) Then
boundCurrentConversion = ApplyConversion(collectionSyntax,
controlVariableType,
boundElement,
isExplicit:=True,
diagnostics:=diagnostics)
boundCurrentConversion.SetWasCompilerGenerated()
Else
boundCurrentConversion = boundElement
End If
End If
End If
' in case the enumerator needs to be possibly disposed, create the bound nodes for the condition that checks
' if Dispose() needs to be called. This bound expression will contain a placeholder for the enumerator that gets
' replaced in the local rewriting.
If needToDispose Then
' no need to report use site errors here, this was already done in InterpretForEachStatementCollection
Dim idisposableType = Compilation.GetSpecialType(SpecialType.System_IDisposable)
' needToDispose can only be true if boundGetEnumeratorCall had no errors (see InterpretForEachStatementCollection)
Dim enumeratorType = boundGetEnumeratorCall.Type
' IDisposable is implemented, which means there must be a check if the enumerator is not nothing
' will be used in code: "If e IsNot Nothing Then"
If isOrInheritsFromOrImplementsIDisposable Then
If Not (enumeratorType.IsValueType) Then
boundDisposeCondition = BindIsExpression(boundEnumeratorPlaceholder,
New BoundLiteral(collectionSyntax, ConstantValue.Nothing, Nothing),
collectionSyntax, True, diagnostics)
boundDisposeCast = ApplyConversion(collectionSyntax, idisposableType, boundEnumeratorPlaceholder, isExplicit:=True, diagnostics:=diagnostics)
End If
Else
Debug.Assert(enumeratorType.SpecialType = SpecialType.System_Collections_IEnumerator)
' Instead of if TypeOf(e) is IDisposable, we'll do a TryCast(e, IDisposable) and call Dispose if the result
' was not Nothing.
' create TryCast
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim conversionKind As ConversionKind = Conversions.ClassifyTryCastConversion(enumeratorType, idisposableType, useSiteInfo)
If diagnostics.Add(collectionSyntax, useSiteInfo) Then
' Suppress additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
End If
boundDisposeCast = New BoundTryCast(collectionSyntax, boundEnumeratorPlaceholder.MakeRValue(), conversionKind, idisposableType, Nothing)
boundDisposeCondition = BindIsExpression(boundDisposeCast,
New BoundLiteral(collectionSyntax, ConstantValue.Nothing, Nothing),
collectionSyntax, True, diagnostics)
End If
End If
' Bind the loop body and the next variables
Dim loopBody As BoundBlock = Nothing
Dim nextVariables As ImmutableArray(Of BoundExpression) = Nothing
Me.BindForLoopBodyAndNextControlVariables(node, nextVariables, loopBody, diagnostics)
Dim enumeratorInfo = New ForEachEnumeratorInfo(boundGetEnumeratorCall,
boundMoveNextCall,
boundCurrentAccess,
elementType,
needToDispose,
isOrInheritsFromOrImplementsIDisposable,
boundDisposeCondition,
boundDisposeCast,
boundCurrentConversion,
boundEnumeratorPlaceholder,
boundCurrentPlaceholder,
collectionPlaceholder)
Return New BoundForEachStatement(node,
collection,
enumeratorInfo,
declaredOrInferredLocalOpt, controlVariableOpt, loopBody, nextVariables,
continueLabel:=GetContinueLabel(SyntaxKind.ContinueForStatement),
exitLabel:=GetExitLabel(SyntaxKind.ExitForStatement))
End Function
''' <summary>
''' Verifies for control variable declaration and outputs diagnostics as needed.
''' </summary>
''' <param name="variableDeclarator">The variable declarator.</param>
''' <param name="diagnostics">The diagnostics.</param><returns></returns>
Private Shared Function VerifyForControlVariableDeclaration(variableDeclarator As VariableDeclaratorSyntax, diagnostics As BindingDiagnosticBag) As Boolean
' Check variable declaration syntax if present
Debug.Assert(variableDeclarator.Names.Count = 1, "should be exactly one control variable")
Dim identifier = variableDeclarator.Names(0)
' nullable type inference is not supported
If variableDeclarator.AsClause Is Nothing AndAlso
identifier.Nullable.Node IsNot Nothing Then
ReportDiagnostic(diagnostics, identifier, ERRID.ERR_NullableTypeInferenceNotSupported)
Return False
End If
' specifying array bounds is not valid for control variable declarations
If identifier.ArrayBounds IsNot Nothing Then
ReportDiagnostic(diagnostics, identifier, ERRID.ERR_ForCtlVarArraySizesSpecified)
Return False
End If
Return True
End Function
''' <summary>
''' This function tries to bind the given controlVariableSyntax.
''' If it was an identifier of a valid target, the bound node is written to controlVariable and true is returned.
''' If something else was bound, that is not legal as a control variable (e.g. a property), a BoundBadNode is written
''' to controlVariable and false is returned.
''' If nothing declared was found, false is returned and controlVariable is set to nothing. In this case it's safe to
''' create a new local for the loop node.
''' </summary>
Private Function TryBindLoopControlVariable(
controlVariableSyntax As VisualBasicSyntaxNode,
<Out()> ByRef controlVariable As BoundExpression,
diagnostics As BindingDiagnosticBag
) As Boolean
Debug.Assert(controlVariableSyntax.Kind <> SyntaxKind.VariableDeclarator)
controlVariable = BindExpression(DirectCast(controlVariableSyntax, ExpressionSyntax), diagnostics)
controlVariable = ReclassifyAsValue(controlVariable, diagnostics)
If controlVariable.HasErrors Then
' VB Spec 10.9.3 2.2: 2.2. Otherwise, if it is classified as anything other than a type
' or a variable, it is a compile-error.
controlVariable = BadExpression(controlVariable)
Return False
End If
' VB Spec 10.9.3 2.1: If the result of this resolution is classified as a variable, then
' the loop control variable is that variable.
If Not VerifyForLoopControlReference(controlVariable, diagnostics) Then
controlVariable = New BoundBadExpression(controlVariableSyntax,
LookupResultKind.NotAVariable,
ImmutableArray(Of Symbol).Empty,
ImmutableArray.Create(controlVariable),
controlVariable.Type,
hasErrors:=True)
Return False
End If
Return True
End Function
''' <summary>
''' If the control variable was bound to a non bad expression, this function checks if the
''' bound expression is a variable and reports diagnostics appropriately.
''' It reports the errors from 10.9.3 2.2
''' </summary>
''' <param name="controlVariable">The control variable.</param>
''' <param name="diagnostics">The diagnostics.</param><returns></returns>
Private Function VerifyForLoopControlReference(controlVariable As BoundExpression, diagnostics As BindingDiagnosticBag) As Boolean
Dim isLValue As Boolean
' A property reference is not allowed as the control variable of any
' kind of For statement.
If controlVariable.IsPropertyOrXmlPropertyAccess() Then
ReportDiagnostic(diagnostics, controlVariable.Syntax, ERRID.ERR_LoopControlMustNotBeProperty)
Return False
Else
isLValue = controlVariable.IsLValue()
End If
If Not isLValue Then
If Not controlVariable.HasErrors Then
ReportAssignmentToRValue(controlVariable, diagnostics)
End If
Return False
End If
If Not controlVariable.HasErrors AndAlso IsInAsyncContext() AndAlso
SeenAwaitVisitor.SeenAwaitIn(controlVariable, diagnostics) Then
ReportDiagnostic(diagnostics, controlVariable.Syntax, ERRID.ERR_LoopControlMustNotAwait)
Return False
End If
Return True
End Function
Private Class SeenAwaitVisitor
Inherits BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
Private _seenAwait As Boolean
Private Sub New()
End Sub
Public Shared Function SeenAwaitIn(node As BoundNode, diagnostics As BindingDiagnosticBag) As Boolean
Dim visitor = New SeenAwaitVisitor()
Try
visitor.Visit(node)
Catch ex As CancelledByStackGuardException
ex.AddAnError(diagnostics)
End Try
Return visitor._seenAwait
End Function
Public Overrides Function Visit(node As BoundNode) As BoundNode
If _seenAwait Then
Return Nothing
End If
Return MyBase.Visit(node)
End Function
Public Overrides Function VisitLambda(node As BoundLambda) As BoundNode
' Do not dive into lambdas.
Return Nothing
End Function
Public Overrides Function VisitAwaitOperator(node As BoundAwaitOperator) As BoundNode
_seenAwait = True
Return Nothing
End Function
End Class
''' <summary>
''' Verifies that the collection is either a string, and array or matches the design pattern criteria and reports
''' diagnostics appropriately.
''' </summary>
''' <param name="collection">The collection of the for each statement.</param>
''' <param name="currentType">If the collection meets all criteria, currentType contains the type of the element from
''' the collection that get's returned by the current property.</param>
''' <param name="elementType">Element type of the collection, could be different from <paramref name="currentType"/>.
''' For example, based on the pattern <paramref name="currentType"/> for an array is Object, but the <paramref name="elementType"/>
''' is the element type of the array.</param>
''' <param name="isEnumerable">if set to <c>true</c>, the collection is enumerable (matches design pattern, IEnumerable
''' or IEnumerable(Of T); otherwise (string or arrays) it's set to false.</param>
''' <param name="diagnostics">The diagnostics.</param>
''' <returns>The collection which might have been converted to IEnumerable or IEnumerable(Of T) if needed.</returns>
Private Function InterpretForEachStatementCollection(
collection As BoundExpression,
<Out()> ByRef currentType As TypeSymbol,
<Out()> ByRef elementType As TypeSymbol,
<Out()> ByRef isEnumerable As Boolean,
<Out()> ByRef boundGetEnumeratorCall As BoundExpression,
<Out()> ByRef boundEnumeratorPlaceholder As BoundLValuePlaceholder,
<Out()> ByRef boundMoveNextCall As BoundExpression,
<Out()> ByRef boundCurrentAccess As BoundExpression,
<Out()> ByRef collectionPlaceholder As BoundRValuePlaceholder,
<Out()> ByRef needToDispose As Boolean,
<Out()> ByRef isOrInheritsFromOrImplementsIDisposable As Boolean,
diagnostics As BindingDiagnosticBag
) As BoundExpression
currentType = Nothing
elementType = Nothing
isEnumerable = False
needToDispose = False
isOrInheritsFromOrImplementsIDisposable = False
boundGetEnumeratorCall = Nothing
boundEnumeratorPlaceholder = Nothing
boundMoveNextCall = Nothing
boundCurrentAccess = Nothing
collectionPlaceholder = Nothing
If collection.HasErrors Then
Return collection
End If
Dim collectionType As TypeSymbol = collection.Type
Dim collectionSyntax = collection.Syntax
Dim targetCollectionType As NamedTypeSymbol = Nothing
' If the collection matches the design pattern, use that.
' Otherwise, if the collection implements IEnumerable, convert it
' to IEnumerable (which matches the design pattern).
'
' The design pattern is preferred to the interface implementation
' because it is more efficient.
Dim interfaceSpecialType As SpecialType = SpecialType.None
Dim detailedDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=diagnostics.AccumulatesDependencies)
If MatchesForEachCollectionDesignPattern(collectionType, collection,
currentType,
boundGetEnumeratorCall,
boundEnumeratorPlaceholder,
boundMoveNextCall,
boundCurrentAccess,
collectionPlaceholder,
detailedDiagnostics) Then
diagnostics.AddRange(detailedDiagnostics)
elementType = currentType
' TODO(rbeckers) check if the long note about spurious errors in Dev10 (statement_semantics.cpp, line 5250) needs
' to be copied.
' We only pass in a temporary diagnostic bag into this method. The method itself only adds to it in case of
' ambiguous lookups or a failed overload resolution for the current property access.
isEnumerable = True
Else
' using a temporary diagnostic bag to only report use site errors for IEnumerable or IEnumerable(Of T) if they are used.
Dim ienumerableUseSiteDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics)
Dim genericIEnumerable = GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T, collectionSyntax, ienumerableUseSiteDiagnostics)
Dim matchingInterfaces As New HashSet(Of NamedTypeSymbol)(EqualsIgnoringComparer.InstanceIgnoringTupleNames)
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If Not collection.IsNothingLiteral AndAlso
Not collectionType.IsArrayType AndAlso
IsOrInheritsFromOrImplementsInterface(collectionType, genericIEnumerable, useSiteInfo, matchingInterfaces) Then
diagnostics.Add(collectionSyntax, useSiteInfo)
Debug.Assert(matchingInterfaces.Count > 0)
isEnumerable = True
targetCollectionType = matchingInterfaces(0)
' merge diagnostics for IEnumerable(Of T)
diagnostics.AddRange(ienumerableUseSiteDiagnostics)
ienumerableUseSiteDiagnostics.Free()
If matchingInterfaces.Count > 1 Then
' matchingInterfaces is a hash set, so it's enough to check if the count is more than one.
' Duplicates found while analyzing type parameter constraints do not occur this way like in Dev10.
ReportDiagnostic(diagnostics,
collectionSyntax,
ErrorFactory.ErrorInfo(ERRID.ERR_ForEachAmbiguousIEnumerable1,
collectionType))
detailedDiagnostics.Free()
Return New BoundBadExpression(collectionSyntax,
LookupResultKind.Empty,
ImmutableArray(Of Symbol).Empty,
ImmutableArray.Create(collection),
collectionType,
hasErrors:=True)
End If
interfaceSpecialType = SpecialType.System_Collections_Generic_IEnumerable_T
Else
ienumerableUseSiteDiagnostics.Clear()
Dim ienumerable = GetSpecialType(SpecialType.System_Collections_IEnumerable, collectionSyntax, ienumerableUseSiteDiagnostics)
If ((collection.IsNothingLiteral OrElse collectionType.IsObjectType) AndAlso Me.OptionStrict <> OptionStrict.On) OrElse
(Not collection.IsNothingLiteral AndAlso Not collectionType.IsArrayType AndAlso IsOrInheritsFromOrImplementsInterface(collectionType, ienumerable, useSiteInfo, matchingInterfaces)) Then
Debug.Assert(collection.IsNothingLiteral OrElse collectionType.IsObjectType OrElse (TypeSymbol.Equals(matchingInterfaces.First, ienumerable, TypeCompareKind.ConsiderEverything) AndAlso matchingInterfaces.Count = 1))
diagnostics.Add(collectionSyntax, useSiteInfo)
isEnumerable = True
targetCollectionType = ienumerable
interfaceSpecialType = SpecialType.System_Collections_IEnumerable
' merge diagnostics for IEnumerable
diagnostics.AddRange(ienumerableUseSiteDiagnostics)
ienumerableUseSiteDiagnostics.Free()
Else
Debug.Assert(collectionType IsNot Nothing OrElse collection.IsNothingLiteral AndAlso Me.OptionStrict = OptionStrict.On)
diagnostics.Add(collectionSyntax, useSiteInfo)
If collection.IsNothingLiteral Then
' in case of option strict on we need to reclassify the nothing literal
collection = MakeRValue(collection, diagnostics)
collectionType = collection.Type
End If
' Show detailed errors for ambiguous lookups or failed overload resolution in case they are available,
' otherwise report the default error "xyz does not match design pattern".
If detailedDiagnostics.HasAnyErrors Then
diagnostics.AddRange(detailedDiagnostics)
Else
ReportDiagnostic(diagnostics,
collectionSyntax,
ErrorFactory.ErrorInfo(ERRID.ERR_ForEachCollectionDesignPattern1,
collectionType))
End If
detailedDiagnostics.Free()
ienumerableUseSiteDiagnostics.Free()
Return New BoundBadExpression(collectionSyntax,
LookupResultKind.Empty,
ImmutableArray(Of Symbol).Empty,
ImmutableArray.Create(collection),
collectionType,
hasErrors:=True)
End If
End If
End If
detailedDiagnostics.Free()
' in case one of the IEnumerable interfaces are used, we'll need to cast to it.
' this needs to happen before creating the bound calls, to force a boxing of collections that are value types.
If targetCollectionType IsNot Nothing Then
collection = ApplyImplicitConversion(collectionSyntax, targetCollectionType, collection, diagnostics)
End If
If isEnumerable Then
If interfaceSpecialType <> SpecialType.None Then
Dim member As Symbol
Dim specialTypeMember As Symbol
'
' GetEnumerator
'
If interfaceSpecialType = SpecialType.System_Collections_Generic_IEnumerable_T Then
specialTypeMember = GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator,
collectionSyntax,
diagnostics)
If specialTypeMember IsNot Nothing AndAlso specialTypeMember.GetUseSiteInfo().DiagnosticInfo Is Nothing AndAlso Not targetCollectionType.IsErrorType Then
member = DirectCast(targetCollectionType, SubstitutedNamedType).GetMemberForDefinition(specialTypeMember)
Else
member = Nothing
End If
Else
member = GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator,
collectionSyntax,
diagnostics)
End If
Dim memberUseSiteInfo As UseSiteInfo(Of AssemblySymbol) = If(member?.GetUseSiteInfo(), New UseSiteInfo(Of AssemblySymbol)())
If member IsNot Nothing AndAlso memberUseSiteInfo.DiagnosticInfo Is Nothing Then
diagnostics.AddDependencies(memberUseSiteInfo)
collectionPlaceholder = New BoundRValuePlaceholder(collectionSyntax,
If(collectionType IsNot Nothing AndAlso collectionType.IsStringType, collectionType, collection.Type))
Dim methodOrPropertyGroup As BoundMethodOrPropertyGroup
methodOrPropertyGroup = New BoundMethodGroup(collectionSyntax,
Nothing,
ImmutableArray.Create(DirectCast(member, MethodSymbol)),
LookupResultKind.Good,
collectionPlaceholder,
QualificationKind.QualifiedViaValue)
boundGetEnumeratorCall = CreateBoundInvocationExpressionFromMethodOrPropertyGroup(collectionSyntax,
methodOrPropertyGroup,
diagnostics)
Dim enumeratorType = boundGetEnumeratorCall.Type
boundEnumeratorPlaceholder = New BoundLValuePlaceholder(collectionSyntax,
enumeratorType)
'
' MoveNext
'
member = GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext,
collectionSyntax,
diagnostics)
memberUseSiteInfo = If(member?.GetUseSiteInfo(), New UseSiteInfo(Of AssemblySymbol)())
If member IsNot Nothing AndAlso memberUseSiteInfo.DiagnosticInfo Is Nothing Then
diagnostics.AddDependencies(memberUseSiteInfo)
methodOrPropertyGroup = New BoundMethodGroup(collectionSyntax,
Nothing,
ImmutableArray.Create(DirectCast(member, MethodSymbol)),
LookupResultKind.Good,
boundEnumeratorPlaceholder,
QualificationKind.QualifiedViaValue)
boundMoveNextCall = CreateBoundInvocationExpressionFromMethodOrPropertyGroup(collectionSyntax,
methodOrPropertyGroup,
diagnostics)
End If
'
' Current
'
If interfaceSpecialType = SpecialType.System_Collections_Generic_IEnumerable_T Then
specialTypeMember = GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerator_T__Current,
collectionSyntax,
diagnostics)
If specialTypeMember IsNot Nothing AndAlso specialTypeMember.GetUseSiteInfo().DiagnosticInfo Is Nothing AndAlso Not enumeratorType.IsErrorType Then
member = DirectCast(enumeratorType, SubstitutedNamedType).GetMemberForDefinition(specialTypeMember)
Else
member = Nothing
End If
Else
member = GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__Current,
collectionSyntax,
diagnostics)
End If
memberUseSiteInfo = If(member?.GetUseSiteInfo(), New UseSiteInfo(Of AssemblySymbol)())
If member IsNot Nothing AndAlso memberUseSiteInfo.DiagnosticInfo Is Nothing Then
diagnostics.AddDependencies(memberUseSiteInfo)
methodOrPropertyGroup = New BoundPropertyGroup(collectionSyntax,
ImmutableArray.Create(DirectCast(member, PropertySymbol)),
LookupResultKind.Good,
boundEnumeratorPlaceholder,
QualificationKind.QualifiedViaValue)
boundCurrentAccess = CreateBoundInvocationExpressionFromMethodOrPropertyGroup(collectionSyntax,
methodOrPropertyGroup,
diagnostics)
currentType = boundCurrentAccess.Type
elementType = currentType
End If
End If
End If
If collectionType IsNot Nothing Then
If collectionType.IsArrayType() Then
Dim arrayType = DirectCast(collectionType, ArrayTypeSymbol)
elementType = arrayType.ElementType
If arrayType.IsSZArray Then
currentType = elementType
End If
ElseIf collectionType.IsStringType() Then
elementType = GetSpecialType(SpecialType.System_Char, collectionSyntax, diagnostics)
currentType = elementType
End If
End If
' if it's enumerable, we'll need to check if the enumerator is disposable.
Dim idisposable = GetSpecialType(SpecialType.System_IDisposable, collectionSyntax, diagnostics)
If (idisposable IsNot Nothing AndAlso Not idisposable.IsErrorType) AndAlso
Not boundGetEnumeratorCall.HasErrors AndAlso Not boundGetEnumeratorCall.Type.IsErrorType Then
Dim getEnumeratorReturnType = boundGetEnumeratorCall.Type
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim conversionKind = Conversions.ClassifyDirectCastConversion(getEnumeratorReturnType, idisposable, useSiteInfo)
diagnostics.Add(collectionSyntax, useSiteInfo)
isOrInheritsFromOrImplementsIDisposable = Conversions.IsWideningConversion(conversionKind)
If isOrInheritsFromOrImplementsIDisposable OrElse
getEnumeratorReturnType.SpecialType = SpecialType.System_Collections_IEnumerator Then
' do not actually generate dispose calls in IL
' Dev10 does the same thing (StatementSemantics.cpp, Line 5678++)
' this is true even for multidimensional arrays that are handled through the design pattern.
Debug.Assert(collectionType IsNot Nothing OrElse OptionStrict <> OptionStrict.On AndAlso collection.Kind = BoundKind.Conversion AndAlso DirectCast(collection, BoundConversion).Operand.IsNothingLiteral)
If collectionType Is Nothing OrElse Not collectionType.IsArrayType Then
needToDispose = True
End If
End If
End If
End If
Return collection
End Function
''' <summary>
''' Checks if the type of the collection matches the for each collection design pattern.
''' </summary>
''' <remarks>
''' The rules are that the collection type must have an accessible GetEnumerator method that takes no parameters and
''' returns a type that has both:
''' - an accessible MoveNext method that takes no parameters and returns a Boolean
''' - an accessible Current property that takes no parameters and is not WriteOnly
'''
''' NOTE: this function ONLY checks for a function named "GetEnumerator" with the appropriate properties.
''' In the spec $10.9 it has these conditions: a type C is a "collection type" if one of
''' (1) it satisfies MatchesForEachCollectionDesignPattern (i.e. has a method named GetEnumerator() which
''' returns a type with MoveNext/Current); or
''' (2) it implements System.Collections.Generic.IEnumerable(Of T); or
''' (3) it implements System.Collections.IEnumerable.
'''
''' This function ONLY checks for part (1). Callers are expected to check for (2)/(3) themselves. The
''' scenario where something satisfies (2/3) but not (1) is
''' Class C
''' Implements IEnumerable
''' Function g1() as IEnumerator implements IEnumerable.GetEnumerator : End Function
'''
''' Clearly this class does not have a method _named_ GetEnumerator, but it does implement IEnumerable.
''' </remarks>
''' <param name="collectionType">The type of the for each collection.</param>
''' <param name="collection">The bound collection expression.</param>
''' <param name="currentType">Return type of the property named "Current" if found.</param>
''' <param name="boundGetEnumeratorCall">A bound call to GetEnumerator on the collection if found.</param>
''' <param name="boundEnumeratorPlaceholder">A bound placeholder value for the collection local if GetEnumerator
''' was bound successful</param>
''' <param name="boundMoveNextCall">A bound call to MoveNext on the instance returned by GetEnumerator if found.</param>
''' <param name="boundCurrentAccess">A bound property access for "Current" on the instance returned by GetEnumerator if found.</param>
''' <param name="collectionPlaceholder">A placeholder for the collection expression.</param>
''' <param name="temporaryDiagnostics">An empty diagnostic bag to capture diagnostics that have to be reported if the
''' collection matches the design pattern and that can be used instead of the generic error message in case non of the
''' for each collection criteria match.</param>
''' <returns>If all required methods have been successfully looked up and bound, true is being returned; otherwise false.
''' </returns>
Private Function MatchesForEachCollectionDesignPattern(
collectionType As TypeSymbol,
collection As BoundExpression,
<Out()> ByRef currentType As TypeSymbol,
<Out()> ByRef boundGetEnumeratorCall As BoundExpression,
<Out()> ByRef boundEnumeratorPlaceholder As BoundLValuePlaceholder,
<Out()> ByRef boundMoveNextCall As BoundExpression,
<Out()> ByRef boundCurrentAccess As BoundExpression,
<Out()> ByRef collectionPlaceholder As BoundRValuePlaceholder,
temporaryDiagnostics As BindingDiagnosticBag
) As Boolean
Debug.Assert(If(temporaryDiagnostics.DiagnosticBag?.IsEmptyWithoutResolution, True))
currentType = Nothing
boundGetEnumeratorCall = Nothing
boundEnumeratorPlaceholder = Nothing
boundMoveNextCall = Nothing
boundCurrentAccess = Nothing
' This method will add diagnostics (errors and warnings) to the given diagnostic bag. It might clean it while processing
' the collection type to reduce noise which is why only a new/empty diagnostic bag should be passed to this method.
'
' The diagnostics returned by this method should be added to the general diagnostic bag in case of a successful match
' of the collection design pattern to e.g. report warnings.
' If the collection does not match the collection design pattern and the collection is not and does not implement or
' inherits IEnumerable(Of T) or IEnumerable then the diagnostics can be used to give a more detailed reason instead
' of the generic "collection {0} does not match design pattern".
' To provide more detailed information that is close to the output of Dev10 we are collection diagnostics from
' ambiguous lookups and from binding the current property get access.
Dim collectionSyntax = collection.Syntax
If collection.IsNothingLiteral OrElse
(collectionType.Kind <> SymbolKind.ArrayType AndAlso
collectionType.Kind <> SymbolKind.NamedType AndAlso
collectionType.Kind <> SymbolKind.TypeParameter) Then
' Dev10 checked for !IsClassInterfaceRecordOrGenericParamType which is equivalent to a named type in Roslyn
Return False
End If
'
' GetEnumerator
'
' first, get GetEnumerator function that takes no arguments, also search in extension methods
Dim lookupResult As New LookupResult()
If Not GetMemberIfMatchesRequirements(WellKnownMemberNames.GetEnumeratorMethodName,
collectionType,
s_isFunctionWithoutArguments,
lookupResult,
collectionSyntax,
temporaryDiagnostics) Then
Return False
End If
Debug.Assert(lookupResult.IsGood)
collectionPlaceholder = New BoundRValuePlaceholder(collectionSyntax, collection.Type)
' bind the call to GetEnumerator (incl. overload resolution, handling of param arrays, optional parameters, ...)
Dim methodOrPropertyGroup As BoundMethodOrPropertyGroup = CreateBoundMethodGroup(collectionSyntax,
lookupResult,
LookupOptions.AllMethodsOfAnyArity,
temporaryDiagnostics.AccumulatesDependencies,
collectionPlaceholder,
Nothing,
QualificationKind.QualifiedViaValue)
boundGetEnumeratorCall = CreateBoundInvocationExpressionFromMethodOrPropertyGroup(collectionSyntax,
methodOrPropertyGroup,
temporaryDiagnostics)
If boundGetEnumeratorCall.HasErrors Then
temporaryDiagnostics.Clear()
Return False
End If
Dim enumeratorType As TypeSymbol = boundGetEnumeratorCall.Type
boundEnumeratorPlaceholder = New BoundLValuePlaceholder(collectionSyntax, enumeratorType)
'
' MoveNext
'
' try lookup an accessible MoveNext function in the return type of GetEnumerator that takes no parameters and
' returns a boolean.
If Not GetMemberIfMatchesRequirements(WellKnownMemberNames.MoveNextMethodName,
enumeratorType,
s_isFunctionWithoutArguments,
lookupResult,
collectionSyntax,
temporaryDiagnostics) Then
Return False
End If
Debug.Assert(lookupResult.IsGood)
' bind the call to MoveNext (incl. overload resolution, handling of param arrays, optional parameters, ...)
methodOrPropertyGroup = CreateBoundMethodGroup(collectionSyntax,
lookupResult,
LookupOptions.AllMethodsOfAnyArity,
temporaryDiagnostics.AccumulatesDependencies,
boundEnumeratorPlaceholder,
Nothing,
QualificationKind.QualifiedViaValue)
boundMoveNextCall = CreateBoundInvocationExpressionFromMethodOrPropertyGroup(collectionSyntax,
methodOrPropertyGroup,
temporaryDiagnostics)
If boundMoveNextCall.HasErrors OrElse
boundMoveNextCall.Kind <> BoundKind.Call OrElse
DirectCast(boundMoveNextCall, BoundCall).Method.OriginalDefinition.ReturnType.SpecialType <> SpecialType.System_Boolean Then
' Dev10 does not accept a MoveNext with a constructed return type, even if it is a boolean.
temporaryDiagnostics.Clear()
Return False
End If
'
' Current
'
' try lookup an accessible and readable property named Current that takes no parameters.
If Not GetMemberIfMatchesRequirements(WellKnownMemberNames.CurrentPropertyName,
enumeratorType,
s_isReadablePropertyWithoutArguments,
lookupResult,
collectionSyntax,
temporaryDiagnostics) Then
Return False
End If
Debug.Assert(lookupResult.IsGood)
' bind the call to Current (incl. overload resolution, handling of param arrays, optional parameters, ...)
methodOrPropertyGroup = New BoundPropertyGroup(collectionSyntax,
lookupResult.Symbols.ToDowncastedImmutable(Of PropertySymbol),
lookupResult.Kind,
boundEnumeratorPlaceholder,
QualificationKind.QualifiedViaValue)
boundCurrentAccess = CreateBoundInvocationExpressionFromMethodOrPropertyGroup(collectionSyntax,
methodOrPropertyGroup,
temporaryDiagnostics)
' the requirement is a "readable" property that takes no parameters, but the get property could be inaccessible
' and then binding a property access will fail.
If boundCurrentAccess.HasErrors Then
Return False
End If
currentType = boundCurrentAccess.Type
Return True
End Function
''' <summary>
''' Creates a BoundCall or BoundPropertyAccess from a MethodOrPropertyGroup.
''' </summary>
''' <remarks>
''' This is not a general purpose helper!
''' </remarks>
''' <param name="syntax">The syntax node.</param>
''' <param name="methodOrPropertyGroup">The method or property group.</param>
''' <param name="diagnostics">The diagnostics.</param>
Private Function CreateBoundInvocationExpressionFromMethodOrPropertyGroup(
syntax As SyntaxNode,
methodOrPropertyGroup As BoundMethodOrPropertyGroup,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Dim boundCall = BindInvocationExpression(syntax, syntax,
TypeCharacter.None,
methodOrPropertyGroup,
ImmutableArray(Of BoundExpression).Empty,
Nothing,
diagnostics,
callerInfoOpt:=syntax)
Return MakeRValue(boundCall, diagnostics)
End Function
''' <summary>
''' Checks if a given symbol is a function that takes no parameters.
''' </summary>
Private Shared ReadOnly s_isFunctionWithoutArguments As Func(Of Symbol, Boolean) = Function(sym)
If sym.Kind = SymbolKind.Method Then
Dim method = DirectCast(sym, MethodSymbol)
Return Not method.IsSub() AndAlso
Not method.IsGenericMethod AndAlso
method.CanBeCalledWithNoParameters
End If
Return False
End Function
''' <summary>
''' Checks if a given symbol is a property that is readable.
''' </summary>
Private Shared ReadOnly s_isReadablePropertyWithoutArguments As Func(Of Symbol, Boolean) = Function(sym)
If sym.Kind = SymbolKind.Property Then
Dim prop = DirectCast(sym, PropertySymbol)
Return prop.IsReadable AndAlso
Not prop.GetMostDerivedGetMethod().IsGenericMethod AndAlso
prop.GetCanBeCalledWithNoParameters
End If
Return False
End Function
''' <summary>
''' Returns the lookup result if at least one found symbol matches the requirements that are verified
''' by using the given symbolChecker. Extension methods will be considered in this check.
''' </summary>
''' <param name="name">The name of the method or property to look for.</param>
''' <param name="container">The container to look in.</param>
''' <param name="symbolChecker">The symbol checker which performs additional checks.</param>
Private Function GetMemberIfMatchesRequirements(
name As String,
container As TypeSymbol,
symbolChecker As Func(Of Symbol, Boolean),
result As LookupResult,
syntax As SyntaxNode,
diagnostics As BindingDiagnosticBag
) As Boolean
result.Clear()
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
LookupMember(result, container, name, 0, LookupOptions.AllMethodsOfAnyArity, useSiteInfo)
diagnostics.Add(syntax, useSiteInfo)
useSiteInfo = New CompoundUseSiteInfo(Of AssemblySymbol)(useSiteInfo)
If result.IsGood Then
For Each candidateSymbol In result.Symbols
If symbolChecker(candidateSymbol) Then
Return True
End If
Next
' if there are instance methods in the results, there will be no extension methods.
' Therefore we need to check separately.
If result.Symbols(0).Kind = SymbolKind.Method AndAlso
Not DirectCast(result.Symbols(0), MethodSymbol).IsReducedExtensionMethod Then
result.Clear()
LookupExtensionMethods(result,
container,
name,
0,
LookupOptions.AllMethodsOfAnyArity,
useSiteInfo)
diagnostics.Add(syntax, useSiteInfo)
If result.IsGood Then
For Each candidateSymbol In result.Symbols
If symbolChecker(candidateSymbol) Then
Return True
End If
Next
End If
Debug.Assert(Not result.IsAmbiguous)
End If
ElseIf result.IsAmbiguous Then
' save these diagnostics to report them if the collection does not match the design pattern at all instead of the
' generic diagnostic
Debug.Assert(result.HasDiagnostic)
diagnostics.Clear()
diagnostics.Add(New VBDiagnostic(result.Diagnostic, syntax.GetLocation()))
End If
result.Clear()
Return False
End Function
''' <summary>
''' Determines whether derivedType is, inherits from or implements the given interface.
''' </summary>
''' <param name="derivedType">The possible derived type.</param>
''' <param name="interfaceType">Type of the interface.</param>
''' <param name="useSiteInfo"/>
''' <param name="matchingInterfaces">A list of matching interfaces.</param>
''' <returns>
''' <c>true</c> if derivedType is, inherits from or implements the interface; otherwise, <c>false</c>.
''' </returns>
Friend Shared Function IsOrInheritsFromOrImplementsInterface(
derivedType As TypeSymbol,
interfaceType As NamedTypeSymbol,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
Optional matchingInterfaces As HashSet(Of NamedTypeSymbol) = Nothing
) As Boolean
' this is a more specialized version of the Dev10 code for IsOrInheritsFromOrImplements (type_helpers.cpp
' line 1210++) just covering Interfaces.
Debug.Assert(interfaceType.IsDefinition)
If derivedType.IsTypeParameter Then
Dim derivedTypeParameter = DirectCast(derivedType, TypeParameterSymbol)
' check constraints if they have an appropriate relation to the given interface
' if it has a value type constraint, check if system.valuetype satisfies the requirements
If derivedTypeParameter.HasValueTypeConstraint Then
Dim valueTypeSymbol = interfaceType.ContainingAssembly.GetSpecialType(SpecialType.System_ValueType)
If IsOrInheritsFromOrImplementsInterface(valueTypeSymbol, interfaceType, useSiteInfo, matchingInterfaces) AndAlso matchingInterfaces Is Nothing Then
Return True
End If
End If
' check if any interface constraint has the appropriate relation to the base type
For Each typeConstraint In derivedTypeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(useSiteInfo)
If IsOrInheritsFromOrImplementsInterface(typeConstraint, interfaceType, useSiteInfo, matchingInterfaces) AndAlso matchingInterfaces Is Nothing Then
Return True
End If
Next
Else
' derivedType could be an interface
If TypeSymbol.Equals(derivedType.OriginalDefinition, interfaceType, TypeCompareKind.ConsiderEverything) Then
If matchingInterfaces Is Nothing Then
Return True
End If
RecordMatchForIsOrInheritsFromOrImplementsInterface(matchingInterfaces, DirectCast(derivedType, NamedTypeSymbol))
End If
' implements or inherits interface
For Each interfaceOfDerived In derivedType.AllInterfacesWithDefinitionUseSiteDiagnostics(useSiteInfo)
If TypeSymbol.Equals(interfaceOfDerived.OriginalDefinition, interfaceType, TypeCompareKind.ConsiderEverything) Then
If matchingInterfaces Is Nothing Then
Return True
End If
RecordMatchForIsOrInheritsFromOrImplementsInterface(matchingInterfaces, interfaceOfDerived)
End If
Next
End If
Return matchingInterfaces IsNot Nothing AndAlso matchingInterfaces.Count > 0
End Function
Private Shared Sub RecordMatchForIsOrInheritsFromOrImplementsInterface(matchingInterfaces As HashSet(Of NamedTypeSymbol), interfaceOfDerived As NamedTypeSymbol)
Debug.Assert(matchingInterfaces.Comparer Is EqualsIgnoringComparer.InstanceIgnoringTupleNames OrElse
matchingInterfaces.Comparer Is EqualityComparer(Of NamedTypeSymbol).Default)
If Not matchingInterfaces.Add(interfaceOfDerived) AndAlso
matchingInterfaces.Comparer Is EqualsIgnoringComparer.InstanceIgnoringTupleNames AndAlso
Not interfaceOfDerived.IsDefinition Then
' Keep the last match in the set
matchingInterfaces.Remove(interfaceOfDerived)
matchingInterfaces.Add(interfaceOfDerived)
End If
End Sub
Public Function BindWithBlock(node As WithBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim binder As Binder = Me.GetBinder(DirectCast(node, VisualBasicSyntaxNode))
Return binder.CreateBoundWithBlock(node, binder, diagnostics)
End Function
Protected Overridable Function CreateBoundWithBlock(node As WithBlockSyntax, boundBlockBinder As Binder, diagnostics As BindingDiagnosticBag) As BoundStatement
Return Me.ContainingBinder.CreateBoundWithBlock(node, boundBlockBinder, diagnostics)
End Function
''' <summary>
''' Initially binding using blocks.
''' A Using statement names a resource that is supposed to be disposed on completion.
''' The resource can be an expression or a list of local variables with initializers.
''' the type of the resource must implement System.IDispose
''' A using statement of the form:
''' using Expression
''' list_of_statements
''' end using
'''
''' when the resource is a using locally declared variable no temporary is generated but the variable is read-only
''' A using statement of the form:
''' using v as new myDispose
''' list_of_statements
''' end using
''' It is also possible to use multiple variable resources:
''' using v1 as new myDispose, v2 as myDispose = new myDispose()
''' list_of_statements
''' end using
'''</summary>
Public Function BindUsingBlock(node As UsingBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim usingBinder = Me.GetBinder(node)
Debug.Assert(usingBinder IsNot Nothing)
Dim resourceList As ImmutableArray(Of BoundLocalDeclarationBase) = Nothing
Dim resourceExpression As BoundExpression = Nothing
Dim usingStatement = node.UsingStatement
Dim usingVariableDeclarations = usingStatement.Variables
Dim usingVariableDeclarationCount = usingVariableDeclarations.Count
Dim iDisposable = GetSpecialType(SpecialType.System_IDisposable,
node,
diagnostics)
Dim placeholderInfo = New Dictionary(Of TypeSymbol, ValueTuple(Of BoundRValuePlaceholder, BoundExpression, BoundExpression))()
If usingVariableDeclarationCount > 0 Then
' this is the case of a using statement with one or more variable declarations.
' this will bind the declaration, infer the local's type if needed and binds the initialization expression.
' implicit variable declarations are handled in that method as well (Option Explicit Off)
resourceList = usingBinder.BindVariableDeclarators(usingVariableDeclarations, diagnostics)
' now that the variable declarations and initialization expression are bound, report
' using statement related diagnostics.
For resourceIndex = 0 To usingVariableDeclarationCount - 1
Dim localDeclarations = resourceList(resourceIndex)
Dim declarationSyntax = localDeclarations.Syntax
' bound locals have the identifier syntax as their syntax, but for error messages we want to
' show squiggles under the whole declaration.
' There is one exception to this rule: the parent is a declarator and has multiple names then
' the identifier syntax should be used to be able to distinguish between the error locations.
' e.g. dim x, y as Integer = 23
Dim syntaxNodeForErrors As SyntaxNode
If localDeclarations.Kind <> BoundKind.AsNewLocalDeclarations Then
syntaxNodeForErrors = declarationSyntax.Parent
If syntaxNodeForErrors Is Nothing OrElse
DirectCast(syntaxNodeForErrors, VariableDeclaratorSyntax).Names.Count > 1 Then
syntaxNodeForErrors = declarationSyntax
End If
Else
syntaxNodeForErrors = declarationSyntax
End If
' check if all declared variables are initialized
If localDeclarations.Kind = BoundKind.LocalDeclaration Then
Dim boundLocalDeclaration = DirectCast(localDeclarations, BoundLocalDeclaration)
Dim initializerExpression = boundLocalDeclaration.InitializerOpt
If initializerExpression Is Nothing Then
ReportDiagnostic(diagnostics,
syntaxNodeForErrors,
ErrorFactory.ErrorInfo(ERRID.ERR_UsingResourceVarNeedsInitializer))
End If
VerifyUsingVariableDeclarationAndBuildUsingInfo(syntaxNodeForErrors,
boundLocalDeclaration.LocalSymbol,
iDisposable,
placeholderInfo,
diagnostics)
Else
Dim boundAsNewDeclarations = DirectCast(localDeclarations, BoundAsNewLocalDeclarations)
' there can be multiple variables be declared in an "As New"
For declarationIndex = 0 To boundAsNewDeclarations.LocalDeclarations.Length - 1
Dim localDeclaration As BoundLocalDeclaration = boundAsNewDeclarations.LocalDeclarations(declarationIndex)
VerifyUsingVariableDeclarationAndBuildUsingInfo(localDeclaration.Syntax,
localDeclaration.LocalSymbol,
iDisposable,
placeholderInfo,
diagnostics)
Next
End If
Next
Else
' the using block has an expression as resource
Debug.Assert(usingStatement.Expression IsNot Nothing)
Dim resourceExpressionSyntax = usingStatement.Expression
resourceExpression = BindRValue(resourceExpressionSyntax, diagnostics)
Dim resourceExpressionType = resourceExpression.Type
If Not resourceExpressionType.IsErrorType AndAlso Not iDisposable.IsErrorType Then
BuildAndVerifyUsingInfo(resourceExpressionSyntax,
resourceExpression.Type,
placeholderInfo,
iDisposable,
diagnostics)
End If
End If
' Bind the body of the using statement.
Dim usingBody As BoundBlock = BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
Dim usingInfo As New UsingInfo(node, placeholderInfo)
Dim locals As ImmutableArray(Of LocalSymbol) = GetUsingBlockLocals(usingBinder)
Return New BoundUsingStatement(node, resourceList, resourceExpression, usingBody, usingInfo, locals)
End Function
Private Function GetUsingBlockLocals(currentBinder As Binder) As ImmutableArray(Of LocalSymbol)
Dim usingBlockBinder As UsingBlockBinder
Do
usingBlockBinder = TryCast(currentBinder, UsingBlockBinder)
If usingBlockBinder IsNot Nothing Then
Return usingBlockBinder.Locals
End If
currentBinder = currentBinder.ContainingBinder
Loop While currentBinder IsNot Nothing
Debug.Fail("Failed to find UsingBlockBinder")
Return ImmutableArray(Of LocalSymbol).Empty
End Function
Private Sub VerifyUsingVariableDeclarationAndBuildUsingInfo(
syntaxNode As SyntaxNode,
localSymbol As LocalSymbol,
iDisposable As TypeSymbol,
placeholderInfo As Dictionary(Of TypeSymbol, ValueTuple(Of BoundRValuePlaceholder, BoundExpression, BoundExpression)),
diagnostics As BindingDiagnosticBag
)
Dim declarationType As TypeSymbol = localSymbol.Type
' Explicit array sizes in declarators imply creating an array this is not supported.
' The resource variable type needs to implement System.IDisposable and System.Array is known to not implement it.
If declarationType.IsArrayType Then
' this diagnostic will be reported even if a missing initializer was reported before. This is a change
' from Dev10 which stopped "binding" at the first error
ReportDiagnostic(diagnostics,
syntaxNode,
ErrorFactory.ErrorInfo(ERRID.ERR_UsingResourceVarCantBeArray))
ElseIf Not declarationType.IsErrorType AndAlso Not iDisposable.IsErrorType Then
BuildAndVerifyUsingInfo(syntaxNode,
declarationType,
placeholderInfo,
iDisposable,
diagnostics)
' TODO: Dev10 shows the error on the symbol name, not the whole declaration. See bug 10720
' TODO: we could suppress this warning if the type does not implement IDisposable and is not late bound.
' BuildAndVerifyUsingInfo would need to return a boolean then, it has all the information available.
ReportMutableStructureConstraintsInUsing(declarationType,
localSymbol.Name,
syntaxNode,
diagnostics)
End If
End Sub
Private Sub BuildAndVerifyUsingInfo(
syntaxNode As SyntaxNode,
resourceType As TypeSymbol,
placeholderInfo As Dictionary(Of TypeSymbol, ValueTuple(Of BoundRValuePlaceholder, BoundExpression, BoundExpression)),
iDisposable As TypeSymbol,
diagnostics As BindingDiagnosticBag
)
If Not placeholderInfo.ContainsKey(resourceType) Then
' TODO: add late binding, see statementsemantics.cpp lines 6765++
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim conversionKind = Conversions.ClassifyDirectCastConversion(resourceType, iDisposable, useSiteInfo)
If diagnostics.Add(syntaxNode, useSiteInfo) Then
' Suppress additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
End If
Dim isValidDispose = Conversions.IsWideningConversion(conversionKind)
If isValidDispose OrElse
(resourceType.IsObjectType() AndAlso OptionStrict <> OptionStrict.On) Then
Dim resourcePlaceholder = New BoundRValuePlaceholder(syntaxNode, resourceType)
Dim disposeConversion As BoundExpression = Nothing
Dim disposeCondition As BoundExpression = Nothing
If Not resourceType.IsValueType Then
disposeConversion = ApplyImplicitConversion(syntaxNode, iDisposable, resourcePlaceholder, diagnostics)
disposeCondition = BindIsExpression(resourcePlaceholder,
New BoundLiteral(syntaxNode, ConstantValue.Nothing, Nothing),
syntaxNode, True, diagnostics)
End If
placeholderInfo.Add(resourceType,
New ValueTuple(Of BoundRValuePlaceholder, BoundExpression, BoundExpression)(
resourcePlaceholder,
disposeConversion,
disposeCondition))
Else
ReportDiagnostic(diagnostics,
syntaxNode,
ErrorFactory.ErrorInfo(ERRID.ERR_UsingRequiresDisposePattern, resourceType))
End If
End If
End Sub
''' <summary>Check the given type of and report WRN_MutableGenericStructureInUsing if needed.</summary>
''' <remarks>This function should only be called for a type of a using variable.</remarks>
Private Sub ReportMutableStructureConstraintsInUsing(type As TypeSymbol, symbolName As String, syntaxNode As SyntaxNode, diagnostics As BindingDiagnosticBag)
' Dev10 #666593: Warn if the type of the variable is not a reference type or an immutable structure.
If Not type.IsReferenceType Then
If type.IsTypeParameter Then
If type.IsValueType Then
Dim typeParameter = DirectCast(type, TypeParameterSymbol)
' there is currently no way to only get the type constraint from a type parameter. So we're
' iterating over all of them
Dim processedValueTypes As Boolean = False
For Each constraintType In DirectCast(type, TypeParameterSymbol).ConstraintTypesNoUseSiteDiagnostics
' TODO: if this constraint is a type parameter as well, dig into its constraints as well.
' there is a case where a type constraint of a type parameter is a concrete structure,
' therefore we'll need to check the type constraint as well.
' See TypeParameter.IsValueType for more information
If constraintType.IsValueType Then
processedValueTypes = True
If ShouldReportMutableStructureInUsing(constraintType) Then
ReportDiagnostic(diagnostics,
syntaxNode,
ErrorFactory.ErrorInfo(ERRID.WRN_MutableStructureInUsing, symbolName))
Return
End If
End If
Next
' only show this message if there was no class constraint which is a value type
' this can be the case of a "Structure" constraint with only interface constraints
If Not processedValueTypes Then
' the type parameter only has a structure constraint
Debug.Assert(typeParameter.HasValueTypeConstraint)
ReportDiagnostic(diagnostics,
syntaxNode,
ErrorFactory.ErrorInfo(ERRID.WRN_MutableStructureInUsing, symbolName))
End If
Else
' it's just a generic type parameter, show generic diagnostics
ReportDiagnostic(diagnostics,
syntaxNode,
ErrorFactory.ErrorInfo(ERRID.WRN_MutableGenericStructureInUsing, symbolName))
End If
ElseIf ShouldReportMutableStructureInUsing(type) Then
ReportDiagnostic(diagnostics,
syntaxNode,
ErrorFactory.ErrorInfo(ERRID.WRN_MutableStructureInUsing, symbolName))
End If
End If
End Sub
' This method decides if a WRN_MutableStructureInUsing should be shown for a given type of the Using variable.
' The purpose of this function is to avoid code duplication in 'CheckForMutableStructureConstraints'.
' This is not a general purpose helper.
Private Shared Function ShouldReportMutableStructureInUsing(structureType As TypeSymbol) As Boolean
Debug.Assert(structureType.IsValueType)
If structureType.Kind = SymbolKind.NamedType Then
If structureType.IsStructureType AndAlso Not structureType.IsEnumType AndAlso Not structureType.IsIntrinsicType Then
For Each member In structureType.GetMembersUnordered
If member.Kind = SymbolKind.Field AndAlso
Not member.IsShared AndAlso Not DirectCast(member, FieldSymbol).IsReadOnly Then
Return True
End If
Next
Else
Debug.Assert(Not structureType.IsVoidType)
End If
End If
Return False
End Function
''' <summary>
''' Binds a sync lock block.
''' A SyncLock come in the following form:
'''
''' SyncLock <expression>
''' <body>
''' End SyncLock
''' </summary>
''' <param name="node">The node.</param>
''' <param name="diagnostics">The diagnostics.</param><returns></returns>
Public Function BindSyncLockBlock(node As SyncLockBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundSyncLockStatement
' bind the expression
Dim lockExpression As BoundExpression = BindRValue(node.SyncLockStatement.Expression, diagnostics)
Dim lockExpressionType = lockExpression.Type
If Not lockExpression.HasErrors Then
If Not lockExpressionType.IsReferenceType Then
ReportDiagnostic(diagnostics,
lockExpression.Syntax,
ErrorFactory.ErrorInfo(ERRID.ERR_SyncLockRequiresReferenceType1, lockExpressionType))
ElseIf lockExpressionType.IsWellKnownTypeLock() Then
ReportDiagnostic(diagnostics, lockExpression.Syntax, ERRID.ERR_LockTypeUnsupported)
End If
End If
Dim boundBody = BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
Return New BoundSyncLockStatement(node, lockExpression, boundBody)
End Function
Public Function BindTryBlock(node As TryBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundTryStatement
Debug.Assert(node IsNot Nothing)
Dim tryBlock As BoundBlock = BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
Dim catchBlocks As ImmutableArray(Of BoundCatchBlock) = BindCatchBlocks(node.CatchBlocks, diagnostics)
Dim finallyBlockOpt As BoundBlock
If node.FinallyBlock IsNot Nothing Then
Dim finallyBinder = GetBinder(node.FinallyBlock)
finallyBlockOpt = finallyBinder.BindBlock(node.FinallyBlock, node.FinallyBlock.Statements, diagnostics)
Else
finallyBlockOpt = Nothing
End If
If catchBlocks.IsEmpty AndAlso finallyBlockOpt Is Nothing Then
ReportDiagnostic(diagnostics, node.TryStatement, ERRID.ERR_TryWithoutCatchOrFinally)
End If
Dim tryBinder As Binder = GetBinder(node)
Return New BoundTryStatement(node, tryBlock, catchBlocks, finallyBlockOpt, tryBinder.GetExitLabel(SyntaxKind.ExitTryStatement))
End Function
Public Function BindCatchBlocks(catchClauses As SyntaxList(Of CatchBlockSyntax), diagnostics As BindingDiagnosticBag) As ImmutableArray(Of BoundCatchBlock)
Dim n As Integer = catchClauses.Count
If n = 0 Then
Return ImmutableArray(Of BoundCatchBlock).Empty
End If
Dim catchBlocks = ArrayBuilder(Of BoundCatchBlock).GetInstance(n)
For Each catchSyntax In catchClauses
Dim catchBinder As Binder = GetBinder(catchSyntax)
Dim catchBlock As BoundCatchBlock = catchBinder.BindCatchBlock(catchSyntax, catchBlocks, diagnostics)
catchBlocks.Add(catchBlock)
Next
Return catchBlocks.ToImmutableAndFree
End Function
Private Function BindCatchBlock(node As CatchBlockSyntax, previousBlocks As ArrayBuilder(Of BoundCatchBlock), diagnostics As BindingDiagnosticBag) As BoundCatchBlock
' we need to compute the following parts
Dim catchLocal As LocalSymbol = Nothing
Dim exceptionSource As BoundExpression = Nothing
Dim exceptionFilter As BoundExpression = Nothing
Dim exceptionType As TypeSymbol
Dim hasError = False
Dim declaration = node.CatchStatement
Dim name As IdentifierNameSyntax = declaration.IdentifierName
Dim asClauseOpt = declaration.AsClause
' if we have "as" we need to declare a local
If asClauseOpt IsNot Nothing Then
Dim localAccess As BoundLocal = BindCatchVariableDeclaration(name, asClauseOpt, diagnostics)
exceptionSource = localAccess
catchLocal = localAccess.LocalSymbol
ElseIf name IsNot Nothing Then
exceptionSource = BindSimpleName(name, False, diagnostics)
End If
' verify the catch variable.
' 1) must be a local or parameter (byref parameters are ok, static locals are not).
' 2) must be or derive from System.Exception
If exceptionSource IsNot Nothing Then
Dim originalExceptionValue As BoundExpression = exceptionSource
If Not exceptionSource.IsValue OrElse exceptionSource.Type Is Nothing OrElse exceptionSource.Type.IsVoidType Then
exceptionSource = BadExpression(exceptionSource.Syntax, exceptionSource, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
End If
exceptionType = exceptionSource.Type
If originalExceptionValue.HasErrors Then
hasError = True
Else
Dim exprKind = exceptionSource.Kind
If Not (exprKind = BoundKind.Parameter OrElse
exprKind = BoundKind.Local AndAlso Not DirectCast(exceptionSource, BoundLocal).LocalSymbol.IsStatic) Then
ReportDiagnostic(diagnostics, name, ERRID.ERR_CatchVariableNotLocal1, name.ToString())
hasError = True
Else
' type of the catch variable must derive from Exception
Debug.Assert(exceptionType IsNot Nothing)
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If exceptionType.IsErrorType() Then
hasError = True
ElseIf Not exceptionType.IsOrDerivedFromWellKnownClass(WellKnownType.System_Exception, Compilation, useSiteInfo) Then
ReportDiagnostic(diagnostics,
If(asClauseOpt IsNot Nothing, asClauseOpt.Type, name),
ERRID.ERR_CatchNotException1,
exceptionType)
hasError = True
End If
diagnostics.Add(If(asClauseOpt IsNot Nothing, asClauseOpt.Type, name), useSiteInfo)
End If
End If
Else
exceptionType = GetWellKnownType(WellKnownType.System_Exception, node, diagnostics)
End If
Dim whenSyntax = declaration.WhenClause
If whenSyntax IsNot Nothing Then
exceptionFilter = BindBooleanExpression(whenSyntax.Filter, diagnostics)
End If
If Not hasError Then
Debug.Assert(exceptionType.IsOrDerivedFromWellKnownClass(WellKnownType.System_Exception, Compilation, CompoundUseSiteInfo(Of AssemblySymbol).Discarded))
For Each previousBlock In previousBlocks
If previousBlock.ExceptionFilterOpt IsNot Nothing Then
' filters are considered to have behavior defined at run time
' therefore catches with filters are not considered in this analysis
' the fact that filter may be a compile-time constant is ignored.
Continue For
End If
Dim previousType As TypeSymbol
If previousBlock.ExceptionSourceOpt IsNot Nothing Then
previousType = previousBlock.ExceptionSourceOpt.Type
Else
' do not report diagnostics if type is missing, it should have been already recorded.
previousType = Compilation.GetWellKnownType(WellKnownType.System_Exception)
End If
If Not previousType.IsErrorType() Then
If TypeSymbol.Equals(previousType, exceptionType, TypeCompareKind.ConsiderEverything) Then
ReportDiagnostic(diagnostics, declaration, ERRID.WRN_DuplicateCatch, exceptionType)
Exit For
End If
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim isBaseType As Boolean = exceptionType.IsOrDerivedFrom(previousType, useSiteInfo)
diagnostics.Add(declaration, useSiteInfo)
If isBaseType Then
ReportDiagnostic(diagnostics, declaration, ERRID.WRN_OverlappingCatch, exceptionType, previousType)
Exit For
End If
End If
Next
End If
Dim block = Me.BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated()
Return New BoundCatchBlock(node, catchLocal, exceptionSource,
errorLineNumberOpt:=Nothing,
exceptionFilterOpt:=exceptionFilter,
body:=block,
hasErrors:=hasError,
isSynthesizedAsyncCatchAll:=False)
End Function
Private Function BindExitStatement(node As ExitStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim targetLabel As LabelSymbol = GetExitLabel(node.Kind)
If targetLabel Is Nothing Then
Dim id As ERRID
Select Case node.Kind
Case SyntaxKind.ExitWhileStatement : id = ERRID.ERR_ExitWhileNotWithinWhile
Case SyntaxKind.ExitTryStatement : id = ERRID.ERR_ExitTryNotWithinTry
Case SyntaxKind.ExitDoStatement : id = ERRID.ERR_ExitDoNotWithinDo
Case SyntaxKind.ExitForStatement : id = ERRID.ERR_ExitForNotWithinFor
Case SyntaxKind.ExitSelectStatement : id = ERRID.ERR_ExitSelectNotWithinSelect
Case SyntaxKind.ExitSubStatement : id = ERRID.ERR_ExitSubOfFunc
Case SyntaxKind.ExitFunctionStatement : id = ERRID.ERR_ExitFuncOfSub
Case SyntaxKind.ExitPropertyStatement : id = ERRID.ERR_ExitPropNot
Case Else : ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
ReportDiagnostic(diagnostics, node, id)
Return New BoundBadStatement(node, ImmutableArray(Of BoundNode).Empty, hasErrors:=True)
Else
Return New BoundExitStatement(node, targetLabel)
End If
End Function
Private Function BindContinueStatement(node As ContinueStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim targetLabel As LabelSymbol = GetContinueLabel(node.Kind)
If targetLabel Is Nothing Then
Dim id As ERRID
Select Case node.Kind
Case SyntaxKind.ContinueWhileStatement : id = ERRID.ERR_ContinueWhileNotWithinWhile
Case SyntaxKind.ContinueDoStatement : id = ERRID.ERR_ContinueDoNotWithinDo
Case SyntaxKind.ContinueForStatement : id = ERRID.ERR_ContinueForNotWithinFor
Case Else : ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
ReportDiagnostic(diagnostics, node, id)
Return New BoundBadStatement(node, ImmutableArray(Of BoundNode).Empty, hasErrors:=True)
Else
Return New BoundContinueStatement(node, targetLabel)
End If
End Function
Private Function BindBooleanExpression(node As ExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
' 11.19 Boolean Expressions
Dim expr As BoundExpression = Me.BindValue(node, diagnostics, isOperandOfConditionalBranch:=True)
Dim boolSymbol As NamedTypeSymbol = GetSpecialType(SpecialType.System_Boolean, node, diagnostics)
' NOTE: Errors in 'expr' will be handles properly by ApplyImplicitConversion(...)
Return Me.ApplyImplicitConversion(node, boolSymbol, expr, diagnostics, isOperandOfConditionalBranch:=True)
End Function
Private Function GetCurrentReturnType(<Out()> ByRef isAsync As Boolean,
<Out()> ByRef isIterator As Boolean,
<Out()> ByRef methodReturnType As TypeSymbol) As TypeSymbol
isAsync = False
isIterator = False
Dim method As MethodSymbol = TryCast(Me.ContainingMember, MethodSymbol)
If method IsNot Nothing Then
methodReturnType = method.ReturnType
isAsync = method.IsAsync
' method cannot be both iterator and async, so async will win here.
isIterator = Not isAsync AndAlso method.IsIterator
If Not method.IsSub Then
If isAsync AndAlso method.ReturnType.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T)) Then
Return DirectCast(method.ReturnType, NamedTypeSymbol).TypeArgumentsNoUseSiteDiagnostics(0)
ElseIf isAsync AndAlso method.ReturnType.Equals(Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task)) Then
' I don't believe we need to report use-site error here because we are using System.Void just as an indicator that we don't expect any value to be returned.
Return Compilation.GetSpecialType(SpecialType.System_Void)
ElseIf isIterator Then
' I don't believe we need to report use-site error here because we are using System.Void just as an indicator that we don't expect any value to be returned.
Return Compilation.GetSpecialType(SpecialType.System_Void)
End If
End If
Return methodReturnType
End If
methodReturnType = ErrorTypeSymbol.UnknownResultType
Return methodReturnType
End Function
Private Function BindReturn(originalSyntax As ReturnStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim expressionSyntax As ExpressionSyntax = originalSyntax.Expression
' UNDONE - Handle ERRID_ReturnFromEventMethod
' If we are in a lambda binding, then the original syntax could be just the expression.
' If we are not in a lambda binding then we have a real return statement.
Debug.Assert(originalSyntax IsNot Nothing)
Debug.Assert(expressionSyntax Is originalSyntax OrElse originalSyntax.Expression Is expressionSyntax)
Dim isAsync As Boolean
Dim isIterator As Boolean
Dim methodReturnType As TypeSymbol = Nothing
Dim retType As TypeSymbol = Me.GetCurrentReturnType(isAsync, isIterator, methodReturnType)
Dim returnLabel = GetReturnLabel()
If BindingTopLevelScriptCode Then
ReportDiagnostic(diagnostics, originalSyntax, ERRID.ERR_KeywordNotAllowedInScript, SyntaxFacts.GetText(SyntaxKind.ReturnKeyword))
Return New BoundReturnStatement(originalSyntax, Nothing, Nothing, returnLabel, hasErrors:=True)
End If
If retType.SpecialType = SpecialType.System_Void Then
' For subs just generate a return
If expressionSyntax IsNot Nothing Then
ReportDiagnostic(diagnostics, originalSyntax,
If(isIterator,
ERRID.ERR_BadReturnValueInIterator,
If(isAsync AndAlso Not methodReturnType.SpecialType = SpecialType.System_Void, ERRID.ERR_ReturnFromNonGenericTaskAsync, ERRID.ERR_ReturnFromNonFunction)))
Dim arg As BoundExpression = Me.BindValue(expressionSyntax, diagnostics)
arg = MakeRValueAndIgnoreDiagnostics(arg)
Return New BoundReturnStatement(originalSyntax, arg, Nothing, returnLabel, hasErrors:=True)
End If
Return New BoundReturnStatement(originalSyntax, Nothing, Nothing, returnLabel)
Else
Dim arg As BoundExpression = Nothing
If expressionSyntax IsNot Nothing Then
arg = Me.BindValue(expressionSyntax, diagnostics)
End If
If arg IsNot Nothing Then
If retType Is LambdaSymbol.ReturnTypeIsUnknown Then
' We will have LambdaSymbol.ReturnTypeIsUnknown as the target return type
' if we are inside a lambda, for which we failed to infer the return type.
Debug.Assert(Me.ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(Me.ContainingMember, MethodSymbol).MethodKind = MethodKind.LambdaMethod)
' We want to make sure that the return expressions are as bound as they can be.
' For example, if return expression is a lambda, we want it to be a BoundLambda, not
' an UnboundLambda node (that is what BindValue stops at). In order to get to BoundLambda,
' we need to call MakeRValue, but we will ignore any diagnostics it will produce because
' an inference error has been reported earlier.
arg = MakeRValueAndIgnoreDiagnostics(arg)
ElseIf retType Is LambdaSymbol.ReturnTypeIsBeingInferred Then
' Target retType can be LambdaSymbol.ReturnTypeIsBeingInferred if we are inferring return type of a lambda.
' We should leave expression unmodified, to allow more accurate inference.
Debug.Assert(Me.ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(Me.ContainingMember, MethodSymbol).MethodKind = MethodKind.LambdaMethod)
Else
' If we were in an async method that returned Task<T> for some T,
' and the return expression can't be converted to T but is identical to Task<T>,
' then don't give the normal error message about "there is no conversion from T to Task<T>"
' and instead say "Since this is async, the return expression must be 'T' rather than 'Task<T>'."
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If isAsync AndAlso Not retType.IsErrorType() AndAlso methodReturnType.Equals(arg.Type) AndAlso
methodReturnType.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T)) AndAlso
Not Conversions.ConversionExists(Conversions.ClassifyConversion(arg, retType, Me, useSiteInfo).Key) Then
If Not diagnostics.Add(arg, useSiteInfo) Then
ReportDiagnostic(diagnostics, arg.Syntax, ERRID.ERR_BadAsyncReturnOperand1, retType)
End If
arg = MakeRValueAndIgnoreDiagnostics(arg)
Else
diagnostics.Add(arg, useSiteInfo)
arg = ApplyImplicitConversion(arg.Syntax, retType, arg, diagnostics)
End If
End If
End If
' For functions generate return expression with local return value symbol
If arg IsNot Nothing Then
Return New BoundReturnStatement(originalSyntax, arg, GetLocalForFunctionValue(), returnLabel)
Else
If isAsync AndAlso retType Is LambdaSymbol.ReturnTypeIsBeingInferred Then
' Target retType can be LambdaSymbol.ReturnTypeIsBeingInferred if we are inferring return type of a lambda.
' We should not require expression, to allow more accurate inference.
Debug.Assert(Me.ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(Me.ContainingMember, MethodSymbol).MethodKind = MethodKind.LambdaMethod)
Return New BoundReturnStatement(originalSyntax, Nothing, Nothing, returnLabel, hasErrors:=False)
Else
ReportDiagnostic(diagnostics, originalSyntax, ERRID.ERR_ReturnWithoutValue)
Return New BoundReturnStatement(originalSyntax, Nothing, Nothing, returnLabel, hasErrors:=True)
End If
End If
End If
End Function
Private Function GetCurrentYieldType(node As YieldStatementSyntax,
diagnostics As BindingDiagnosticBag) As TypeSymbol
Dim method As MethodSymbol = TryCast(Me.ContainingMember, MethodSymbol)
If method IsNot Nothing Then
Dim methodReturnType = method.ReturnType
If Not method.IsIterator Then
' no idea how we can get here since parsing of Yield is contextual, but there is an error ID for this
ReportDiagnostic(diagnostics, node, ERRID.ERR_BadYieldInNonIteratorMethod)
End If
If Not method.IsSub Then
Dim returnNamedType = TryCast(methodReturnType.OriginalDefinition, NamedTypeSymbol)
Dim returnSpecialType As SpecialType = If(returnNamedType IsNot Nothing, returnNamedType.SpecialType, Nothing)
If returnSpecialType = SpecialType.System_Collections_Generic_IEnumerable_T OrElse
returnSpecialType = SpecialType.System_Collections_Generic_IEnumerator_T Then
Return DirectCast(methodReturnType, NamedTypeSymbol).TypeArgumentsNoUseSiteDiagnostics(0)
End If
If returnSpecialType = SpecialType.System_Collections_IEnumerable OrElse
returnSpecialType = SpecialType.System_Collections_IEnumerator Then
Return GetSpecialType(SpecialType.System_Object, node, diagnostics)
End If
End If
If methodReturnType Is LambdaSymbol.ReturnTypeIsUnknown OrElse
methodReturnType Is LambdaSymbol.ReturnTypeIsBeingInferred Then
Return methodReturnType
End If
End If
' It is either nongeneric iterator or an error (which should be already reported).
Return ErrorTypeSymbol.UnknownResultType
End Function
Private Function BindYield(originalSyntax As YieldStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim expressionSyntax As ExpressionSyntax = originalSyntax.Expression
Dim arg As BoundExpression = Me.BindValue(expressionSyntax, diagnostics)
If BindingTopLevelScriptCode Then
ReportDiagnostic(diagnostics, originalSyntax, ERRID.ERR_KeywordNotAllowedInScript, SyntaxFacts.GetText(SyntaxKind.YieldKeyword))
arg = MakeRValueAndIgnoreDiagnostics(arg)
Return New BoundYieldStatement(originalSyntax, arg, hasErrors:=True)
End If
Dim retType As TypeSymbol = Me.GetCurrentYieldType(originalSyntax, diagnostics)
If retType Is LambdaSymbol.ReturnTypeIsUnknown Then
' We will have LambdaSymbol.ReturnTypeIsUnknown as the target return type
' if we are inside a lambda, for which we failed to infer the return type.
Debug.Assert(Me.ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(Me.ContainingMember, MethodSymbol).MethodKind = MethodKind.LambdaMethod)
' We want to make sure that the return expressions are as bound as they can be.
' For example, if return expression is a lambda, we want it to be a BoundLambda, not
' an UnboundLambda node (that is what BindValue stops at). In order to get to BoundLambda,
' we need to call MakeRValue, but we will ignore any diagnostics it will produce because
' an inference error has been reported earlier.
arg = MakeRValueAndIgnoreDiagnostics(arg)
ElseIf retType Is LambdaSymbol.ReturnTypeIsBeingInferred Then
' Target retType can be LambdaSymbol.ReturnTypeIsBeingInferred if we are inferring return type of a lambda.
' We should leave expression unmodified, to allow more accurate inference.
Debug.Assert(Me.ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(Me.ContainingMember, MethodSymbol).MethodKind = MethodKind.LambdaMethod)
Return New BoundYieldStatement(originalSyntax, arg, hasErrors:=False, returnTypeIsBeingInferred:=True)
Else
arg = ApplyImplicitConversion(arg.Syntax, retType, arg, diagnostics)
End If
Return New BoundYieldStatement(originalSyntax, arg)
End Function
Private Function BindThrow(node As ThrowStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim expressionSyntax As ExpressionSyntax = node.Expression
Dim hasError As Boolean = False
If expressionSyntax Is Nothing Then
Dim curSyntax As VisualBasicSyntaxNode = node.Parent
Dim canRethrow As Boolean = False
While curSyntax IsNot Nothing
Select Case curSyntax.Kind
Case SyntaxKind.CatchBlock
canRethrow = True
Exit While
Case SyntaxKind.FinallyBlock
' CLI spec (with Microsoft specific implementation notes).
' 12.4.2.8.2.2 rethrow:
' The Microsoft implementation requires that either the catch handler is the innermost enclosing
' exception-handling block, or all intervening exception-handling blocks are protected regions.
Exit While
Case SyntaxKind.SingleLineFunctionLambdaExpression
Case SyntaxKind.MultiLineFunctionLambdaExpression
Case SyntaxKind.SingleLineSubLambdaExpression
Case SyntaxKind.MultiLineSubLambdaExpression
Exit While
Case Else
If TypeOf curSyntax Is MethodBlockBaseSyntax Then
Exit While
End If
End Select
curSyntax = curSyntax.Parent
End While
If Not canRethrow Then
hasError = True
ReportDiagnostic(diagnostics, node, ERRID.ERR_MustBeInCatchToRethrow)
End If
Return New BoundThrowStatement(node, Nothing, hasError)
Else
Dim value As BoundExpression = BindRValue(expressionSyntax, diagnostics)
Dim exceptionType = value.Type
If Not exceptionType.IsErrorType Then
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If Not exceptionType.IsOrDerivedFromWellKnownClass(WellKnownType.System_Exception, Compilation, useSiteInfo) Then
hasError = True
ReportDiagnostic(diagnostics, node, ERRID.ERR_CantThrowNonException)
End If
diagnostics.Add(node, useSiteInfo)
Else
hasError = True
End If
Return New BoundThrowStatement(node, value, hasError)
End If
End Function
Private Function BindError(node As ErrorStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
Dim value As BoundExpression = ApplyImplicitConversion(node.ErrorNumber,
GetSpecialType(SpecialType.System_Int32, node.ErrorNumber, diagnostics),
BindValue(node.ErrorNumber, diagnostics),
diagnostics)
Return New BoundThrowStatement(node, value)
End Function
Private Function BindResumeStatement(node As ResumeStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundResumeStatement
If IsInLambda Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_MultilineLambdasCannotContainOnError)
ElseIf IsInAsyncContext() OrElse IsInIteratorContext() Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_ResumablesCannotContainOnError)
End If
Select Case node.Kind
Case SyntaxKind.ResumeStatement
Return New BoundResumeStatement(node)
Case SyntaxKind.ResumeNextStatement
Return New BoundResumeStatement(node, isNext:=True)
Case SyntaxKind.ResumeLabelStatement
Dim symbol As LabelSymbol = Nothing
Dim boundLabelExpression As BoundExpression = BindExpression(node.Label, diagnostics)
If boundLabelExpression.Kind = BoundKind.Label Then
Dim boundLabel = DirectCast(boundLabelExpression, boundLabel)
symbol = boundLabel.Label
Return New BoundResumeStatement(node, symbol, boundLabel, hasErrors:=Not IsValidLabelForGoto(symbol, node.Label, diagnostics))
Else
' if the bound label is e.g. a bad bound expression because of a non-existent label,
' make this a bad statement.
Return New BoundResumeStatement(node, Nothing, boundLabelExpression, hasErrors:=True)
End If
Case Else
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
End Function
Private Function BindOnErrorStatement(node As StatementSyntax, diagnostics As BindingDiagnosticBag) As BoundOnErrorStatement
If IsInLambda Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_MultilineLambdasCannotContainOnError)
ElseIf IsInAsyncContext() OrElse IsInIteratorContext() Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_ResumablesCannotContainOnError)
End If
Select Case node.Kind
Case SyntaxKind.OnErrorGoToMinusOneStatement
Return New BoundOnErrorStatement(node, OnErrorStatementKind.GoToMinusOne, Nothing, Nothing)
Case SyntaxKind.OnErrorGoToZeroStatement
Return New BoundOnErrorStatement(node, OnErrorStatementKind.GoToZero, Nothing, Nothing)
Case SyntaxKind.OnErrorResumeNextStatement
Return New BoundOnErrorStatement(node, OnErrorStatementKind.ResumeNext, Nothing, Nothing)
Case SyntaxKind.OnErrorGoToLabelStatement
Dim onError = DirectCast(node, OnErrorGoToStatementSyntax)
Dim symbol As LabelSymbol = Nothing
Dim boundLabelExpression As BoundExpression = BindExpression(onError.Label, diagnostics)
If boundLabelExpression.Kind = BoundKind.Label Then
Dim boundLabel = DirectCast(boundLabelExpression, boundLabel)
symbol = boundLabel.Label
Return New BoundOnErrorStatement(node, symbol, boundLabel, hasErrors:=Not IsValidLabelForGoto(symbol, onError.Label, diagnostics))
Else
' if the bound label is e.g. a bad bound expression because of a non-existent label,
' make this a bad statement.
Return New BoundOnErrorStatement(node, Nothing, boundLabelExpression, hasErrors:=True)
End If
Case Else
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
End Function
Private Function BindStopStatement(stopStatementSyntax As StopOrEndStatementSyntax) As BoundStatement
Return New BoundStopStatement(stopStatementSyntax)
End Function
Private Function BindEndStatement(endStatementSyntax As StopOrEndStatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
If Not Compilation.Options.OutputKind.IsApplication() Then
ReportDiagnostic(diagnostics, endStatementSyntax, ERRID.ERR_EndDisallowedInDllProjects)
End If
Return New BoundEndStatement(endStatementSyntax)
End Function
End Class
End Namespace
|