Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split imports binder into its own binder. #51452

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ private Binder MakeNamespaceBinder(CSharpSyntaxNode node, NameSyntax name, Binde

NamespaceSymbol ns = ((NamespaceSymbol)container).GetNestedNamespace(name);
if ((object)ns == null) return outer;
return new InContainerBinder(ns, outer, node, inUsing: inUsing);
return new InContainerBinder(ns, new ImportsBinder(ns, outer, node, inUsing: inUsing));
}

public override Binder VisitCompilationUnit(CompilationUnitSyntax parent)
Expand Down Expand Up @@ -897,14 +897,14 @@ internal Binder VisitCompilationUnit(CompilationUnitSyntax compilationUnit, bool
}
else
{
result = new InContainerBinder(container: null, next: result, imports: compilation.GlobalImports);
result = new ImportsBinder(container: null, next: result, imports: compilation.GlobalImports);

// NB: This binder has a full Imports object, but only the non-alias imports are
// ever consumed. Aliases are actually checked in scriptClassBinder (below).
// Note: #loaded trees don't consume previous submission imports.
result = compilation.PreviousSubmission == null || !isSubmissionTree
? new InContainerBinder(result, basesBeingResolved => scriptClassBinder.GetImports(basesBeingResolved))
: new InContainerBinder(result, basesBeingResolved =>
? new ImportsBinder(result, basesBeingResolved => scriptClassBinder.GetImports(basesBeingResolved))
: new ImportsBinder(result, basesBeingResolved =>
compilation.GetPreviousSubmissionImports().Concat(scriptClassBinder.GetImports(basesBeingResolved)));
}

Expand All @@ -915,7 +915,8 @@ internal Binder VisitCompilationUnit(CompilationUnitSyntax compilationUnit, bool
result = new HostObjectModelBinder(result);
}

scriptClassBinder = new InContainerBinder(compilation.ScriptClass, result, compilationUnit, inUsing: inUsing);
var scriptClass = compilation.ScriptClass;
scriptClassBinder = new InContainerBinder(scriptClass, new ImportsBinder(scriptClass, result, compilationUnit, inUsing: inUsing));
result = scriptClassBinder;
}
else
Expand All @@ -925,7 +926,8 @@ internal Binder VisitCompilationUnit(CompilationUnitSyntax compilationUnit, bool
//
// + global namespace with top-level imports
//
result = new InContainerBinder(compilation.GlobalNamespace, result, compilationUnit, inUsing: inUsing);
var globalNamespace = compilation.GlobalNamespace;
result = new InContainerBinder(globalNamespace, new ImportsBinder(globalNamespace, result, compilationUnit, inUsing: inUsing));

if (!inUsing &&
SimpleProgramNamedTypeSymbol.GetSimpleProgramEntryPoint(compilation, compilationUnit, fallbackToMainEntryPoint: true) is SynthesizedSimpleProgramEntryPointSymbol simpleProgram)
Expand Down
3 changes: 1 addition & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ private void LookupExtensionMethodsInSingleBinder(ExtensionMethodScope scope, Lo
{
var methods = ArrayBuilder<MethodSymbol>.GetInstance();
var binder = scope.Binder;
binder.GetCandidateExtensionMethods(scope.SearchUsingsNotNamespace, methods, name, arity, options, this);
binder.GetCandidateExtensionMethods(methods, name, arity, options, this);

foreach (var method in methods)
{
Expand Down Expand Up @@ -692,7 +692,6 @@ internal virtual bool SupportsExtensionMethods
/// to search the available members list in binding types that represent types, namespaces, and usings.
/// </summary>
internal virtual void GetCandidateExtensionMethods(
bool searchUsingsNotNamespace,
ArrayBuilder<MethodSymbol> methods,
string name,
int arity,
Expand Down
19 changes: 4 additions & 15 deletions src/Compilers/CSharp/Portable/Binder/ExtensionMethodScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ namespace Microsoft.CodeAnalysis.CSharp
internal struct ExtensionMethodScope
{
public readonly Binder Binder;
public readonly bool SearchUsingsNotNamespace;

public ExtensionMethodScope(Binder binder, bool searchUsingsNotNamespace)
public ExtensionMethodScope(Binder binder)
{
this.Binder = binder;
this.SearchUsingsNotNamespace = searchUsingsNotNamespace;
}
}

Expand Down Expand Up @@ -70,17 +68,8 @@ public bool MoveNext()
else
{
var binder = _current.Binder;
if (!_current.SearchUsingsNotNamespace)
{
// Return a scope for the same Binder that was previously exposed
// for the namespace, this time exposed for the usings.
_current = new ExtensionMethodScope(binder, searchUsingsNotNamespace: true);
}
else
{
// Return a scope for the next Binder that supports extension methods.
_current = GetNextScope(binder.Next);
}
// Return a scope for the next Binder that supports extension methods.
_current = GetNextScope(binder.Next);
Copy link
Member

@333fred 333fred Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: it looks like this whole if/else could be simplified to _current = GetNextScope(_current.Binder?.Next ?? _binder); #WontFix

}

return (_current.Binder != null);
Expand All @@ -92,7 +81,7 @@ private static ExtensionMethodScope GetNextScope(Binder binder)
{
if (scope.SupportsExtensionMethods)
{
return new ExtensionMethodScope(scope, searchUsingsNotNamespace: false);
return new ExtensionMethodScope(scope);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Imports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ internal string GetDebuggerDisplay()

public static Imports FromSyntax(
CSharpSyntaxNode declarationSyntax,
InContainerBinder binder,
ImportsBinder binder,
ConsList<TypeSymbol> basesBeingResolved,
bool inUsing)
{
Expand Down Expand Up @@ -132,7 +132,7 @@ public static Imports FromSyntax(
ImmutableArray<NamespaceOrTypeAndUsingDirective>.Empty,
externAliases,
diagnostics: null);
usingsBinder = new InContainerBinder(binder.Container, binder.Next, imports);
usingsBinder = new InContainerBinder(binder.Container, new ImportsBinder(binder.Container, binder.Next, imports));
}

var uniqueUsings = SpecializedSymbolCollections.GetPooledSymbolHashSetInstance<NamespaceOrTypeSymbol>();
Expand Down Expand Up @@ -483,7 +483,7 @@ private static ImmutableArray<AliasAndExternAliasDirective> ConcatExternAliases(

private static ImmutableArray<AliasAndExternAliasDirective> BuildExternAliases(
SyntaxList<ExternAliasDirectiveSyntax> syntaxList,
InContainerBinder binder,
ImportsBinder binder,
DiagnosticBag diagnostics)
{
CSharpCompilation compilation = binder.Compilation;
Expand Down
238 changes: 238 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/ImportsBinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
// 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.

#nullable disable

using System;
using System.Collections.Generic;
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 represent scope introduced by 'using' directives and deals with looking up names in it.
/// </summary>
internal sealed class ImportsBinder : Binder
{
private readonly NamespaceOrTypeSymbol _container;
private readonly Func<ConsList<TypeSymbol>, Imports> _computeImports;
private Imports _lazyImports;
private ImportChain _lazyImportChain;
private QuickAttributeChecker _lazyQuickAttributeChecker;
private readonly SyntaxList<UsingDirectiveSyntax> _usingsSyntax;

/// <summary>
/// Creates a binder imports (usings and extern aliases) within a <paramref name="container"/> that can be
/// retrieved from <paramref name="declarationSyntax"/>.
/// </summary>
internal ImportsBinder(NamespaceOrTypeSymbol container, Binder next, CSharpSyntaxNode declarationSyntax, bool inUsing)
: base(next)
{
Debug.Assert((object)container != null);
Debug.Assert(declarationSyntax != null);

_container = container;
_computeImports = basesBeingResolved => Imports.FromSyntax(declarationSyntax, this, basesBeingResolved, inUsing);

if (!inUsing)
{
if (declarationSyntax.Kind() == SyntaxKind.CompilationUnit)
{
var compilationUnit = (CompilationUnitSyntax)declarationSyntax;
_usingsSyntax = compilationUnit.Usings;
}
else if (declarationSyntax.Kind() == SyntaxKind.NamespaceDeclaration)
{
var namespaceDecl = (NamespaceDeclarationSyntax)declarationSyntax;
_usingsSyntax = namespaceDecl.Usings;
}
}
}

/// <summary>
/// Creates a binder with given imports.
/// </summary>
internal ImportsBinder(NamespaceOrTypeSymbol container, Binder next, Imports imports = null)
: base(next)
{
Debug.Assert((object)container != null || imports != null);

_container = container;
_lazyImports = imports ?? Imports.Empty;
}

/// <summary>
/// Creates a binder with given import computation function.
/// </summary>
internal ImportsBinder(Binder next, Func<ConsList<TypeSymbol>, Imports> computeImports)
: base(next)
{
Debug.Assert(computeImports != null);

_container = null;
_computeImports = computeImports;
}

internal NamespaceOrTypeSymbol Container
{
get
{
return _container;
}
}

private bool IsSubmissionClass
{
get { return (_container?.Kind == SymbolKind.NamedType) && ((NamedTypeSymbol)_container).IsSubmissionClass; }
}

internal override Imports GetImports(ConsList<TypeSymbol> basesBeingResolved)
{
Debug.Assert(_lazyImports != null || _computeImports != null, "Have neither imports nor a way to compute them.");

if (_lazyImports == null)
{
Interlocked.CompareExchange(ref _lazyImports, _computeImports(basesBeingResolved), null);
}

return _lazyImports;
}

/// <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)
{
var imports = GetImports(basesBeingResolved: null);
foreach (var typeOrNamespace in imports.Usings)
{
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 ImportChain ImportChain
{
get
{
if (_lazyImportChain == null)
{
ImportChain importChain = this.Next.ImportChain;
if ((object)_container == null || _container.Kind == SymbolKind.Namespace)
{
importChain = new ImportChain(GetImports(basesBeingResolved: null), importChain);
}

Interlocked.CompareExchange(ref _lazyImportChain, importChain, null);
}

Debug.Assert(_lazyImportChain != null);

return _lazyImportChain;
}
}

/// <summary>
/// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for certain attribute applications in context of this binder.
/// </summary>
internal override QuickAttributeChecker QuickAttributeChecker
{
get
{
if (_lazyQuickAttributeChecker == null)
{
QuickAttributeChecker result = this.Next.QuickAttributeChecker;

if ((object)_container == null || _container.Kind == SymbolKind.Namespace)
{
result = result.AddAliasesIfAny(_usingsSyntax);
}

_lazyQuickAttributeChecker = result;
}

return _lazyQuickAttributeChecker;
}
}

internal override bool SupportsExtensionMethods
{
get { return true; }
}

internal override void GetCandidateExtensionMethods(
ArrayBuilder<MethodSymbol> methods,
string name,
int arity,
LookupOptions options,
Binder originalBinder)
{
this.GetImports(basesBeingResolved: null).LookupExtensionMethodsInUsings(methods, name, arity, options, originalBinder);
}

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);

if (IsSubmissionClass)
{
// Submission imports are handled by LookupMembersInternal in InContainerBinder.LookupSymbolsInSingleBinder.
return;
}

var imports = GetImports(basesBeingResolved);

// Try using aliases or symbols in imported namespaces
imports.LookupSymbol(originalBinder, result, name, arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
}

protected 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.
// Submission imports are handled by AddMemberLookupSymbolsInfo in InContainerBinder.AddLookupSymbolsInfoInSingleBinder.
if (!IsSubmissionClass && ((options & LookupOptions.LabelsOnly) == 0))
{
var imports = GetImports(basesBeingResolved: null);
imports.AddLookupSymbolsInfo(result, options, originalBinder);
}
}

protected override SourceLocalSymbol LookupLocal(SyntaxToken nameToken)
{
return null;
}

protected override LocalFunctionSymbol LookupLocalFunction(SyntaxToken nameToken)
{
return null;
}

internal override uint LocalScopeDepth => Binder.ExternalScope;
}
}
Loading