|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
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 Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
/// <summary>
/// A binder that represents a scope introduced by 'using' namespace or type directives and deals with looking up names in it.
/// </summary>
internal abstract class WithUsingNamespacesAndTypesBinder : Binder
{
private readonly bool _withImportChainEntry;
private ImportChain? _lazyImportChain;
protected WithUsingNamespacesAndTypesBinder(Binder next, bool withImportChainEntry)
: base(next)
{
_withImportChainEntry = withImportChainEntry;
}
#if DEBUG
internal bool WithImportChainEntry => _withImportChainEntry;
#endif
internal abstract ImmutableArray<NamespaceOrTypeAndUsingDirective> GetUsings(ConsList<TypeSymbol>? basesBeingResolved);
/// <summary>
/// Look for a type forwarder for the given type in any referenced assemblies, checking any using namespaces in
/// the current imports.
/// </summary>
/// <param name="name">The metadata name of the (potentially) forwarded type, without qualifiers.</param>
/// <param name="qualifierOpt">Will be used to return the namespace of the found forwarder,
/// if any.</param>
/// <param name="diagnostics">Will be used to report non-fatal errors during look up.</param>
/// <param name="location">Location to report errors on.</param>
/// <returns>Returns the Assembly to which the type is forwarded, or null if none is found.</returns>
/// <remarks>
/// Since this method is intended to be used for error reporting, it stops as soon as it finds
/// any type forwarder (or an error to report). It does not check other assemblies for consistency or better results.
/// </remarks>
protected override AssemblySymbol? GetForwardedToAssemblyInUsingNamespaces(string name, ref NamespaceOrTypeSymbol qualifierOpt, BindingDiagnosticBag diagnostics, Location location)
{
foreach (var typeOrNamespace in GetUsings(basesBeingResolved: null))
{
var fullName = typeOrNamespace.NamespaceOrType + "." + name;
var result = GetForwardedToAssembly(fullName, diagnostics, location);
if (result != null)
{
qualifierOpt = typeOrNamespace.NamespaceOrType;
return result;
}
}
return base.GetForwardedToAssemblyInUsingNamespaces(name, ref qualifierOpt, diagnostics, location);
}
internal override bool SupportsExtensionMethods
{
get { return true; }
}
internal override void GetCandidateExtensionMethods(
ArrayBuilder<MethodSymbol> methods,
string name,
int arity,
LookupOptions options,
Binder originalBinder)
{
Debug.Assert(methods.Count == 0);
bool callerIsSemanticModel = originalBinder.IsSemanticModelBinder;
// We need to avoid collecting multiple candidates for an extension method imported both through a namespace and a static class
// We will look for duplicates only if both of the following flags are set to true
bool seenNamespaceWithExtensionMethods = false;
bool seenStaticClassWithExtensionMethods = false;
foreach (var nsOrType in this.GetUsings(basesBeingResolved: null))
{
switch (nsOrType.NamespaceOrType.Kind)
{
case SymbolKind.Namespace:
{
var count = methods.Count;
((NamespaceSymbol)nsOrType.NamespaceOrType).GetExtensionMethods(methods, name, arity, options);
// If we found any extension methods, then consider this using as used.
if (methods.Count != count)
{
MarkImportDirective(nsOrType.UsingDirectiveReference, callerIsSemanticModel);
seenNamespaceWithExtensionMethods = true;
}
break;
}
case SymbolKind.NamedType:
{
var count = methods.Count;
((NamedTypeSymbol)nsOrType.NamespaceOrType).GetExtensionMethods(methods, name, arity, options);
// If we found any extension methods, then consider this using as used.
if (methods.Count != count)
{
MarkImportDirective(nsOrType.UsingDirectiveReference, callerIsSemanticModel);
seenStaticClassWithExtensionMethods = true;
}
break;
}
}
}
if (seenNamespaceWithExtensionMethods && seenStaticClassWithExtensionMethods)
{
methods.RemoveDuplicates();
}
}
internal override void LookupSymbolsInSingleBinder(
LookupResult result, string name, int arity, ConsList<TypeSymbol>? basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(result.IsClear);
bool callerIsSemanticModel = originalBinder.IsSemanticModelBinder;
foreach (var typeOrNamespace in this.GetUsings(basesBeingResolved))
{
ImmutableArray<Symbol> candidates = Binder.GetCandidateMembers(typeOrNamespace.NamespaceOrType, name, options, originalBinder: originalBinder);
foreach (Symbol symbol in candidates)
{
if (!IsValidLookupCandidateInUsings(symbol))
{
continue;
}
// Found a match in our list of normal using directives. Mark the directive
// as being seen so that it won't be reported to the user as something that
// can be removed.
var res = originalBinder.CheckViability(symbol, arity, options, null, diagnose, ref useSiteInfo, basesBeingResolved);
if (res.Kind == LookupResultKind.Viable)
{
MarkImportDirective(typeOrNamespace.UsingDirectiveReference, callerIsSemanticModel);
}
result.MergeEqual(res);
}
}
}
private static bool IsValidLookupCandidateInUsings(Symbol symbol)
{
switch (symbol.Kind)
{
// lookup via "using namespace" ignores namespaces inside
case SymbolKind.Namespace:
return false;
// lookup via "using static" ignores extension methods and non-static methods
case SymbolKind.Method:
if (!symbol.IsStatic || ((MethodSymbol)symbol).IsExtensionMethod)
{
return false;
}
break;
// types are considered static members for purposes of "using static" feature
// regardless of whether they are declared with "static" modifier or not
case SymbolKind.NamedType:
break;
// lookup via "using static" ignores non-static members
default:
if (!symbol.IsStatic)
{
return false;
}
break;
}
return true;
}
internal override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
{
// If we are looking only for labels we do not need to search through the imports.
if ((options & LookupOptions.LabelsOnly) == 0)
{
// Add types within namespaces imported through usings, but don't add nested namespaces.
options = (options & ~(LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) | LookupOptions.MustNotBeNamespace;
// look in all using namespaces
foreach (var namespaceSymbol in this.GetUsings(basesBeingResolved: null))
{
foreach (var member in namespaceSymbol.NamespaceOrType.GetMembersUnordered())
{
if (IsValidLookupCandidateInUsings(member) && originalBinder.CanAddLookupSymbolInfo(member, options, result, null))
{
result.AddSymbol(member, member.Name, member.GetArity());
}
}
}
}
}
protected override SourceLocalSymbol? LookupLocal(SyntaxToken nameToken)
{
return null;
}
protected override LocalFunctionSymbol? LookupLocalFunction(SyntaxToken nameToken)
{
return null;
}
internal override ImportChain? ImportChain
{
get
{
if (_lazyImportChain == null)
{
ImportChain? importChain = this.Next!.ImportChain;
if (_withImportChainEntry)
{
importChain = new ImportChain(GetImports(), importChain);
}
Interlocked.CompareExchange(ref _lazyImportChain, importChain, null);
}
Debug.Assert(_lazyImportChain != null || !_withImportChainEntry);
return _lazyImportChain;
}
}
protected abstract Imports GetImports();
internal static WithUsingNamespacesAndTypesBinder Create(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next, bool withPreviousSubmissionImports = false, bool withImportChainEntry = false)
{
if (withPreviousSubmissionImports)
{
return new FromSyntaxWithPreviousSubmissionImports(declaringSymbol, declarationSyntax, next, withImportChainEntry);
}
return new FromSyntax(declaringSymbol, declarationSyntax, next, withImportChainEntry);
}
internal static WithUsingNamespacesAndTypesBinder Create(ImmutableArray<NamespaceOrTypeAndUsingDirective> namespacesOrTypes, Binder next, bool withImportChainEntry = false)
{
return new FromNamespacesOrTypes(namespacesOrTypes, next, withImportChainEntry);
}
private sealed class FromSyntax : WithUsingNamespacesAndTypesBinder
{
private readonly SourceNamespaceSymbol _declaringSymbol;
private readonly CSharpSyntaxNode _declarationSyntax;
private ImmutableArray<NamespaceOrTypeAndUsingDirective> _lazyUsings;
internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next, bool withImportChainEntry)
: base(next, withImportChainEntry)
{
Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration);
_declaringSymbol = declaringSymbol;
_declarationSyntax = declarationSyntax;
}
internal override ImmutableArray<NamespaceOrTypeAndUsingDirective> GetUsings(ConsList<TypeSymbol>? basesBeingResolved)
{
if (_lazyUsings.IsDefault)
{
ImmutableInterlocked.InterlockedInitialize(ref _lazyUsings, _declaringSymbol.GetUsingNamespacesOrTypes(_declarationSyntax, basesBeingResolved));
}
return _lazyUsings;
}
protected override Imports GetImports()
{
return _declaringSymbol.GetImports(_declarationSyntax, basesBeingResolved: null);
}
}
private sealed class FromSyntaxWithPreviousSubmissionImports : WithUsingNamespacesAndTypesBinder
{
private readonly SourceNamespaceSymbol _declaringSymbol;
private readonly CSharpSyntaxNode _declarationSyntax;
private Imports? _lazyFullImports;
internal FromSyntaxWithPreviousSubmissionImports(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next, bool withImportChainEntry)
: base(next, withImportChainEntry)
{
Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration));
_declaringSymbol = declaringSymbol;
_declarationSyntax = declarationSyntax;
}
internal override ImmutableArray<NamespaceOrTypeAndUsingDirective> GetUsings(ConsList<TypeSymbol>? basesBeingResolved)
{
return GetImports(basesBeingResolved).Usings;
}
private Imports GetImports(ConsList<TypeSymbol>? basesBeingResolved)
{
if (_lazyFullImports is null)
{
Interlocked.CompareExchange(ref _lazyFullImports,
_declaringSymbol.DeclaringCompilation.GetPreviousSubmissionImports().Concat(_declaringSymbol.GetImports(_declarationSyntax, basesBeingResolved)),
null);
}
return _lazyFullImports;
}
protected override Imports GetImports()
{
return GetImports(basesBeingResolved: null);
}
}
private sealed class FromNamespacesOrTypes : WithUsingNamespacesAndTypesBinder
{
private readonly ImmutableArray<NamespaceOrTypeAndUsingDirective> _usings;
internal FromNamespacesOrTypes(ImmutableArray<NamespaceOrTypeAndUsingDirective> namespacesOrTypes, Binder next, bool withImportChainEntry)
: base(next, withImportChainEntry)
{
Debug.Assert(!namespacesOrTypes.IsDefault);
_usings = namespacesOrTypes;
}
internal override ImmutableArray<NamespaceOrTypeAndUsingDirective> GetUsings(ConsList<TypeSymbol>? basesBeingResolved)
{
return _usings;
}
protected override Imports GetImports()
{
return Imports.Create(ImmutableDictionary<string, AliasAndUsingDirective>.Empty, _usings, ImmutableArray<AliasAndExternAliasDirective>.Empty);
}
}
}
}
|