Skip to content
Draft
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
71 changes: 39 additions & 32 deletions meetings/working-groups/extensions/Extension-API-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,23 +155,32 @@ The unspeakable skeleton provides the prototypes for the extension members and t
See the following code and XML for an example of extension members and the resulting XML output.

```csharp
/// <summary>Summary for E</summary>
static class E
{
/// <summary>Summary for extension block</summary>
/// <typeparam name="T">Description for T</typeparam>
/// <param name="t">Description for t</param>
extension<T>(T t)
public static class E
{
/// <summary>Summary for M</summary>
/// <typeparam name="U">Description for U</typeparam>
/// <param name="u">Description for u</param>
public void M<U>(U u) => throw null!;

/// <summary>Summary for P</summary>
public int P => 0;
/// <summary>
/// Represents a sequence of integers with additional operations.
/// </summary>
extension(IEnumerable<int> sequence)
{
/// <summary>
/// Adds a value to each element in the sequence.
/// </summary>
/// <param name="operand"></param>
/// <returns></returns>
public IEnumerable<int> AddValue(int operand)
{
foreach (var item in sequence)
{
yield return item + operand;
}
}

/// <summary>
/// Calculates the median value of the sequence.
/// </summary>
public int Median => 0;
}
}
}
```

produces the following XML output:
Expand All @@ -183,27 +192,25 @@ produces the following XML output:
<name>Test</name>
</assembly>
<members>
<member name="T:E">
<member name="T:ClassLibrary6.E">
<summary>Summary for E</summary>
</member>
<member name="T:E.<>E__0`1">
<summary>Summary for extension block</summary>
<typeparam name="T">Description for T</typeparam>
<param name="t">Description for t</param>
</member>
<member name="M:E.<>E__0`1.M``1(``0)">
<summary>Summary for M</summary>
<typeparam name="U">Description for U</typeparam>
<param name="u">Description for u</param>
</member>
<member name="P:E.<>E__0`1.P">
<summary>Summary for P</summary>
<member name="T:ClassLibrary6.E.">
<summary>
Represents a sequence of integers with additional operations.
</summary>
</member>
<member name="M:E.M``2(``0,``1)">
<inheritdoc cref="M:E.<>E__0`1.M``1(``0)"/>
<member name="M:ClassLibrary6.E..AddValue(System.Int32)">
Copy link
Member

Choose a reason for hiding this comment

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

This is the wrong expectation. The correct output is <member name="M:ClassLibrary6.E.&lt;&gt;E__0.AddValue(System.Int32)">.

The same comment applies elsewhere: the extension block should appear with its synthesized name (<>E__ followed by an index) and the synthesized name should be escaped (fix pending).

Copy link
Member

Choose a reason for hiding this comment

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

The metadata design for extensions was updated to use content-based grouping and marker type names. Here's an updated example showing the impact on xml docs:


    [Fact]
    public void XmlDoc_01()
    {
        // Instance members
        var src = """
/// <summary>Summary for E</summary>
static class E
{
    /// <summary>Summary for extension block</summary>
    /// <typeparam name="T">Description for T</typeparam>
    /// <param name="t">Description for t</param>
    extension<T>(T t)
    {
        /// <summary>Summary for M</summary>
        /// <typeparam name="U">Description for U</typeparam>
        /// <param name="u">Description for u</param>
        public void M<U>(U u) => throw null!;

        /// <summary>Summary for P</summary>
        public int P => 0;
    }
}
""";
        var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments, assemblyName: "Test");
        comp.VerifyEmitDiagnostics();

        var e = comp.GetMember<NamedTypeSymbol>("E");
        AssertEx.Equal("""
<member name="T:E">
    <summary>Summary for E</summary>
</member>

""", e.GetDocumentationCommentXml());

        var extension = e.GetTypeMembers().Single();
        AssertEx.Equal("T:E.<G>$8048A6C8BE30A622530249B904B537EB.<M>$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId());
        AssertEx.Equal("""
<member name="T:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.&lt;M&gt;$D1693D81A12E8DED4ED68FE22D9E856F">
    <summary>Summary for extension block</summary>
    <typeparam name="T">Description for T</typeparam>
    <param name="t">Description for t</param>
</member>

""", extension.GetDocumentationCommentXml());

        var mSkeleton = extension.GetMember<MethodSymbol>("M");
        AssertEx.Equal("""
<member name="M:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.M``1(``0)">
    <summary>Summary for M</summary>
    <typeparam name="U">Description for U</typeparam>
    <param name="u">Description for u</param>
</member>

""", mSkeleton.GetDocumentationCommentXml());

        var mImplementation = e.GetMember<MethodSymbol>("M");
        AssertEx.Equal("""
<member name="M:E.M``2(``0,``1)">
    <inheritdoc cref="M:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.M``1(``0)"/>
</member>

""", mImplementation.GetDocumentationCommentXml());

        var p = extension.GetMember<PropertySymbol>("P");
        AssertEx.Equal("""
<member name="P:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.P">
    <summary>Summary for P</summary>
</member>

""", p.GetDocumentationCommentXml());

        var pGetImplementation = e.GetMember<MethodSymbol>("get_P");
        AssertEx.Equal("""
<member name="M:E.get_P``1(``0)">
    <inheritdoc cref="P:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.P"/>
</member>

""", pGetImplementation.GetDocumentationCommentXml());

        var expected = """
<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Test</name>
    </assembly>
    <members>
        <member name="T:E">
            <summary>Summary for E</summary>
        </member>
        <member name="M:E.M``2(``0,``1)">
            <inheritdoc cref="M:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.M``1(``0)"/>
        </member>
        <member name="M:E.get_P``1(``0)">
            <inheritdoc cref="P:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.P"/>
        </member>
        <member name="T:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.&lt;M&gt;$D1693D81A12E8DED4ED68FE22D9E856F">
            <summary>Summary for extension block</summary>
            <typeparam name="T">Description for T</typeparam>
            <param name="t">Description for t</param>
        </member>
        <member name="M:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.M``1(``0)">
            <summary>Summary for M</summary>
            <typeparam name="U">Description for U</typeparam>
            <param name="u">Description for u</param>
        </member>
        <member name="P:E.&lt;G&gt;$8048A6C8BE30A622530249B904B537EB.P">
            <summary>Summary for P</summary>
        </member>
    </members>
</doc>
""";
        AssertEx.Equal(expected, GetDocumentationCommentText(comp));

        var tree = comp.SyntaxTrees.Single();
        var model = comp.GetSemanticModel(tree);
        AssertEx.SequenceEqual(["(T, T)", "(t, T t)", "(U, U)", "(u, U u)"], PrintXmlNameSymbols(tree, model));
    }

<summary>
Adds a value to each element in the sequence.
</summary>
<param name="operand"></param>
<returns></returns>
</member>
<member name="M:E.get_P``1(``0)">
<inheritdoc cref="P:E.<>E__0`1.P"/>
<member name="P:ClassLibrary6.E..Median">
<summary>
Calculates the median value of the sequence.
</summary>
</member>
</members>
</doc>
Expand Down