diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 8a8deefe24293b47a452bedfcc3eff3761c4e155..df4b443133a376fc7840b1a2b13c13c2ca0489a3 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -764,9 +764,9 @@ private InContainerBinder MakeNamespaceBinder(CSharpSyntaxNode node, NameSyntax public override Binder VisitCompilationUnit(CompilationUnitSyntax parent) { return VisitCompilationUnit( - parent, - inUsing: IsInUsing(parent), - inScript: InScript); + parent, + inUsing: IsInUsing(parent), + inScript: InScript); } internal InContainerBinder VisitCompilationUnit(CompilationUnitSyntax compilationUnit, bool inUsing, bool inScript) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e1f87af1ebf0606c990e144d407be6a00c1f9a46..59359e07ef798281b94bc1dbbd56557c6243ec51 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -852,8 +852,9 @@ private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, Diag options |= LookupOptions.MustNotBeMethodTypeParameter; } + var name = node.Identifier.ValueText; HashSet useSiteDiagnostics = null; - this.LookupSymbolsWithFallback(lookupResult, node.Identifier.ValueText, arity: arity, useSiteDiagnostics: ref useSiteDiagnostics, options: options); + this.LookupSymbolsWithFallback(lookupResult, name, arity: arity, useSiteDiagnostics: ref useSiteDiagnostics, options: options); diagnostics.Add(node, useSiteDiagnostics); if (lookupResult.Kind != LookupResultKind.Empty) @@ -862,7 +863,7 @@ private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, Diag bool isError = false; bool wasError; var members = ArrayBuilder.GetInstance(); - Symbol symbol = GetSymbolOrMethodOrPropertyGroup(lookupResult, node, node.Identifier.ValueText, node.Arity, members, diagnostics, out wasError); // reports diagnostics in result. + Symbol symbol = GetSymbolOrMethodOrPropertyGroup(lookupResult, node, name, node.Arity, members, diagnostics, out wasError); // reports diagnostics in result. isError |= wasError; @@ -876,7 +877,7 @@ private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, Diag typeArgumentList, typeArguments, receiver, - node.Identifier.ValueText, + name, members, lookupResult, receiver != null ? BoundMethodGroupFlags.HasImplicitReceiver : BoundMethodGroupFlags.None, @@ -919,15 +920,15 @@ private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, Diag } else if (IsJoinRangeVariableInLeftKey(node)) { - Error(diagnostics, ErrorCode.ERR_QueryOuterKey, node, node.Identifier.ValueText); + Error(diagnostics, ErrorCode.ERR_QueryOuterKey, node, name); } else if (IsInJoinRightKey(node)) { - Error(diagnostics, ErrorCode.ERR_QueryInnerKey, node, node.Identifier.ValueText); + Error(diagnostics, ErrorCode.ERR_QueryInnerKey, node, name); } else { - Error(diagnostics, ErrorCode.ERR_NameNotInContext, node, node.Identifier.ValueText); + Error(diagnostics, ErrorCode.ERR_NameNotInContext, node, name); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs index 43f80fcc3f13f9fcbbb9f7b31fda86e5013ce9df..e55a89998a9980b323fc7d908b65c17b888c880f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -106,7 +107,7 @@ internal struct ProcessedFieldInitializers firstDebugImports = parentBinder.ImportChain; } - parentBinder = new LocalScopeBinder(parentBinder).WithAdditionalFlagsAndContainingMemberOrLambda(parentBinder.Flags | BinderFlags.FieldInitializer, fieldSymbol); + parentBinder = new LocalScopeBinder(parentBinder).WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.FieldInitializer, fieldSymbol); BoundFieldInitializer boundInitializer = BindFieldInitializer(parentBinder, fieldSymbol, initializerNode, diagnostics); boundInitializers.Add(boundInitializer); @@ -137,6 +138,8 @@ internal struct ProcessedFieldInitializers // factory across siblings. Unfortunately, we cannot reuse the binder itself, because // individual fields might have their own binders (e.g. because of being declared unsafe). BinderFactory binderFactory = null; + // Label instances must be shared across all global statements. + ScriptLocalScopeBinder.Labels labels = null; for (int j = 0; j < siblingInitializers.Length; j++) { @@ -150,44 +153,47 @@ internal struct ProcessedFieldInitializers } var syntaxRef = initializer.Syntax; - Debug.Assert(syntaxRef.SyntaxTree.Options.Kind != SourceCodeKind.Regular); + var syntaxTree = syntaxRef.SyntaxTree; + Debug.Assert(syntaxTree.Options.Kind != SourceCodeKind.Regular); - var initializerNode = (CSharpSyntaxNode)syntaxRef.GetSyntax(); + var syntax = (CSharpSyntaxNode)syntaxRef.GetSyntax(); + var syntaxRoot = syntaxTree.GetCompilationUnitRoot(); if (binderFactory == null) { - binderFactory = compilation.GetBinderFactory(syntaxRef.SyntaxTree); + binderFactory = compilation.GetBinderFactory(syntaxTree); + labels = new ScriptLocalScopeBinder.Labels(scriptInitializer, syntaxRoot); } - Binder scriptClassBinder = binderFactory.GetBinder(initializerNode); - Debug.Assert(((ImplicitNamedTypeSymbol)scriptClassBinder.ContainingMemberOrLambda).IsScriptClass); + Binder scriptClassBinder = binderFactory.GetBinder(syntax); + Debug.Assert(((NamedTypeSymbol)scriptClassBinder.ContainingMemberOrLambda).IsScriptClass); if (firstDebugImports == null) { firstDebugImports = scriptClassBinder.ImportChain; } - Binder parentBinder = new ExecutableCodeBinder((CSharpSyntaxNode)syntaxRef.SyntaxTree.GetRoot(), scriptInitializer, scriptClassBinder); + Binder parentBinder = new ExecutableCodeBinder( + syntaxRoot, + scriptInitializer, + new ScriptLocalScopeBinder(labels, scriptClassBinder)); BoundInitializer boundInitializer; if ((object)fieldSymbol != null) { boundInitializer = BindFieldInitializer( - new LocalScopeBinder(parentBinder).WithAdditionalFlagsAndContainingMemberOrLambda(parentBinder.Flags | BinderFlags.FieldInitializer, fieldSymbol), + parentBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.FieldInitializer, fieldSymbol), fieldSymbol, - (EqualsValueClauseSyntax)initializerNode, + (EqualsValueClauseSyntax)syntax, diagnostics); } - else if (initializerNode.Kind() == SyntaxKind.LabeledStatement) - { - // TODO: labels in interactive - var boundStatement = new BoundBadStatement(initializerNode, ImmutableArray.Empty, true); - boundInitializer = new BoundGlobalStatementInitializer(initializerNode, boundStatement); - } else { - var collisionDetector = new LocalScopeBinder(parentBinder); - boundInitializer = BindGlobalStatement(collisionDetector, scriptInitializer, (StatementSyntax)initializerNode, diagnostics, + boundInitializer = BindGlobalStatement( + parentBinder, + scriptInitializer, + (StatementSyntax)syntax, + diagnostics, isLast: i == initializers.Length - 1 && j == siblingInitializers.Length - 1); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index bf518743490c15dae1d3a4efc588864429748039..8b8a9d55d21c835bce06d70488a88e9b3421fad2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -53,31 +53,36 @@ internal void LookupExtensionMethods(LookupResult result, string name, int arity /// /// Makes a second attempt if the results are not viable, in order to produce more detailed failure information (symbols and diagnostics). /// - private void LookupSymbolsWithFallback(LookupResult result, string name, int arity, ref HashSet useSiteDiagnostics, ConsList basesBeingResolved = null, LookupOptions options = LookupOptions.Default) + private Binder LookupSymbolsWithFallback(LookupResult result, string name, int arity, ref HashSet useSiteDiagnostics, ConsList basesBeingResolved = null, LookupOptions options = LookupOptions.Default) { Debug.Assert(options.AreValid()); // don't create diagnosis instances unless lookup fails - this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: false, useSiteDiagnostics: ref useSiteDiagnostics); + var binder = this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: false, useSiteDiagnostics: ref useSiteDiagnostics); + Debug.Assert((binder != null) || result.IsClear); + if (result.Kind != LookupResultKind.Viable && result.Kind != LookupResultKind.Empty) { result.Clear(); // retry to get diagnosis - this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: true, useSiteDiagnostics: ref useSiteDiagnostics); + var otherBinder = this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: true, useSiteDiagnostics: ref useSiteDiagnostics); + Debug.Assert(binder == otherBinder); } Debug.Assert(result.IsMultiViable || result.IsClear || result.Error != null); + return binder; } - private void LookupSymbolsInternal( + private Binder LookupSymbolsInternal( LookupResult result, string name, int arity, ConsList basesBeingResolved, LookupOptions options, bool diagnose, ref HashSet useSiteDiagnostics) { Debug.Assert(result.IsClear); Debug.Assert(options.AreValid()); + Binder binder = null; for (var scope = this; scope != null && !result.IsMultiViable; scope = scope.Next) { - if (!result.IsClear) + if (binder != null) { var tmp = LookupResult.GetInstance(); scope.LookupSymbolsInSingleBinder(tmp, name, arity, basesBeingResolved, options, this, diagnose, ref useSiteDiagnostics); @@ -87,8 +92,13 @@ private void LookupSymbolsWithFallback(LookupResult result, string name, int ari else { scope.LookupSymbolsInSingleBinder(result, name, arity, basesBeingResolved, options, this, diagnose, ref useSiteDiagnostics); + if (!result.IsClear) + { + binder = scope; + } } } + return binder; } internal virtual void LookupSymbolsInSingleBinder( diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 3f6a3114ab6be394d9c9f09decf81d0286d0bedb..248b77871003e61fffcb446e2b86ac910db0d2e7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -342,7 +342,7 @@ private BoundLabeledStatement BindLabeled(LabeledStatementSyntax node, Diagnosti var result = LookupResult.GetInstance(); HashSet useSiteDiagnostics = null; - this.LookupSymbolsWithFallback(result, node.Identifier.ValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics, options: LookupOptions.LabelsOnly); + var binder = this.LookupSymbolsWithFallback(result, node.Identifier.ValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics, options: LookupOptions.LabelsOnly); // result.Symbols can be empty in some malformed code, e.g. when a labeled statement is used an embedded statement in an if or foreach statement // In this case we create new label symbol on the fly, and an error is reported by parser @@ -357,14 +357,18 @@ private BoundLabeledStatement BindLabeled(LabeledStatementSyntax node, Diagnosti } // check to see if this label (illegally) hides a label from an enclosing scope - result.Clear(); - this.Next.LookupSymbolsWithFallback(result, node.Identifier.ValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics, options: LookupOptions.LabelsOnly); - if (result.IsMultiViable) + if (binder != null) { - // The label '{0}' shadows another label by the same name in a contained scope - Error(diagnostics, ErrorCode.ERR_LabelShadow, node.Identifier, node.Identifier.ValueText); - hasError = true; + result.Clear(); + binder.Next.LookupSymbolsWithFallback(result, node.Identifier.ValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics, options: LookupOptions.LabelsOnly); + if (result.IsMultiViable) + { + // The label '{0}' shadows another label by the same name in a contained scope + Error(diagnostics, ErrorCode.ERR_LabelShadow, node.Identifier, node.Identifier.ValueText); + hasError = true; + } } + diagnostics.Add(node, useSiteDiagnostics); result.Free(); diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs index d0f5a36b9b3679027e232d6ab8a2e816be092092..bfbbee4e2ae15e8b2944db626d236367a412f4b1 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; diff --git a/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs b/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs index 1bebe1f21d9dd57ec9cf423b6240743e0562b27c..64472c968f0d2b3241c9403ced1d3745d526df4e 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs @@ -159,22 +159,25 @@ protected SourceLocalSymbol MakeLocal(VariableDeclarationSyntax declaration, Var protected void BuildLabels(SyntaxList statements, ref ArrayBuilder labels) { var containingMethod = (MethodSymbol)this.ContainingMemberOrLambda; - foreach (var statement in statements) { - var stmt = statement; - while (stmt.Kind() == SyntaxKind.LabeledStatement) - { - var labeledStatement = (LabeledStatementSyntax)stmt; - if (labels == null) - { - labels = ArrayBuilder.GetInstance(); - } + BuildLabels(containingMethod, statement, ref labels); + } + } - var labelSymbol = new SourceLabelSymbol(containingMethod, labeledStatement.Identifier); - labels.Add(labelSymbol); - stmt = labeledStatement.Statement; + internal static void BuildLabels(MethodSymbol containingMethod, StatementSyntax statement, ref ArrayBuilder labels) + { + while (statement.Kind() == SyntaxKind.LabeledStatement) + { + var labeledStatement = (LabeledStatementSyntax)statement; + if (labels == null) + { + labels = ArrayBuilder.GetInstance(); } + + var labelSymbol = new SourceLabelSymbol(containingMethod, labeledStatement.Identifier); + labels.Add(labelSymbol); + statement = labeledStatement.Statement; } } diff --git a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs index 9dbfa3d00177c12ff5cd00c49ebef58d4d927590..4953eb96222c8d939f337afa07c19d96425b1973 100644 --- a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs @@ -1,16 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.CSharp { - internal class NameofBinder : Binder + internal sealed class NameofBinder : Binder { private readonly SyntaxNode _nameofArgument; diff --git a/src/Compilers/CSharp/Portable/Binder/ScriptLocalScopeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ScriptLocalScopeBinder.cs new file mode 100644 index 0000000000000000000000000000000000000000..6fbea6a7ee480c4e05b4421ff85dd1757d15cb14 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/ScriptLocalScopeBinder.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal sealed class ScriptLocalScopeBinder : LocalScopeBinder + { + private readonly Labels _labels; + + internal ScriptLocalScopeBinder(Labels labels, Binder next) : base(next) + { + _labels = labels; + } + + internal override Symbol ContainingMemberOrLambda + { + get { return _labels.ScriptInitializer; } + } + + protected override ImmutableArray BuildLabels() + { + return _labels.GetLabels(); + } + + // Labels potentially shared across multiple ScriptLocalScopeBinder instances. + new internal sealed class Labels + { + private readonly SynthesizedInteractiveInitializerMethod _scriptInitializer; + private readonly CompilationUnitSyntax _syntax; + private ImmutableArray _lazyLabels; + + internal Labels(SynthesizedInteractiveInitializerMethod scriptInitializer, CompilationUnitSyntax syntax) + { + _scriptInitializer = scriptInitializer; + _syntax = syntax; + } + + internal SynthesizedInteractiveInitializerMethod ScriptInitializer + { + get { return _scriptInitializer; } + } + + internal ImmutableArray GetLabels() + { + if (_lazyLabels == null) + { + ImmutableInterlocked.InterlockedInitialize(ref _lazyLabels, GetLabels(_scriptInitializer, _syntax)); + } + return _lazyLabels; + } + + private static ImmutableArray GetLabels(SynthesizedInteractiveInitializerMethod scriptInitializer, CompilationUnitSyntax syntax) + { + var builder = ArrayBuilder.GetInstance(); + foreach (var member in syntax.Members) + { + if (member.Kind() != SyntaxKind.GlobalStatement) + { + continue; + } + LocalScopeBinder.BuildLabels(scriptInitializer, ((GlobalStatementSyntax)member).Statement, ref builder); + } + return builder.ToImmutableAndFree(); + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj index cc8389b53b5ca6956564671f956c6c9d9feaad07..3381a3c36131081a9f6d72f709d5278c28505004 100644 --- a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj +++ b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj @@ -141,6 +141,7 @@ + diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 1aead3df0a228842168981efcb6932d77ff9c1ca..2f2e83f2bdae2b3798289f43e291b5a7a7e27b81 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -1368,10 +1368,11 @@ private void CheckModelAndSyntaxNodeToSpeculate(CSharpSyntaxNode syntax) container = baseType; } - if (!binder.IsInMethodBody && (options & LookupOptions.NamespacesOrTypesOnly) == 0) + if (!binder.IsInMethodBody && + (options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly | LookupOptions.LabelsOnly)) == 0) { - // Method type parameters are not in scope outside a method body unless - // the position is either: + // Method type parameters are not in scope outside a method + // body unless the position is either: // a) in a type-only context inside an expression, or // b) inside of an XML name attribute in an XML doc comment. var parentExpr = token.Parent as ExpressionSyntax; diff --git a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs index 856a10eca667e387d5836f6cdf215f7293c3305e..5f7afa49ff4626b39ad6a49fe3410f997f0762d3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs @@ -9,15 +9,13 @@ namespace Microsoft.CodeAnalysis.CSharp { internal sealed class MethodBodySemanticModel : MemberSemanticModel { - private DiagnosticBag _ignoredDiagnostics = new DiagnosticBag(); - private MethodBodySemanticModel(CSharpCompilation compilation, Symbol owner, Binder rootBinder, CSharpSyntaxNode syntax, SyntaxTreeSemanticModel parentSemanticModelOpt = null, int speculatedPosition = 0) : base(compilation, syntax, owner, rootBinder, parentSemanticModelOpt, speculatedPosition) { Debug.Assert((object)owner != null); Debug.Assert(owner.Kind == SymbolKind.Method); Debug.Assert(syntax != null); - Debug.Assert(owner.ContainingType.IsScriptClass || syntax.Kind() != SyntaxKind.CompilationUnit); + Debug.Assert(syntax.Kind() != SyntaxKind.CompilationUnit); } /// @@ -29,16 +27,6 @@ internal static MethodBodySemanticModel Create(CSharpCompilation compilation, Me return new MethodBodySemanticModel(compilation, owner, executableCodeBinder, syntax); } - /// - /// Creates a SemanticModel for an ArrowExpressionClause, which includes - /// an ExecutableCodeBinder and a ScopedExpressionBinder. - /// - internal static MethodBodySemanticModel Create(CSharpCompilation compilation, MethodSymbol owner, Binder rootBinder, ArrowExpressionClauseSyntax syntax) - { - Binder binder = new ExecutableCodeBinder(syntax, owner, rootBinder); - return new MethodBodySemanticModel(compilation, owner, binder, syntax); - } - internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, DiagnosticBag diagnostics) { if (node.Kind() == SyntaxKind.ArrowExpressionClause) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 5a0c530ec750ba41d1978ffcbba8166477ba8553..575fe9c795a07e51f1b2e098d22a5b02c05f777e 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -26,6 +25,7 @@ internal partial class SyntaxTreeSemanticModel : CSharpSemanticModel private readonly BinderFactory _binderFactory; private Func _createMemberModelFunction; private readonly bool _ignoresAccessibility; + private ScriptLocalScopeBinder.Labels _globalStatementLabels; private static readonly Func s_isMemberDeclarationFunction = IsMemberDeclaration; @@ -979,14 +979,27 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node) case SyntaxKind.GlobalStatement: { Debug.Assert(!this.IsRegularCSharp); + var parent = node.Parent; // TODO (tomat): handle misplaced global statements - if (node.Parent.Kind() == SyntaxKind.CompilationUnit) + if (parent.Kind() == SyntaxKind.CompilationUnit) { - var scriptConstructor = this.Compilation.ScriptClass.InstanceConstructors.First(); + var scriptInitializer = _compilation.ScriptClass.GetScriptInitializer(); + Debug.Assert((object)scriptInitializer != null); + if ((object)scriptInitializer == null) + { + return null; + } + + // Share labels across all global statements. + if (_globalStatementLabels == null) + { + Interlocked.CompareExchange(ref _globalStatementLabels, new ScriptLocalScopeBinder.Labels(scriptInitializer, (CompilationUnitSyntax)parent), null); + } + return MethodBodySemanticModel.Create( this.Compilation, - scriptConstructor, - outer, + scriptInitializer, + new ScriptLocalScopeBinder(_globalStatementLabels, outer), node); } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index b0e95d068095b9ba1df2f704bfc89d3ecc54c55e..584444acf257d5d7673804e65b161a528d0f7416 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -1,10 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs index b6b27e460663a2f4cc3a978229ae985db48be0fb..12cb20d3b69410fd70f6f5cce6be827afa05ae7f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs @@ -3,11 +3,12 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - public class GotoStatementTest : EmitMetadataTestBase + public class GotoTests : EmitMetadataTestBase { [Fact] public void Goto() @@ -804,34 +805,256 @@ public static int Main() CompileAndVerify(text, expectedOutput: "Catch"); } - [Fact(Skip = "3712"), WorkItem(3712)] - public void Goto_Script() + [Fact] + public void OutOfScriptBlock() { - string source = @" -using System; + string source = +@"bool b = true; +L0: ; +{ + { + System.Console.WriteLine(b); + if (b) b = !b; + else goto L1; + goto L0; + } + L1: ; +}"; + string expectedOutput = +@"True +False"; + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script, options: TestOptions.DebugExe); + CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: false); + } -Console.WriteLine(""a""); -goto C; -Console.Write(""you won't see me""); -C: Console.WriteLine(""b""); -"; - string expectedOutput = @"a -b -"; - CompileAndVerify(source, parseOptions: new CSharpParseOptions(kind: SourceCodeKind.Script), expectedOutput: expectedOutput); + [Fact] + public void IntoScriptBlock() + { + string source = +@"goto L0; +{ + L0: goto L1; +} +{ + L1: ; +}"; + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + // (1,6): error CS0159: No such label 'L0' within the scope of the goto statement + // goto L0; + Diagnostic(ErrorCode.ERR_LabelNotFound, "L0").WithArguments("L0").WithLocation(1, 6), + // (3,14): error CS0159: No such label 'L1' within the scope of the goto statement + // L0: goto L1; + Diagnostic(ErrorCode.ERR_LabelNotFound, "L1").WithArguments("L1").WithLocation(3, 14), + // (3,5): warning CS0164: This label has not been referenced + // L0: goto L1; + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "L0").WithLocation(3, 5), + // (6,5): warning CS0164: This label has not been referenced + // L1: ; + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "L1").WithLocation(6, 5)); + } + + [Fact] + public void AcrossScriptDeclarations() + { + string source = +@"int P { get; } = G(""P""); +L: +int F = G(""F""); +int Q { get; } = G(""Q""); +static int x = 2; +static int G(string s) +{ + System.Console.WriteLine(""{0}: {1}"", x, s); + x++; + return x; +} +if (Q < 4) goto L;"; + string expectedOutput = +@"2: P +3: F +4: Q"; + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script, options: TestOptions.DebugExe); + CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: false); + } + + [Fact] + public void AcrossSubmissions() + { + var references = new[] { MscorlibRef_v4_0_30316_17626, SystemCoreRef }; + var source0 = +@"bool b = false; +L: ; +if (b) +{ + goto L; +}"; + var source1 = +@"goto L;"; + var s0 = CSharpCompilation.CreateScriptCompilation("s0.dll", SyntaxFactory.ParseSyntaxTree(source0, options: TestOptions.Script), references); + s0.VerifyDiagnostics(); + var s1 = CSharpCompilation.CreateScriptCompilation("s1.dll", SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.Script), references, previousScriptCompilation: s0); + s1.VerifyDiagnostics( + // (1,6): error CS0159: No such label 'L' within the scope of the goto statement + // goto L; + Diagnostic(ErrorCode.ERR_LabelNotFound, "L").WithArguments("L").WithLocation(1, 6)); + } + + [Fact] + public void OutOfScriptMethod() + { + string source = +@"static void F(bool b) +{ + if (b) goto L; +} +L: +F(true);"; + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script); + compilation.VerifyDiagnostics( + // (5,1): warning CS0164: This label has not been referenced + // L: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "L").WithLocation(5, 1), + // (3,17): error CS0159: No such label 'L' within the scope of the goto statement + // if (b) goto L; + Diagnostic(ErrorCode.ERR_LabelNotFound, "L").WithArguments("L").WithLocation(3, 17)); + } + + [Fact] + public void IntoScriptMethod() + { + string source = +@"static void F() +{ +L: + return; +} +goto L;"; + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script); + compilation.VerifyDiagnostics( + // (6,6): error CS0159: No such label 'L' within the scope of the goto statement + // goto L; + Diagnostic(ErrorCode.ERR_LabelNotFound, "L").WithArguments("L").WithLocation(6, 6), + // (3,1): warning CS0164: This label has not been referenced + // L: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "L").WithLocation(3, 1)); + } + + [Fact] + public void InScriptSwitch() + { + string source = +@"int x = 3; +switch (x) +{ +case 1: + break; +case 2: + System.Console.WriteLine(x); + break; +default: + goto case 2; +}"; + string expectedOutput = +@"3"; + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script, options: TestOptions.DebugExe); + CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: false); + } + + [Fact] + public void DuplicateLabelInScript() + { + string source = +@"bool b = false; +L: ; +if (b) +{ + goto L; +} +else +{ + b = !b; + if (b) goto L; +L: ; +}"; + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script, options: TestOptions.DebugExe); + compilation.VerifyDiagnostics( + // (11,1): error CS0158: The label 'L' shadows another label by the same name in a contained scope + // L: ; + Diagnostic(ErrorCode.ERR_LabelShadow, "L").WithArguments("L").WithLocation(11, 1)); + } + + [Fact] + public void DuplicateLabelInSeparateSubmissions() + { + var references = new[] { MscorlibRef_v4_0_30316_17626, SystemCoreRef }; + var source0 = +@"bool b = false; +L: ; +if (b) +{ + goto L; +}"; + var source1 = +@"if (!b) +{ + b = !b; + if (b) goto L; +L: ; +}"; + var s0 = CSharpCompilation.CreateScriptCompilation("s0.dll", SyntaxFactory.ParseSyntaxTree(source0, options: TestOptions.Script), references); + s0.VerifyDiagnostics(); + var s1 = CSharpCompilation.CreateScriptCompilation("s1.dll", SyntaxFactory.ParseSyntaxTree(source1, options: TestOptions.Script), references, previousScriptCompilation: s0); + s1.VerifyDiagnostics(); + } + + [Fact] + public void LoadedFile() + { + var sourceA = +@"goto A; +A: goto B;"; + var sourceB = +@"#load ""a.csx"" +goto B; +B: goto A;"; + var resolver = TestSourceReferenceResolver.Create(KeyValuePair.Create("a.csx", sourceA)); + var options = TestOptions.DebugDll.WithSourceReferenceResolver(resolver); + var compilation = CreateCompilationWithMscorlib45(sourceB, options: options, parseOptions: TestOptions.Script); + compilation.GetDiagnostics().Verify( + // a.csx(2,9): error CS0159: No such label 'B' within the scope of the goto statement + // A: goto B; + Diagnostic(ErrorCode.ERR_LabelNotFound, "B").WithArguments("B").WithLocation(2, 9), + // (3,9): error CS0159: No such label 'A' within the scope of the goto statement + // B: goto A; + Diagnostic(ErrorCode.ERR_LabelNotFound, "A").WithArguments("A").WithLocation(3, 9)); + } + + [Fact, WorkItem(3712)] + public void Label_GetDeclaredSymbol_Script() + { + string source = +@"L0: goto L1; +static void F() { } +L1: goto L0;"; + var tree = Parse(source, options: TestOptions.Script); + var model = CreateCompilationWithMscorlib45(new[] { tree }).GetSemanticModel(tree, ignoreAccessibility: false); + var label = (LabeledStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.LabeledStatement); + var symbol = model.GetDeclaredSymbol(label); + Assert.Equal("L0", symbol.Name); } - [Fact(Skip = "3712"), WorkItem(3712)] + [Fact, WorkItem(3712)] public void Label_GetDeclaredSymbol_Error_Script() { string source = @" C: \a\b\ "; - var tree = Parse(source, options: new CSharpParseOptions(kind: SourceCodeKind.Script)); + var tree = Parse(source, options: TestOptions.Script); var model = CreateCompilationWithMscorlib45(new[] { tree }).GetSemanticModel(tree, ignoreAccessibility: false); var label = (LabeledStatementSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.LabeledStatement); var symbol = model.GetDeclaredSymbol(label); - // TODO: Add some verification for symbol... + Assert.Equal("C", symbol.Name); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs index e24379acac5060d2fb1ad28d2e0bab648f1eed0f..eb41a233de9b03f6d3fba8eb14da3370fb81c4f9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs @@ -298,6 +298,38 @@ public void LabelLookup() Assert.Empty(model.LookupLabels(source.Length - 1)); // Used to assert. } + [Fact] + public void Labels() + { + string source = +@"L0: ; +goto L0;"; + var tree = Parse(source, options: TestOptions.Script); + var model = CreateCompilationWithMscorlib45(new[] { tree }).GetSemanticModel(tree, ignoreAccessibility: false); + var root = tree.GetCompilationUnitRoot(); + var statements = root.ChildNodes().Select(n => ((GlobalStatementSyntax)n).Statement).ToArray(); + var symbol0 = model.GetDeclaredSymbol((LabeledStatementSyntax)statements[0]); + Assert.NotNull(symbol0); + var symbol1 = model.GetSymbolInfo(((GotoStatementSyntax)statements[1]).Expression).Symbol; + Assert.Same(symbol0, symbol1); + } + + [Fact] + public void Variables() + { + string source = +@"int x = 1; +object y = x;"; + var tree = Parse(source, options: TestOptions.Script); + var model = CreateCompilationWithMscorlib45(new[] { tree }).GetSemanticModel(tree, ignoreAccessibility: false); + var root = tree.GetCompilationUnitRoot(); + var declarations = root.ChildNodes().Select(n => ((FieldDeclarationSyntax)n).Declaration.Variables[0]).ToArray(); + var symbol0 = model.GetDeclaredSymbol(declarations[0]); + Assert.NotNull(symbol0); + var symbol1 = model.GetSymbolInfo(declarations[1].Initializer.Value).Symbol; + Assert.Same(symbol0, symbol1); + } + [WorkItem(543890)] [Fact] public void ThisIndexerAccessInSubmission() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs index 227e9204505b0f6040548676f794987c352e90e4..e31c51c5d85e17fc1ace1d872dbac3c22cea3af1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs @@ -2115,7 +2115,7 @@ static int Main() } [Fact] - public void SwitchFallOut_Script1() + public void SwitchFallOut_Script() { var source = @"using System; @@ -2137,7 +2137,7 @@ public void SwitchFallOut_Script1() } [Fact] - public void SwitchFallOut_Script2() + public void SwitchFallOut_Submission() { var source = @"using System; diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs index 98ff56286286eaaad183a2fd1cf3996bc48dc5d3..d3f4deefd9eabf30df4c351bb9dc43fcc894c64f 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Xunit; using Roslyn.Test.Utilities; +using Roslyn.Utilities; +using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -39,8 +39,8 @@ public void MissingFile() public void FileWithErrors() { var code = "#load \"a.csx\""; - var resolver = CreateResolver( - Script("a.csx", @" + var resolver = TestSourceReferenceResolver.Create( + KeyValuePair.Create("a.csx", @" #load ""b.csx"" asdf();")); var options = TestOptions.DebugDll.WithSourceReferenceResolver(resolver); @@ -72,20 +72,5 @@ public void NoSourceReferenceResolver() // #load "test" Diagnostic(ErrorCode.ERR_SourceFileReferencesNotSupported, @"#load ""test""").WithLocation(1, 1)); } - - private static SourceReferenceResolver CreateResolver(params KeyValuePair[] scripts) - { - var sources = new Dictionary(); - foreach (var script in scripts) - { - sources.Add(script.Key, script.Value); - } - return TestSourceReferenceResolver.Create(sources); - } - - private static KeyValuePair Script(string path, string source) - { - return new KeyValuePair(path, source); - } } } diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenScriptTests.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenScriptTests.vb index c69d14009019adfd235996a789930b9a54c99a20..58bbf3981b459e3191a78e89c09e4334af8227ba 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenScriptTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenScriptTests.vb @@ -398,6 +398,20 @@ System.Console.Write("complete") }") End Sub + + Public Sub ScriptEntryPoint_MissingMethods() + Dim comp = CreateCompilationWithMscorlib( + + + , + parseOptions:=TestOptions.Script, + options:=TestOptions.DebugExe) + comp.VerifyDiagnostics( + Diagnostic(ERRID.ERR_MissingRuntimeHelper).WithArguments("Task.GetAwaiter").WithLocation(1, 1)) + End Sub + End Class End Namespace diff --git a/src/Test/Utilities/Desktop/TestSourceReferenceResolver.cs b/src/Test/Utilities/Desktop/TestSourceReferenceResolver.cs index 2df85330249dd3b3d2ae89e153c0d13ed5aa5ca7..956f4c68d9d9e5bf8f741b88d414c1468a74b9ae 100644 --- a/src/Test/Utilities/Desktop/TestSourceReferenceResolver.cs +++ b/src/Test/Utilities/Desktop/TestSourceReferenceResolver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Text; using Microsoft.CodeAnalysis; @@ -12,6 +13,11 @@ public sealed class TestSourceReferenceResolver : SourceReferenceResolver { public static readonly SourceReferenceResolver Default = new TestSourceReferenceResolver(sources: null); + public static SourceReferenceResolver Create(params KeyValuePair[] sources) + { + return TestSourceReferenceResolver.Create(sources.ToDictionary(p => p.Key, p => p.Value)); + } + public static SourceReferenceResolver Create(Dictionary sources = null) { return (sources == null || sources.Count == 0) ? Default : new TestSourceReferenceResolver(sources);