Extensions: FindReferences should link extension members and implementations#81685
Extensions: FindReferences should link extension members and implementations#81685jcouv merged 7 commits intodotnet:mainfrom
Conversation
| { | ||
| // If the given symbol is an extension member, cascade to its implementation method | ||
| if (symbol.AssociatedExtensionImplementation is { } associatedExtensionImplementation) | ||
| result.Add(associatedExtensionImplementation); |
There was a problem hiding this comment.
can likely inline this method, and just replace it with result.AddIfNotNull(symbol.AssociatedExtensionImplementation) #Resolved
| private static void CascadeFromExtensionImplementation(IMethodSymbol symbol, ArrayBuilder<ISymbol> result) | ||
| { | ||
| // If the given symbol is an implementation method of an extension member, cascade to the extension member itself | ||
| var containingType = symbol.ContainingType; |
There was a problem hiding this comment.
since we're going from teh classic Foo(this whatever) method to the modern extension, can't you just check if the .MethodKind is Extension here instead of the MightContainExtensionMethods bit? #Resolved
There was a problem hiding this comment.
The implementation method is not necessarily an extension method. It's only an extension method if the extension member is a non-static extension method.
If it's a static extension method or a property accessor, the implementation method is not an extension method.
So you can't do 42.get_P() for instance, you can only do E.get_P(42).
There was a problem hiding this comment.
Can we at least check and only do this if the member is implicitly declared by the compiler? It feels weird to do this search for all members, including normal user ones.
| if (containingType is null || !containingType.MightContainExtensionMethods || !symbol.IsStatic) | ||
| return; | ||
|
|
||
| var implementationDefinition = symbol.OriginalDefinition; |
There was a problem hiding this comment.
i don't think you need to do .OriginalDefinition within the REfFinder code. Everything is OriginalDefs here. #Resolved
| if (associated is null) | ||
| continue; | ||
|
|
||
| if (!object.ReferenceEquals(associated.OriginalDefinition, implementationDefinition)) | ||
| continue; |
There was a problem hiding this comment.
| if (associated is null) | |
| continue; | |
| if (!object.ReferenceEquals(associated.OriginalDefinition, implementationDefinition)) | |
| continue; | |
| if (!Equals(associated, implementationDefinition)) | |
| continue; |
(we don't use RefEquals or == for symbols in IDE layer).
#Resolved
| result.Add(method); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
note: having this reverse mapping be available from the copielr would be nice (fi the methods are linked at that level). having to do the search like this feels not great. of course, if it's not available, this is fine. #Resolved
There was a problem hiding this comment.
Such mapping isn't available (the compiler doesn't keep a map to easily expose a public API for this)
There was a problem hiding this comment.
Filed tracking issue #81686 to collect usage scenarios where such an API would be useful
| { | ||
| // If the given symbol is an extension accessor, cascade to its implementation method | ||
| if (symbol.AssociatedExtensionImplementation is { } associatedExtensionImplementation) | ||
| result.Add(associatedExtensionImplementation); |
There was a problem hiding this comment.
like above. inline this and replace with:
result.AddIfNotNull(symbol.AssociatedExtensionImplementation);
``` #Resolved
CyrusNajmabadi
left a comment
There was a problem hiding this comment.
Signing off with feedback. Would love an actual compiler api to do some of the contorted stuff.
|
@CyrusNajmabadi I'd missed operator scenarios. Added |
| // If the given symbol is an implementation method of an extension member, cascade to the extension member itself | ||
| var containingType = symbol.ContainingType; | ||
| if (containingType is null || !containingType.MightContainExtensionMethods || !symbol.IsStatic) | ||
| if (containingType is null || !containingType.MightContainExtensionMethods || !symbol.IsStatic || !symbol.IsImplicitlyDeclared) |
There was a problem hiding this comment.
if (symbol is not { IsStatic: true, IsImplicitlyDeclared: true ContainingType.MightContainExtensionMethods: true })
return;
``` #Resolved
| if (associated is null) | ||
| continue; | ||
|
|
There was a problem hiding this comment.
| if (associated is null) | |
| continue; | |
| ``` #Resolved |
| if (!options.AssociatePropertyReferencesWithSpecificAccessor && symbol.AssociatedSymbol != null) | ||
| result.Add(symbol.AssociatedSymbol); |
There was a problem hiding this comment.
if (!options.AssociatePropertyReferencesWithSpecificAccessor)
result.AddIfNotNull(symbol.AssociatedSymbol)
``` #Resolved
| result.Add(symbol.AssociatedSymbol); | ||
|
|
||
| // If the given symbol is an extension accessor, cascade to its implementation method | ||
| result.AddIfNotNull(symbol.AssociatedExtensionImplementation); |
There was a problem hiding this comment.
what cascades abck from teh impl method to the extension accessor? #Resolved
There was a problem hiding this comment.
That's handled by OrdinaryMethodReferenceFinder since the implementation method is an ordinary method. Then the iterative process of cascading should make the cascading transitive.
|
|
||
| foreach (var member in nestedType.GetMembers()) | ||
| { | ||
| if (member is IMethodSymbol method) |
There was a problem hiding this comment.
is this ok that we filtered down to just methods? couldn't an impl method for a property map back to an property?
or will it map back to the accessor? which then maps back to the property? #Resolved
There was a problem hiding this comment.
The latter. From my understanding cascading is an iterative process (see DetermineInitialSearchSymbolsAsync)
There was a problem hiding this comment.
ok great. thanks :)
| if (symbol.AssociatedExtensionImplementation is { } associatedExtensionMethod) | ||
| { | ||
| // If given an extension operator `E.extension(...).operator+`, we cascade to the its implementation method `E.op_Addition(...)` | ||
| return new([associatedExtensionMethod]); | ||
| } | ||
|
|
||
| return new([]); |
There was a problem hiding this comment.
| if (symbol.AssociatedExtensionImplementation is { } associatedExtensionMethod) | |
| { | |
| // If given an extension operator `E.extension(...).operator+`, we cascade to the its implementation method `E.op_Addition(...)` | |
| return new([associatedExtensionMethod]); | |
| } | |
| return new([]); | |
| // If given an extension operator `E.extension(...).operator+`, we cascade to the its implementation method `E.op_Addition(...)` | |
| return symbol.AssociatedExtensionImplementation is { } associatedExtensionMethod) | |
| ? new([associatedExtensionMethod]) | |
| : new([]); | |
| ``` #Resolved |
|
minor cleanup feedback. overall looks great. thanks! |
Addresses part of #81507