-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Extensions: update semantic model APIs #77619
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
Changes from 2 commits
d73d99b
83fe1df
693d896
70be557
3930175
8fad903
72216d8
6317607
e3af843
95f8dec
f4d9568
14d407d
d356861
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,13 +44,19 @@ internal void LookupSymbolsSimpleName( | |
} | ||
} | ||
|
||
internal void LookupExtensionMethods(LookupResult result, string name, int arity, LookupOptions options, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) | ||
#nullable enable | ||
internal void LookupAllExtensions(LookupResult result, TypeSymbol receiverType, string? name, LookupOptions options) | ||
{ | ||
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; | ||
|
||
foreach (var scope in new ExtensionScopes(this)) | ||
{ | ||
this.LookupExtensionMethodsInSingleBinder(scope, result, name, arity, options, ref useSiteInfo); | ||
scope.Binder.LookupAllExtensionMembersInSingleBinder( | ||
result, receiverType, name, arity: 0, | ||
options: options, originalBinder: this, classicExtensionUseSiteInfo: ref discardedUseSiteInfo); | ||
} | ||
} | ||
#nullable disable | ||
|
||
/// <summary> | ||
/// Look for any symbols in scope with the given name and arity. | ||
|
@@ -177,34 +183,72 @@ protected void LookupMembersInternal(LookupResult result, NamespaceOrTypeSymbol | |
} | ||
|
||
#nullable enable | ||
private void LookupExtensionMembersInSingleBinder(LookupResult result, TypeSymbol receiverType, | ||
string name, int arity, ConsList<TypeSymbol>? basesBeingResolved, LookupOptions options, | ||
Binder originalBinder, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) | ||
private void LookupAllExtensionMembersInSingleBinder(LookupResult result, TypeSymbol receiverType, | ||
string? name, int arity, LookupOptions options, Binder originalBinder, | ||
ref CompoundUseSiteInfo<AssemblySymbol> classicExtensionUseSiteInfo) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
Debug.Assert(name is not null); | ||
var singleLookupResults = ArrayBuilder<SingleLookupResult>.GetInstance(); | ||
EnumerateAllExtensionMembersInSingleBinder(singleLookupResults, receiverType, name, arity, options, originalBinder, ref classicExtensionUseSiteInfo); | ||
foreach (var singleLookupResult in singleLookupResults) | ||
{ | ||
result.MergeEqual(singleLookupResult); | ||
} | ||
|
||
var extensions = ArrayBuilder<NamedTypeSymbol>.GetInstance(); | ||
singleLookupResults.Free(); | ||
} | ||
|
||
Debug.Assert(!receiverType.IsDynamic()); | ||
if (!receiverType.IsErrorType()) | ||
internal void EnumerateAllExtensionMembersInSingleBinder(ArrayBuilder<SingleLookupResult> result, | ||
TypeSymbol receiverType, string? name, int arity, LookupOptions options, Binder originalBinder, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that! Thanks |
||
ref CompoundUseSiteInfo<AssemblySymbol> classicExtensionUseSiteInfo) | ||
{ | ||
// 1. Collect new extension members | ||
PooledHashSet<MethodSymbol>? implementationsToShadow = null; | ||
if (!receiverType.IsErrorType() && !receiverType.IsDynamic()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
this.GetExtensionDeclarations(extensions, originalBinder); | ||
var extensionDeclarations = ArrayBuilder<NamedTypeSymbol>.GetInstance(); | ||
this.GetExtensionDeclarations(extensionDeclarations, originalBinder); | ||
|
||
foreach (NamedTypeSymbol extensionDeclaration in extensionDeclarations) | ||
{ | ||
var candidates = name is null ? extensionDeclaration.GetMembers() : extensionDeclaration.GetMembers(name); | ||
|
||
foreach (var candidate in candidates) | ||
{ | ||
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(candidate, arity, options, null, diagnose: true, useSiteInfo: ref classicExtensionUseSiteInfo); | ||
result.Add(resultOfThisMember); | ||
|
||
if (!candidate.IsStatic) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
implementationsToShadow ??= PooledHashSet<MethodSymbol>.GetInstance(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
if (candidate is MethodSymbol { IsStatic: false } shadows && | ||
shadows.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is { } toShadow) | ||
{ | ||
implementationsToShadow.Add(toShadow); | ||
} | ||
} | ||
} | ||
} | ||
|
||
extensionDeclarations.Free(); | ||
} | ||
|
||
var tempResult = LookupResult.GetInstance(); | ||
foreach (NamedTypeSymbol extension in extensions) | ||
{ | ||
// No need for "diagnose" since we discard the lookup results (and associated diagnostic info) | ||
// unless the results are good. | ||
LookupMembersInClass(tempResult, extension, name, arity, basesBeingResolved, | ||
options, originalBinder, diagnose: false, ref useSiteInfo); | ||
// 2. Collect classic extension methods | ||
var extensionMethods = ArrayBuilder<MethodSymbol>.GetInstance(); | ||
this.GetCandidateExtensionMethods(extensionMethods, name, arity, options, originalBinder: originalBinder); | ||
|
||
result.MergeEqual(tempResult); | ||
tempResult.Clear(); | ||
foreach (var method in extensionMethods) | ||
{ | ||
// Prefer instance extension declarations vs. their implementations | ||
if (implementationsToShadow is null || !implementationsToShadow.Remove(method.OriginalDefinition)) | ||
{ | ||
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(method, arity, options, null, diagnose: true, useSiteInfo: ref classicExtensionUseSiteInfo); | ||
result.Add(resultOfThisMember); | ||
} | ||
} | ||
|
||
tempResult.Free(); | ||
extensions.Free(); | ||
extensionMethods.Free(); | ||
implementationsToShadow?.Free(); | ||
} | ||
#nullable disable | ||
|
||
|
@@ -484,6 +528,7 @@ private static void LookupMembersInNamespace(LookupResult result, NamespaceSymbo | |
} | ||
} | ||
|
||
// PROTOTYPE we should be able to remove this method once all the callers are updated to account for new extension members | ||
/// <summary> | ||
/// Lookup extension methods by name and arity in the given binder and | ||
/// check viability in this binder. The lookup is performed on a single | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,41 +47,15 @@ internal void PopulateWithExtensionMethods( | |
this.PopulateHelper(receiverOpt, resultKind, error); | ||
this.IsExtensionMethodGroup = true; | ||
|
||
PooledHashSet<MethodSymbol> implementationsToShadow = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 this deduplication between skeleton and implementation method is now handled upstream, in |
||
|
||
if (members.Any(static m => m is MethodSymbol { IsExtensionMethod: true }) && | ||
members.Any(static m => m is MethodSymbol { IsExtensionMethod: false, IsStatic: false })) | ||
{ | ||
implementationsToShadow = PooledHashSet<MethodSymbol>.GetInstance(); | ||
|
||
foreach (var member in members) | ||
{ | ||
if (member is MethodSymbol { IsExtensionMethod: false, IsStatic: false } shadows && | ||
shadows.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is { } toShadow) | ||
{ | ||
implementationsToShadow.Add(toShadow); | ||
} | ||
} | ||
} | ||
|
||
foreach (var member in members) | ||
{ | ||
if (member is MethodSymbol method) | ||
{ | ||
Debug.Assert(method.IsExtensionMethod || method.GetIsNewExtensionMember()); | ||
|
||
// Prefer instance extension declarations vs. their implementations | ||
if (method.IsExtensionMethod && implementationsToShadow?.Remove(method.OriginalDefinition) == true) | ||
{ | ||
continue; | ||
} | ||
|
||
this.Methods.Add(method); | ||
} | ||
} | ||
|
||
implementationsToShadow?.Free(); | ||
|
||
if (!typeArguments.IsDefault) | ||
{ | ||
this.TypeArguments.AddRange(typeArguments); | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we testing this code path? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The interesting test is
PropertyAccess_NotAmbiguousWithInapplicableMethod