EWSoftware / SHFB

Sandcastle Help File Builder (SHFB). A standalone GUI, Visual Studio integration package, and MSBuild tasks providing full configuration and extensibility for building help files with the Sandcastle tools.
Other
2.19k stars 369 forks source link

Overload not showing up in descendant class member list. #968

Closed JeffreySax closed 1 year ago

JeffreySax commented 1 year ago

Given the following minimal library code:

namespace MRefBuilderGetTemplateMemberFail
{
    /// <summary>Class A.</summary>
    public class A<T>
    {
        /// <summary>Method M, TUT variant.</summary>
        public int M<U>(Func<T, U, T> f, int x) => x;
        /// <summary>Method M, UTT variant.</summary>
        public int M<U>(Func<U, T, T> f, int x) => x;
    }

    /// <summary>Class B</summary>
    public class B<T> : A<T>
    { }
}

In the SHFB output for this project, the second overload (with the Func<U, T, T> parameter) does not show up in the member list for B<T>.

I was able to track the problem down to MRefBuilder's Member.GetTemplateMember method which apparently returns the first overload as the template for the second overload.

I don't understand the reasoning behind how this is done. In MRefWriter.WriteTypeElements, you first create a MemberDictionary whose constructor adds members from ancestors that were not overloaded. Then it seems that for each member you just added, you go through some elaborate and error-prone process (in Member.GetTemplateMember) to trace it back to its original definition.

EWSoftware commented 1 year ago

Template matching is rather complicated and what's there is what I've been able to figure out on my own when fixing cases as they're reported (see the change history in Reflection.cs). Microsoft wrote the original code and there isn't any documentation on how it works so it was mostly trial and error trying figure out where the necessary information resided and how to use it.

This looks to be a variation on a prior fix in ReflectionUtilities.ParametersMatch (MyBaseClass<T, T> vs MyBaseClass<T, U>). At a guess, it probably isn't handling the type parameter check correctly in this case for some reason. I'll take a look at it this weekend if I get some time.