/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb
5 329 строк
290 KB
Jan Jones
Implement control flow graph for Lock object feature (#71987)
15 фев 2024, 12:06
Не верифицирован
15 фев 2024, 12:06
f16bfb4
Код
Авторство
О чём код?
' 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