提交 b5ebfe37 编写于 作者: A Andrew Casey

Merge pull request #5520 from amcasey/GH5450

Inherit Global Usings from previous submissions
......@@ -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);
}
......
......@@ -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;
}
}
}
......@@ -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);
}
}
}
......
......@@ -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<string, AliasAndUsingDirective>.Empty, boundUsings.ToImmutableAndFree(), ImmutableArray<AliasAndExternAliasDirective>.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<string, AliasAndUsingDirective>.Empty;
if (!previousSubmissionImports.UsingAliases.IsEmpty)
{
var expandedAliasesBuilder = ImmutableDictionary.CreateBuilder<string, AliasAndUsingDirective>();
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<NamespaceOrTypeAndUsingDirective>.Empty;
if (!previousSubmissionImports.Usings.IsEmpty)
{
var expandedUsingsBuilder = ArrayBuilder<NamespaceOrTypeAndUsingDirective>.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<string>.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<NamespaceSymbol>().Single();
}
nameParts.Free();
return expandedNamespace;
}
public static Imports FromCustomDebugInfo(
CSharpCompilation compilation,
ImmutableDictionary<string, AliasAndUsingDirective> 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<NamespaceOrTypeAndUsingDirective> usings, Binder originalBinder, LookupSymbolsInfo result, LookupOptions options)
private static void AddLookupSymbolsInfoInUsings(
ImmutableArray<NamespaceOrTypeAndUsingDirective> usings, LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
{
Debug.Assert(!options.CanConsiderNamespaces());
......
......@@ -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 <paramref name="declarationSyntax"/>.
/// </summary>
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<Symbol> 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<DiagnosticInfo> useSiteDiagnostics, ConsList<Symbol> 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);
}
}
......
// 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<NamespaceOrTypeAndUsingDirective> 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();
}
/// <summary>
/// Returns a new list of usings with all namespace symbols replaced by namespace symbols updated from current compilation references.
/// </summary>
private ImmutableArray<NamespaceOrTypeAndUsingDirective> RebindAndAddUsings(
ImmutableArray<NamespaceOrTypeAndUsingDirective> usingsToRebind,
ImmutableArray<NamespaceOrTypeAndUsingDirective> usingsToAdd)
{
if (usingsToRebind.Length == 0)
{
return usingsToAdd;
}
int rebindCount = usingsToRebind.Length;
var result = new NamespaceOrTypeAndUsingDirective[rebindCount + usingsToAdd.Length];
var reversedQualifiedName = new List<string>();
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();
}
}
}
// 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<NamespaceOrTypeAndUsingDirective> _lazyConsolidatedUsings;
internal UsingsBinder(Binder next, ImmutableArray<NamespaceOrTypeAndUsingDirective> usings = default(ImmutableArray<NamespaceOrTypeAndUsingDirective>))
: base(next)
{
_lazyConsolidatedUsings = usings;
}
internal ImmutableArray<NamespaceOrTypeAndUsingDirective> ConsolidatedUsings
{
get
{
if (_lazyConsolidatedUsings.IsDefault)
{
ImmutableInterlocked.InterlockedCompareExchange(
ref _lazyConsolidatedUsings, GetConsolidatedUsings(), default(ImmutableArray<NamespaceOrTypeAndUsingDirective>));
}
return _lazyConsolidatedUsings;
}
}
protected virtual ImmutableArray<NamespaceOrTypeAndUsingDirective> GetConsolidatedUsings()
{
throw ExceptionUtilities.Unreachable;
}
internal override void LookupSymbolsInSingleBinder(
LookupResult result, string name, int arity, ConsList<Symbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref HashSet<DiagnosticInfo> 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<MethodSymbol> 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);
}
}
}
}
}
}
......@@ -128,7 +128,6 @@
<Compile Include="Binder\Imports.cs" />
<Compile Include="Binder\InContainerBinder.cs" />
<Compile Include="Binder\InMethodBinder.cs" />
<Compile Include="Binder\InteractiveUsingsBinder.cs" />
<Compile Include="Binder\LocalBinderFactory.cs" />
<Compile Include="Binder\LocalInProgressBinder.cs" />
<Compile Include="Binder\LocalScopeBinder.cs" />
......@@ -188,7 +187,6 @@
<Compile Include="Binder\SwitchBinder.cs" />
<Compile Include="Binder\SimpleLocalScopeBinder.cs" />
<Compile Include="Binder\TypeofBinder.cs" />
<Compile Include="Binder\UsingsBinder.cs" />
<Compile Include="Binder\UsingStatementBinder.cs" />
<Compile Include="Binder\WhileBinder.cs" />
<Compile Include="Binder\WithClassTypeParametersBinder.cs" />
......
......@@ -292,7 +292,7 @@ public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol co
this.builtInOperators = new BuiltInOperators(this);
_scriptClass = new Lazy<ImplicitNamedTypeSymbol>(BindScriptClass);
_globalImports = new Lazy<Imports>(BindGlobalUsings);
_globalImports = new Lazy<Imports>(BindGlobalImports);
_globalNamespaceAlias = new Lazy<AliasSymbol>(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; }
}
/// <summary>
/// Global imports (including those from previous submissions, if there are any).
/// </summary>
internal Imports GlobalImports => _globalImports.Value;
internal IEnumerable<NamespaceOrTypeSymbol> GlobalUsings
private Imports BindGlobalImports() => Imports.FromGlobalUsings(this);
/// <summary>
/// Imports declared by this submission (null if this isn't one).
/// </summary>
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<D
private ImmutableArray<Diagnostic> GetSourceDeclarationDiagnostics(SyntaxTree syntaxTree = null, TextSpan? filterSpanWithinTree = null, Func<IEnumerable<Diagnostic>, SyntaxTree, TextSpan?, IEnumerable<Diagnostic>> locationFilterOpt = null, CancellationToken cancellationToken = default(CancellationToken))
{
// global imports diagnostics (specified via compilation options):
GlobalImports.Complete(cancellationToken);
SourceLocation location = null;
......
......@@ -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<Location> locations)
private AliasSymbol(Binder binder, NamespaceOrTypeSymbol target, SyntaxToken aliasName, ImmutableArray<Location> 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<Location>.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<Symbol> basesBeingResolved)
private static NamespaceOrTypeSymbol ResolveAliasTarget(Binder binder, NameSyntax syntax, DiagnosticBag diagnostics, ConsList<Symbol> basesBeingResolved)
{
var declarationBinder = binder.WithAdditionalFlags(BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressObsoleteChecks);
return declarationBinder.BindNamespaceOrTypeSymbol(syntax, diagnostics, basesBeingResolved);
......
......@@ -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();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册