dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.13k stars 4.04k forks source link

"Go To Definition" on re-abstraction doesn't go the re-abstracted member #61268

Open AlekseyTs opened 2 years ago

AlekseyTs commented 2 years ago
interface I1<T1> where T1 : I1<T1>
{
    static abstract void M1();
    void M3();
}

interface I3<T3> : I1<T3> where T3 : I3<T3>
{
    static abstract void I1<T3>.M1();
    abstract void I1<T3>.M3();
}

Try using "Go To Definition" on abstract explicit implementations.

Observed: Doesn't work.

Expected: Should go to the corresponding definition in I1.

AlekseyTs commented 2 years ago

CC @sharwell

CyrusNajmabadi commented 3 days ago

@AlekseyTs I debugged into this on the IDE side. We make a call here to determine what interface members a particular member is an implementation of:

    public static ImmutableArray<ISymbol> ExplicitOrImplicitInterfaceImplementations(this ISymbol symbol)
    {
        if (symbol.Kind is not SymbolKind.Method and not SymbolKind.Property and not SymbolKind.Event)
            return [];

        var containingType = symbol.ContainingType;
        var query = from iface in containingType.AllInterfaces
                    from interfaceMember in iface.GetMembers()
                    let impl = containingType.FindImplementationForInterfaceMember(interfaceMember)
                    where symbol.Equals(impl)
                    select interfaceMember;
        return query.ToImmutableArray();
    }

However, in the bowels of FindImplementationForInterfaceMember it eventually gets to: TypeSymbol.FindMostSpecificImplementation which does this:

                case 1:
                    {
                        Symbol result = implementingMember.Single();

                        if (result.IsAbstract)
                        {
                            return null;
                        }

As this member is abstract, it's not considered an implementation of the interface member.

Is this the expected behavior of this API? If so, do you have a recommendation on how to get the originating interface member that an explicit abstract member is an implementation of?