diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 855e321067ac9454807f7004d63ef8bdcaaa1b5d..18bbf8a8ddb0f4e66af761fa61c852a784bbc546 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -758,7 +758,7 @@ private InContainerBinder MakeNamespaceBinder(CSharpSyntaxNode node, NameSyntax NamespaceOrTypeSymbol container = outer.Container; NamespaceSymbol ns = ((NamespaceSymbol)container).GetNestedNamespace(name); if ((object)ns == null) return outer; - return new InContainerBinder(ns, outer, node, allowStaticClassUsings: ((CSharpParseOptions)syntaxTree.Options).LanguageVersion >= LanguageVersion.CSharp6, inUsing: inUsing); + return new InContainerBinder(ns, outer, node, inUsing: inUsing); } public override Binder VisitCompilationUnit(CompilationUnitSyntax parent) @@ -803,15 +803,7 @@ internal InContainerBinder VisitCompilationUnit(CompilationUnitSyntax compilatio // + script class members & top-level using aliases // - if (compilation.GlobalImports.Usings.Length > 0) - { - result = new UsingsBinder(result, compilation.GlobalImports.Usings); - } - - if (compilation.IsSubmission) - { - result = new InteractiveUsingsBinder(result); - } + result = new InContainerBinder(container: null, next: result, imports: compilation.GlobalImports); result = new InContainerBinder(compilation.GlobalNamespace, result); @@ -832,7 +824,7 @@ internal InContainerBinder VisitCompilationUnit(CompilationUnitSyntax compilatio importsContainer = compilation.GlobalNamespace; } - result = new InContainerBinder(importsContainer, result, compilationUnit, allowStaticClassUsings: ((CSharpParseOptions)syntaxTree.Options).LanguageVersion >= LanguageVersion.CSharp6, inUsing: inUsing); + result = new InContainerBinder(importsContainer, result, compilationUnit, inUsing: inUsing); binderCache.TryAdd(key, result); } diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs index 62136e76cda20a000cbd751e297bdfad16fc02fe..30ccb97aec2c3e9d0f62c7024c0c37869943853f 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs @@ -154,26 +154,5 @@ internal InContainerBinder GetImportsBinder(CSharpSyntaxNode unit) return null; } } - - internal InteractiveUsingsBinder GetInteractiveUsingsBinder() - { - Debug.Assert(_compilation.IsSubmission); - - BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Position = 0; - - Binder binder = visitor.VisitCompilationUnit(_syntaxTree.GetCompilationUnitRoot(), inUsing: false, inScript: true); - _binderFactoryVisitorPool.Free(visitor); - - if (_compilation.HostObjectType != null) - { - binder = binder.Next; - Debug.Assert(binder is HostObjectModelBinder); - } - - Debug.Assert(binder.Next is InContainerBinder); - - return (InteractiveUsingsBinder)binder.Next.Next; - } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 0e38c5e44482cbd350c1eda4cd27fc088bc25ca3..448c0936de97f68516f8dacb9b7c2f6ee5161168 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -244,29 +244,37 @@ private void LookupMembersInSubmissions(LookupResult result, TypeSymbol submissi { submissionSymbols.Clear(); + var submissionImports = submission.SubmissionImports; + if (submission != Compilation) + { + submissionImports = Imports.ExpandPreviousSubmissionImports(submissionImports, Compilation); + } + // If a viable using alias and a matching member are both defined in the submission an error is reported elsewhere. // Ignore the member in such case. if ((options & LookupOptions.NamespaceAliasesOnly) == 0 && (object)submission.ScriptClass != null) { LookupMembersWithoutInheritance(submissionSymbols, submission.ScriptClass, name, arity, options, originalBinder, submissionClass, diagnose, ref useSiteDiagnostics, basesBeingResolved); - } - // using aliases: - Imports imports = submission.GetSubmissionImports(); - if (submissionSymbols.Symbols.Count > 0 && imports.IsUsingAlias(name, this.IsSemanticModelBinder)) - { - // using alias is ambiguous with another definition within the same submission iff the other definition is a 0-ary type or a non-type: - Symbol existingDefinition = submissionSymbols.Symbols.First(); - if (existingDefinition.Kind == SymbolKind.NamedType && arity == 0 || existingDefinition.Kind != SymbolKind.NamedType) + if (submissionSymbols.IsMultiViable && submissionImports.IsUsingAlias(name, originalBinder.IsSemanticModelBinder)) { - CSDiagnosticInfo diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_ConflictingAliasAndDefinition, name, existingDefinition.GetKindText()); - var error = new ExtendedErrorTypeSymbol((NamespaceOrTypeSymbol)null, name, arity, diagInfo, unreported: true); - result.SetFrom(LookupResult.Good(error)); // force lookup to be done w/ error symbol as result - break; + // using alias is ambiguous with another definition within the same submission iff the other definition is a 0-ary type or a non-type: + Symbol existingDefinition = submissionSymbols.Symbols.First(); + if (existingDefinition.Kind != SymbolKind.NamedType || arity == 0) + { + CSDiagnosticInfo diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_ConflictingAliasAndDefinition, name, existingDefinition.GetKindText()); + var error = new ExtendedErrorTypeSymbol((NamespaceOrTypeSymbol)null, name, arity, diagInfo, unreported: true); + result.SetFrom(LookupResult.Good(error)); // force lookup to be done w/ error symbol as result + break; + } } } - imports.LookupSymbolInAliases(originalBinder, submissionSymbols, name, arity, basesBeingResolved, options, diagnose, ref useSiteDiagnostics); + if (!submissionSymbols.IsMultiViable) + { + // next try using aliases or symbols in imported namespaces + submissionImports.LookupSymbol(originalBinder, submissionSymbols, name, arity, basesBeingResolved, options, diagnose, ref useSiteDiagnostics); + } if (lookingForOverloadsOfKind == null) { @@ -1491,13 +1499,25 @@ private void AddMemberLookupSymbolsInfoInSubmissions(LookupSymbolsInfo result, T { // TODO: we need tests // TODO: optimize lookup (there might be many interactions in the chain) + for (CSharpCompilation submission = Compilation; submission != null; submission = submission.PreviousSubmission) { - submission.GetSubmissionImports().AddLookupSymbolsInfoInAliases(this, result, options); if ((object)submission.ScriptClass != null) { AddMemberLookupSymbolsInfoWithoutInheritance(result, submission.ScriptClass, options, originalBinder, scriptClass); } + + // If we are looking only for labels we do not need to search through the imports. + // Submission imports are handled by AddMemberLookupSymbolsInfo (above). + if ((options & LookupOptions.LabelsOnly) == 0) + { + var submissionImports = submission.SubmissionImports; + if (submission != Compilation) + { + submissionImports = Imports.ExpandPreviousSubmissionImports(submissionImports, Compilation); + } + submissionImports.AddLookupSymbolsInfo(result, options, originalBinder); + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Imports.cs b/src/Compilers/CSharp/Portable/Binder/Imports.cs index 8ed60f94d6e5518d05fb23619d2eb62bc520f88e..1bb214309f941c7e25b39ee3722176a49c783e07 100644 --- a/src/Compilers/CSharp/Portable/Binder/Imports.cs +++ b/src/Compilers/CSharp/Portable/Binder/Imports.cs @@ -251,16 +251,113 @@ public static Imports FromGlobalUsings(CSharpCompilation compilation) } } - uniqueUsings.Free(); - if (diagnostics.IsEmptyWithoutResolution) { diagnostics = null; } + var previousSubmissionImports = compilation.PreviousSubmission?.GlobalImports; + if (previousSubmissionImports != null) + { + // Currently, only usings are supported. + Debug.Assert(previousSubmissionImports.UsingAliases.IsEmpty); + Debug.Assert(previousSubmissionImports.ExternAliases.IsEmpty); + + var expandedImports = ExpandPreviousSubmissionImports(previousSubmissionImports, compilation); + + foreach (var previousUsing in expandedImports.Usings) + { + if (uniqueUsings.Add(previousUsing.NamespaceOrType)) + { + boundUsings.Add(previousUsing); + } + } + } + + uniqueUsings.Free(); + return new Imports(compilation, ImmutableDictionary.Empty, boundUsings.ToImmutableAndFree(), ImmutableArray.Empty, diagnostics); } + // TODO (https://github.com/dotnet/roslyn/issues/5517): skip namespace expansion if references haven't changed. + internal static Imports ExpandPreviousSubmissionImports(Imports previousSubmissionImports, CSharpCompilation newSubmission) + { + Debug.Assert(previousSubmissionImports != null); + Debug.Assert(previousSubmissionImports._compilation?.IsSubmission != false); + Debug.Assert(newSubmission.IsSubmission); + + var expandedGlobalNamespace = newSubmission.GlobalNamespace; + + var expandedAliases = ImmutableDictionary.Empty; + if (!previousSubmissionImports.UsingAliases.IsEmpty) + { + var expandedAliasesBuilder = ImmutableDictionary.CreateBuilder(); + foreach (var pair in previousSubmissionImports.UsingAliases) + { + var name = pair.Key; + var directive = pair.Value; + expandedAliasesBuilder.Add(name, new AliasAndUsingDirective(directive.Alias.ToNewSubmission(newSubmission), directive.UsingDirective)); + } + expandedAliases = expandedAliasesBuilder.ToImmutable(); + } + + var expandedUsings = ImmutableArray.Empty; + if (!previousSubmissionImports.Usings.IsEmpty) + { + var expandedUsingsBuilder = ArrayBuilder.GetInstance(previousSubmissionImports.Usings.Length); + foreach (var previousUsing in previousSubmissionImports.Usings) + { + var previousTarget = previousUsing.NamespaceOrType; + if (previousTarget.IsType) + { + expandedUsingsBuilder.Add(previousUsing); + } + else + { + var expandedNamespace = ExpandPreviousSubmissionNamespace((NamespaceSymbol)previousTarget, expandedGlobalNamespace); + expandedUsingsBuilder.Add(new NamespaceOrTypeAndUsingDirective(expandedNamespace, previousUsing.UsingDirective)); + } + } + expandedUsings = expandedUsingsBuilder.ToImmutableAndFree(); + } + + return new Imports( + newSubmission, + expandedAliases, + expandedUsings, + previousSubmissionImports.ExternAliases, + diagnostics: null); + } + + internal static NamespaceSymbol ExpandPreviousSubmissionNamespace(NamespaceSymbol originalNamespace, NamespaceSymbol expandedGlobalNamespace) + { + // Soft assert: we'll still do the right thing if it fails. + Debug.Assert(!originalNamespace.IsGlobalNamespace, "Global using to global namespace"); + + // Hard assert: we depend on this. + Debug.Assert(expandedGlobalNamespace.IsGlobalNamespace, "Global namespace required"); + + var nameParts = ArrayBuilder.GetInstance(); + var curr = originalNamespace; + while (!curr.IsGlobalNamespace) + { + nameParts.Add(curr.Name); + curr = curr.ContainingNamespace; + } + + var expandedNamespace = expandedGlobalNamespace; + for (int i = nameParts.Count - 1; i >= 0; i--) + { + // Note, the name may have become ambiguous (e.g. if a type with the same name + // is now in scope), but we're not rebinding - we're just expanding to the + // current contents of the same namespace. + expandedNamespace = expandedNamespace.GetMembers(nameParts[i]).OfType().Single(); + } + nameParts.Free(); + + return expandedNamespace; + } + public static Imports FromCustomDebugInfo( CSharpCompilation compilation, ImmutableDictionary usingAliases, @@ -318,7 +415,8 @@ private void MarkImportDirective(CSharpSyntaxNode directive, bool callerIsSemant private static void MarkImportDirective(CSharpCompilation compilation, CSharpSyntaxNode directive, bool callerIsSemanticModel) { - if (directive != null && compilation != null && !callerIsSemanticModel) + Debug.Assert(compilation != null); // If any directives are used, then there must be a compilation. + if (directive != null && !callerIsSemanticModel) { compilation.MarkImportDirectiveAsUsed(directive); } @@ -594,34 +692,34 @@ internal bool IsUsingAlias(string name, bool callerIsSemanticModel) // SemanticModel.LookupNames/LookupSymbols work and do not count as usages of the directives // when the actual code is bound. - internal void AddLookupSymbolsInfoInAliases(Binder originalBinder, LookupSymbolsInfo result, LookupOptions options) + internal void AddLookupSymbolsInfo(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder) { foreach (var usingAlias in this.UsingAliases.Values) { - var usingAliasSymbol = usingAlias.Alias; - var usingAliasTargetSymbol = usingAliasSymbol.GetAliasTarget(basesBeingResolved: null); - if (originalBinder.CanAddLookupSymbolInfo(usingAliasTargetSymbol, options, null)) - { - result.AddSymbol(usingAliasSymbol, usingAliasSymbol.Name, 0); - } + AddAliasSymbolToResult(result, usingAlias.Alias, options, originalBinder); } - if (this.ExternAliases != null) + foreach (var externAlias in this.ExternAliases) { - foreach (var externAlias in this.ExternAliases) - { - var externAliasSymbol = externAlias.Alias; - var externAliasTargetSymbol = externAliasSymbol.GetAliasTarget(basesBeingResolved: null); - if (originalBinder.CanAddLookupSymbolInfo(externAliasTargetSymbol, options, null)) - { - result.AddSymbol(externAliasSymbol, externAliasSymbol.Name, 0); - } - } + AddAliasSymbolToResult(result, externAlias.Alias, options, originalBinder); + } + + // Add types within namespaces imported through usings, but don't add nested namespaces. + LookupOptions usingOptions = (options & ~(LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) | LookupOptions.MustNotBeNamespace; + AddLookupSymbolsInfoInUsings(this.Usings, result, usingOptions, originalBinder); + } + + private static void AddAliasSymbolToResult(LookupSymbolsInfo result, AliasSymbol aliasSymbol, LookupOptions options, Binder originalBinder) + { + var targetSymbol = aliasSymbol.GetAliasTarget(basesBeingResolved: null); + if (originalBinder.CanAddLookupSymbolInfo(targetSymbol, options, null)) + { + result.AddSymbol(aliasSymbol, aliasSymbol.Name, 0); } } - internal static void AddLookupSymbolsInfoInUsings( - ImmutableArray usings, Binder originalBinder, LookupSymbolsInfo result, LookupOptions options) + private static void AddLookupSymbolsInfoInUsings( + ImmutableArray usings, LookupSymbolsInfo result, LookupOptions options, Binder originalBinder) { Debug.Assert(!options.CanConsiderNamespaces()); diff --git a/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs b/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs index a474dd96dfed3ea8f4877b6065f37ad40783e688..d2171b71bbb5375c601829a8c8aad031f74d115b 100644 --- a/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs @@ -16,8 +16,7 @@ internal sealed class InContainerBinder : Binder { private readonly NamespaceOrTypeSymbol _container; private readonly CSharpSyntaxNode _declarationSyntax; - private readonly bool _allowStaticClassUsings; - private Imports _imports; // might be initialized lazily + private Imports _lazyImports; private ImportChain _lazyImportChain; private readonly bool _inUsing; @@ -25,7 +24,7 @@ internal sealed class InContainerBinder : Binder /// Creates a binder for a container with imports (usings and extern aliases) that can be /// retrieved from . /// - internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, CSharpSyntaxNode declarationSyntax, bool allowStaticClassUsings, bool inUsing) + internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, CSharpSyntaxNode declarationSyntax, bool inUsing) : base(next) { Debug.Assert((object)container != null); @@ -33,7 +32,6 @@ internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, CSharpS _declarationSyntax = declarationSyntax; _container = container; - _allowStaticClassUsings = allowStaticClassUsings; _inUsing = inUsing; } @@ -43,10 +41,10 @@ internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, CSharpS internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, Imports imports = null) : base(next) { - Debug.Assert((object)container != null); + Debug.Assert((object)container != null || imports != null); _container = container; - _imports = imports ?? Imports.Empty; + _lazyImports = imports ?? Imports.Empty; } internal NamespaceOrTypeSymbol Container @@ -57,14 +55,6 @@ internal NamespaceOrTypeSymbol Container } } - internal bool AllowStaticClassUsings - { - get - { - return _allowStaticClassUsings; - } - } - internal Imports GetImports() { return GetImports(basesBeingResolved: null); @@ -72,12 +62,12 @@ internal Imports GetImports() private Imports GetImports(ConsList basesBeingResolved) { - if (_imports == null) + if (_lazyImports == null) { - Interlocked.CompareExchange(ref _imports, Imports.FromSyntax(_declarationSyntax, this, basesBeingResolved, _inUsing), null); + Interlocked.CompareExchange(ref _lazyImports, Imports.FromSyntax(_declarationSyntax, this, basesBeingResolved, _inUsing), null); } - return _imports; + return _lazyImports; } internal override ImportChain ImportChain @@ -87,7 +77,7 @@ internal override ImportChain ImportChain if (_lazyImportChain == null) { ImportChain importChain = this.Next.ImportChain; - if (_container.Kind == SymbolKind.Namespace) + if ((object)_container == null || _container.Kind == SymbolKind.Namespace) { importChain = new ImportChain(GetImports(), importChain); } @@ -112,7 +102,7 @@ internal override Symbol ContainingMemberOrLambda internal bool IsSubmissionClass { - get { return (_container.Kind == SymbolKind.NamedType) && ((NamedTypeSymbol)_container).IsSubmissionClass; } + get { return (_container?.Kind == SymbolKind.NamedType) && ((NamedTypeSymbol)_container).IsSubmissionClass; } } internal override bool IsAccessibleHelper(Symbol symbol, TypeSymbol accessThroughType, out bool failedThroughTypeCheck, ref HashSet useSiteDiagnostics, ConsList basesBeingResolved) @@ -145,22 +135,15 @@ internal override bool SupportsExtensionMethods { this.GetImports().LookupExtensionMethodsInUsings(methods, name, arity, options, isCallerSemanticModel); } - else + else if (_container?.Kind == SymbolKind.Namespace) { - if (_container.Kind == SymbolKind.Namespace) - { - ((NamespaceSymbol)_container).GetExtensionMethods(methods, name, arity, options); - } - else if (((NamedTypeSymbol)_container).IsScriptClass) + ((NamespaceSymbol)_container).GetExtensionMethods(methods, name, arity, options); + } + else if (IsSubmissionClass) + { + for (var submission = this.Compilation; submission != null; submission = submission.PreviousSubmission) { - for (var submission = this.Compilation; submission != null; submission = submission.PreviousSubmission) - { - var scriptClass = submission.ScriptClass; - if ((object)scriptClass != null) - { - scriptClass.GetExtensionMethods(methods, name, arity, options); - } - } + submission.ScriptClass?.GetExtensionMethods(methods, name, arity, options); } } } @@ -170,16 +153,16 @@ internal override bool SupportsExtensionMethods { Debug.Assert(result.IsClear); + var imports = GetImports(basesBeingResolved); + if (IsSubmissionClass) { this.LookupMembersInternal(result, _container, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteDiagnostics); return; } - var imports = GetImports(basesBeingResolved); - // first lookup members of the namespace - if ((options & LookupOptions.NamespaceAliasesOnly) == 0) + if ((options & LookupOptions.NamespaceAliasesOnly) == 0 && _container != null) { this.LookupMembersInternal(result, _container, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteDiagnostics); @@ -203,18 +186,17 @@ internal override bool SupportsExtensionMethods protected override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder) { - this.AddMemberLookupSymbolsInfo(result, _container, options, originalBinder); + if (_container != null) + { + this.AddMemberLookupSymbolsInfo(result, _container, options, originalBinder); + } - // if we are looking only for labels we do not need to search through the imports + // If we are looking only for labels we do not need to search through the imports. + // Submission imports are handled by AddMemberLookupSymbolsInfo (above). if (!IsSubmissionClass && ((options & LookupOptions.LabelsOnly) == 0)) { var imports = GetImports(basesBeingResolved: null); - - imports.AddLookupSymbolsInfoInAliases(originalBinder, result, options); - - // Add types within namespaces imported through usings, but don't add nested namespaces. - LookupOptions usingOptions = (options & ~(LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) | LookupOptions.MustNotBeNamespace; - Imports.AddLookupSymbolsInfoInUsings(imports.Usings, originalBinder, result, usingOptions); + imports.AddLookupSymbolsInfo(result, options, originalBinder); } } diff --git a/src/Compilers/CSharp/Portable/Binder/InteractiveUsingsBinder.cs b/src/Compilers/CSharp/Portable/Binder/InteractiveUsingsBinder.cs deleted file mode 100644 index 341db17ea0e626eab4d053dad8041b9a3672b867..0000000000000000000000000000000000000000 --- a/src/Compilers/CSharp/Portable/Binder/InteractiveUsingsBinder.cs +++ /dev/null @@ -1,118 +0,0 @@ -// 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.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.CSharp -{ - internal sealed class InteractiveUsingsBinder : UsingsBinder - { - internal InteractiveUsingsBinder(Binder next) - : base(next) - { - } - - protected override ImmutableArray GetConsolidatedUsings() - { - var currentSubmissionUsings = this.Compilation.GetSubmissionImports().Usings; - - // find the first preceding non-empty submission (has InteractiveUsingsBinder): - CSharpCompilation previous = this.Compilation.PreviousSubmission; - InteractiveUsingsBinder previousBinder = null; - - while (previous != null && previousBinder == null) - { - previousBinder = previous.GetInteractiveUsingsBinder(); - previous = previous.PreviousSubmission; - } - - if (previousBinder != null) - { - // TODO (tomat): - // optimization: do this only if additional references are added to the submission: - return RebindAndAddUsings(previousBinder.ConsolidatedUsings, currentSubmissionUsings); - } - - return currentSubmissionUsings; - } - - internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, DiagnosticBag diagnostics) - { - diagnostics.Add(ErrorCode.ERR_IteratorInInteractive, node.Location); - return CreateErrorType(); - } - - /// - /// Returns a new list of usings with all namespace symbols replaced by namespace symbols updated from current compilation references. - /// - private ImmutableArray RebindAndAddUsings( - ImmutableArray usingsToRebind, - ImmutableArray usingsToAdd) - { - if (usingsToRebind.Length == 0) - { - return usingsToAdd; - } - - int rebindCount = usingsToRebind.Length; - var result = new NamespaceOrTypeAndUsingDirective[rebindCount + usingsToAdd.Length]; - var reversedQualifiedName = new List(); - - int resultIndex = 0; - for (int usingIndex = 0; usingIndex < rebindCount; usingIndex++) - { - var symbol = usingsToRebind[usingIndex]; - - if (symbol.NamespaceOrType.Kind == SymbolKind.Namespace) - { - var namespaceSymbol = (NamespaceSymbol)symbol.NamespaceOrType; - Debug.Assert(!namespaceSymbol.IsGlobalNamespace); - reversedQualifiedName.Clear(); - - do - { - reversedQualifiedName.Add(namespaceSymbol.Name); - namespaceSymbol = namespaceSymbol.ContainingNamespace; - } - while (!namespaceSymbol.IsGlobalNamespace); - - NamespaceSymbol newNamespaceSymbol = Compilation.GlobalNamespace; - for (int i = reversedQualifiedName.Count - 1; i >= 0; i--) - { - newNamespaceSymbol = newNamespaceSymbol.GetNestedNamespace(reversedQualifiedName[i]); - - // new submissions can only add more members to namespaces, not remove them - Debug.Assert((object)newNamespaceSymbol != null); - } - - symbol = new NamespaceOrTypeAndUsingDirective(newNamespaceSymbol, null); - } - - result[resultIndex++] = symbol; - } - - // Don't add usings that are already present in rebound usings. An error has been - // reported if there are duplicate usings within one submission, so we only need to - // check for duplicates in previous submissions. Note that usings within each submission - // are already distinct. - foreach (var usingToAdd in usingsToAdd) - { - // The number of usings is small so use linear search rather than a hash set. - if (!result.Any(n => Equals(n.NamespaceOrType, usingToAdd.NamespaceOrType))) - { - result[resultIndex++] = usingToAdd; - } - } - - Array.Resize(ref result, resultIndex); - return result.AsImmutableOrNull(); - } - } -} diff --git a/src/Compilers/CSharp/Portable/Binder/UsingsBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingsBinder.cs deleted file mode 100644 index 4b11818410df3d98d9d0a3bf930483956297a26b..0000000000000000000000000000000000000000 --- a/src/Compilers/CSharp/Portable/Binder/UsingsBinder.cs +++ /dev/null @@ -1,108 +0,0 @@ -// 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 Microsoft.CodeAnalysis.CSharp.Symbols; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.CSharp -{ - internal class UsingsBinder : Binder - { - private ImmutableArray _lazyConsolidatedUsings; - - internal UsingsBinder(Binder next, ImmutableArray usings = default(ImmutableArray)) - : base(next) - { - _lazyConsolidatedUsings = usings; - } - - internal ImmutableArray ConsolidatedUsings - { - get - { - if (_lazyConsolidatedUsings.IsDefault) - { - ImmutableInterlocked.InterlockedCompareExchange( - ref _lazyConsolidatedUsings, GetConsolidatedUsings(), default(ImmutableArray)); - } - - return _lazyConsolidatedUsings; - } - } - - protected virtual ImmutableArray GetConsolidatedUsings() - { - throw ExceptionUtilities.Unreachable; - } - - internal override void LookupSymbolsInSingleBinder( - LookupResult result, string name, int arity, ConsList basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref HashSet useSiteDiagnostics) - { - if (!ShouldLookInUsings(options)) - { - return; - } - - LookupResult tmp = LookupResult.GetInstance(); - - // usings: - Imports.LookupSymbolInUsings(ConsolidatedUsings, originalBinder, tmp, name, arity, basesBeingResolved, options, diagnose, ref useSiteDiagnostics); - - // if we found a viable result in imported namespaces, use it instead of unviable symbols found in source: - if (tmp.IsMultiViable) - { - result.MergeEqual(tmp); - } - - tmp.Free(); - } - protected override void AddLookupSymbolsInfoInSingleBinder( - LookupSymbolsInfo result, LookupOptions options, Binder originalBinder) - { - - if (!ShouldLookInUsings(options)) - { - return; - } - - // Add types within namespaces imported through usings, but don't add nested namespaces. - LookupOptions usingOptions = (options & ~(LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) | LookupOptions.MustNotBeNamespace; - - Imports.AddLookupSymbolsInfoInUsings(ConsolidatedUsings, this, result, usingOptions); - } - - private static bool ShouldLookInUsings(LookupOptions options) - { - return (options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.LabelsOnly)) == 0; - } - - internal override bool SupportsExtensionMethods - { - get - { - return true; - } - } - - internal override void GetCandidateExtensionMethods( - bool searchUsingsNotNamespace, - ArrayBuilder methods, - string name, - int arity, - LookupOptions options, - bool isCallerSemanticModel) - { - if (searchUsingsNotNamespace) - { - foreach (var nsOrType in ConsolidatedUsings) - { - if (nsOrType.NamespaceOrType.Kind == SymbolKind.Namespace) - { - ((NamespaceSymbol)nsOrType.NamespaceOrType).GetExtensionMethods(methods, name, arity, options); - } - } - } - } - } -} diff --git a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj index e7877e9893017901cb87e6bbdf4924772ddc9936..966514ca563ce68589652206b00e34a5370004e1 100644 --- a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj +++ b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj @@ -128,7 +128,6 @@ - @@ -188,7 +187,6 @@ - diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 4b141124f63475da1ae007ba56c386266e5e2cd1..d00f1dcb937d36261dd07e20b61301941d009995 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -292,7 +292,7 @@ public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol co this.builtInOperators = new BuiltInOperators(this); _scriptClass = new Lazy(BindScriptClass); - _globalImports = new Lazy(BindGlobalUsings); + _globalImports = new Lazy(BindGlobalImports); _globalNamespaceAlias = new Lazy(CreateGlobalNamespaceAlias); _anonymousTypeManager = new AnonymousTypeManager(this); this.LanguageVersion = CommonLanguageVersion(syntaxAndDeclarations.ExternalSyntaxTrees); @@ -1195,16 +1195,34 @@ private ImplicitNamedTypeSymbol BindScriptClass() return namespaceOrType as ImplicitNamedTypeSymbol; } - internal Imports GlobalImports - { - get { return _globalImports.Value; } - } + /// + /// Global imports (including those from previous submissions, if there are any). + /// + internal Imports GlobalImports => _globalImports.Value; - internal IEnumerable GlobalUsings + private Imports BindGlobalImports() => Imports.FromGlobalUsings(this); + + /// + /// Imports declared by this submission (null if this isn't one). + /// + internal Imports SubmissionImports { get { - return GlobalImports.Usings.Select(u => u.NamespaceOrType); + if (!this.IsSubmission) + { + return null; + } + + // A submission may be empty or comprised of a single script file. + var tree = _syntaxAndDeclarations.ExternalSyntaxTrees.SingleOrDefault(); + if (tree == null) + { + return Imports.Empty; + } + + var binder = GetBinderFactory(tree).GetImportsBinder((CSharpSyntaxNode)tree.GetRoot()); + return binder.GetImports(); } } @@ -1658,30 +1676,6 @@ internal Imports GetImports(SingleNamespaceDeclaration declaration) return GetBinderFactory(declaration.SyntaxReference.SyntaxTree).GetImportsBinder((CSharpSyntaxNode)declaration.SyntaxReference.GetSyntax()).GetImports(); } - internal Imports GetSubmissionImports() - { - return ((SourceNamespaceSymbol)SourceModule.GlobalNamespace).GetBoundImportsMerged().SingleOrDefault() ?? Imports.Empty; - } - - internal InteractiveUsingsBinder GetInteractiveUsingsBinder() - { - Debug.Assert(IsSubmission); - - // empty compilation: - if ((object)ScriptClass == null) - { - Debug.Assert(_syntaxAndDeclarations.ExternalSyntaxTrees.Length == 0); - return null; - } - - return GetBinderFactory(_syntaxAndDeclarations.ExternalSyntaxTrees.Single()).GetInteractiveUsingsBinder(); - } - - private Imports BindGlobalUsings() - { - return Imports.FromGlobalUsings(this); - } - private AliasSymbol CreateGlobalNamespaceAlias() { return AliasSymbol.CreateGlobalNamespaceAlias(this.GlobalNamespace, new InContainerBinder(this.GlobalNamespace, new BuckStopsHereBinder(this))); @@ -2096,7 +2090,6 @@ private bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable GetSourceDeclarationDiagnostics(SyntaxTree syntaxTree = null, TextSpan? filterSpanWithinTree = null, Func, SyntaxTree, TextSpan?, IEnumerable> locationFilterOpt = null, CancellationToken cancellationToken = default(CancellationToken)) { - // global imports diagnostics (specified via compilation options): GlobalImports.Complete(cancellationToken); SourceLocation location = null; diff --git a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs index 771cc5748e7aad0feb1929b5f86b5a67bfdae364..495726a0bb5f8374d99d5e2fa77d1c553ca8f46d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs @@ -45,7 +45,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols internal sealed class AliasSymbol : Symbol, IAliasSymbol { private readonly SyntaxToken _aliasName; - private readonly InContainerBinder _binder; + private readonly Binder _binder; private SymbolCompletionState _state; private NamespaceOrTypeSymbol _aliasTarget; @@ -56,7 +56,7 @@ internal sealed class AliasSymbol : Symbol, IAliasSymbol private readonly bool _isExtern; private DiagnosticBag _aliasTargetDiagnostics; - private AliasSymbol(InContainerBinder binder, NamespaceOrTypeSymbol target, SyntaxToken aliasName, ImmutableArray locations) + private AliasSymbol(Binder binder, NamespaceOrTypeSymbol target, SyntaxToken aliasName, ImmutableArray locations) { _aliasName = aliasName; _locations = locations; @@ -65,20 +65,20 @@ private AliasSymbol(InContainerBinder binder, NamespaceOrTypeSymbol target, Synt _state.NotePartComplete(CompletionPart.AliasTarget); } - private AliasSymbol(InContainerBinder binder, SyntaxToken aliasName) + private AliasSymbol(Binder binder, SyntaxToken aliasName) { _aliasName = aliasName; _locations = ImmutableArray.Create(aliasName.GetLocation()); _binder = binder; } - internal AliasSymbol(InContainerBinder binder, UsingDirectiveSyntax syntax) + internal AliasSymbol(Binder binder, UsingDirectiveSyntax syntax) : this(binder, syntax.Alias.Name.Identifier) { _aliasTargetName = syntax.Name; } - internal AliasSymbol(InContainerBinder binder, ExternAliasDirectiveSyntax syntax) + internal AliasSymbol(Binder binder, ExternAliasDirectiveSyntax syntax) : this(binder, syntax.Identifier) { _isExtern = true; @@ -86,17 +86,34 @@ internal AliasSymbol(InContainerBinder binder, ExternAliasDirectiveSyntax syntax // For the purposes of SemanticModel, it is convenient to have an AliasSymbol for the "global" namespace that "global::" binds // to. This alias symbol is returned only when binding "global::" (special case code). - internal static AliasSymbol CreateGlobalNamespaceAlias(NamespaceSymbol globalNamespace, InContainerBinder globalNamespaceBinder) + internal static AliasSymbol CreateGlobalNamespaceAlias(NamespaceSymbol globalNamespace, Binder globalNamespaceBinder) { SyntaxToken aliasName = SyntaxFactory.Identifier(SyntaxFactory.TriviaList(), SyntaxKind.GlobalKeyword, "global", "global", SyntaxFactory.TriviaList()); return new AliasSymbol(globalNamespaceBinder, globalNamespace, aliasName, ImmutableArray.Empty); } - internal static AliasSymbol CreateCustomDebugInfoAlias(NamespaceOrTypeSymbol targetSymbol, SyntaxToken aliasToken, InContainerBinder binder) + internal static AliasSymbol CreateCustomDebugInfoAlias(NamespaceOrTypeSymbol targetSymbol, SyntaxToken aliasToken, Binder binder) { return new AliasSymbol(binder, targetSymbol, aliasToken, ImmutableArray.Create(aliasToken.GetLocation())); } + internal AliasSymbol ToNewSubmission(CSharpCompilation compilation) + { + Debug.Assert(_state.HasComplete(CompletionPart.AliasTarget)); + Debug.Assert(_binder.Compilation.IsSubmission); + + var previousTarget = _aliasTarget; + if (previousTarget.Kind != SymbolKind.Namespace) + { + return this; + } + + var expandedGlobalNamespace = compilation.GlobalNamespace; + var expandedNamespace = Imports.ExpandPreviousSubmissionNamespace((NamespaceSymbol)previousTarget, expandedGlobalNamespace); + var binder = new InContainerBinder(expandedGlobalNamespace, new BuckStopsHereBinder(compilation)); + return new AliasSymbol(binder, expandedNamespace, _aliasName, _locations); + } + public override string Name { get @@ -303,7 +320,7 @@ private NamespaceSymbol ResolveExternAliasTarget(DiagnosticBag diagnostics) return target; } - private static NamespaceOrTypeSymbol ResolveAliasTarget(InContainerBinder binder, NameSyntax syntax, DiagnosticBag diagnostics, ConsList basesBeingResolved) + private static NamespaceOrTypeSymbol ResolveAliasTarget(Binder binder, NameSyntax syntax, DiagnosticBag diagnostics, ConsList basesBeingResolved) { var declarationBinder = binder.WithAdditionalFlags(BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressObsoleteChecks); return declarationBinder.BindNamespaceOrTypeSymbol(syntax, diagnostics, basesBeingResolved); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InteractiveUsingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InteractiveUsingTests.cs index f962288aac41b4cf527224f2a81dc8f7a59c4e3e..3e142a2d4690d34d1c7742b896394d1532d48249 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InteractiveUsingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InteractiveUsingTests.cs @@ -41,7 +41,7 @@ public void UsingStatic() } [WorkItem(5450, "https://github.com/dotnet/roslyn/issues/5450")] - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/5450")] + [Fact] public void GlobalUsings() { var sub1 = CreateSubmission( @@ -319,15 +319,15 @@ public class B{0} {{ }} var options = TestOptions.DebugDll.WithUsings("B"); - var sub1 = CreateSubmission("using A;", new[] { lib1 }, options); + var sub1 = CreateSubmission("using A; typeof(A1) == typeof(B1)", new[] { lib1 }, options); sub1.VerifyDiagnostics(); - var sub2 = CreateSubmission("typeof(A1) == typeof(A2) && typeof(B1) == typeof(B2)", new[] { lib1, lib2 }, options: options, previous: sub1); + var sub2 = CreateSubmission("typeof(A1) == typeof(B1) && typeof(A2) == typeof(B2)", new[] { lib1, lib2 }, options: options, previous: sub1); sub2.VerifyDiagnostics(); } [WorkItem(5423, "https://github.com/dotnet/roslyn/issues/5423")] - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/5423")] + [Fact] void UsingsFromLoadedScript() { const string scriptSource = @" @@ -442,6 +442,96 @@ void GlobalUsingsToLoadedScript() compilation.VerifyDiagnostics(); } + [WorkItem(4811, "https://github.com/dotnet/roslyn/issues/4811")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/4811")] + public void ConsumePreviousSubmissionUsings_Valid() + { + const string libSource = @" +namespace NOuter +{ + public class Test { } + + namespace NInner + { + public static class COuter + { + public static void M() { } + + public static class CInner + { + public static void N() { } + } + } + } +} +"; + + var lib = CreateCompilationWithMscorlib(libSource).EmitToImageReference(); + var refs = new[] { lib }; + + var submissions = new[] + { + "using NOuter;", + "typeof(Test)", + "using NI = NOuter.NInner;", + "typeof(NI.COuter)", + "using static NI.COuter;", + "M()", + "using static CInner;", + "N()", + }; + + CSharpCompilation prev = null; + foreach (var submission in submissions) + { + System.Console.WriteLine(submission); + var curr = CreateSubmission(submission, refs, previous: prev); + curr.VerifyDiagnostics(); + prev = curr; + } + } + + [Fact] + public void ConsumePreviousSubmissionUsings_Invalid() + { + const string libSource = @" +namespace NOuter +{ + public class COuter { } + + namespace NInner + { + public static class CInner + { + } + } +} +"; + + var lib = CreateCompilationWithMscorlib(libSource).EmitToImageReference(); + var refs = new[] { lib }; + + CreateSubmission("using NInner;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics( + // (1,7): error CS0246: The type or namespace name 'NInner' could not be found (are you missing a using directive or an assembly reference?) + // using NInner; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "NInner").WithArguments("NInner").WithLocation(1, 7)); + + CreateSubmission("using NI = NInner;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics( + // (1,12): error CS0246: The type or namespace name 'NInner' could not be found (are you missing a using directive or an assembly reference?) + // using NI = NInner; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "NInner").WithArguments("NInner").WithLocation(1, 12)); + + CreateSubmission("using static COuter;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics( + // (1,14): error CS0246: The type or namespace name 'COuter' could not be found (are you missing a using directive or an assembly reference?) + // using static COuter; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "COuter").WithArguments("COuter").WithLocation(1, 14)); + + CreateSubmission("using static NInner.CInner;", refs, previous: CreateSubmission("using NOuter;", refs)).VerifyDiagnostics( + // (1,14): error CS0246: The type or namespace name 'NInner' could not be found (are you missing a using directive or an assembly reference?) + // using static NInner.CInner; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "NInner").WithArguments("NInner").WithLocation(1, 14)); + } + private static Symbol GetSpeculativeSymbol(CSharpCompilation comp, string name) { var tree = comp.SyntaxTrees.Single();