Skip to content

C# emitter: <param> documentation read from LastContractView silently drops <see cref> references #11768

Description

@JoshLove-msft

Describe the bug

NamedTypeSymbolProvider.GetParameterXmlDocumentation extracts <param> documentation with XElement.Value, which concatenates text nodes only. <see cref="..."/> is an empty element with no text content, so every cross-reference in a parameter doc is silently deleted when a type is read back from a LastContractView.

https://github.com/microsoft/typespec/blob/main/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/NamedTypeSymbolProvider.cs

var paramElement = xmlDoc.Descendants("param")
                         .FirstOrDefault(e => e.Attribute("name")?.Value == parameterSymbol.Name);

return paramElement?.Value.Trim();   // <-- drops <see cref="..."/>

The sibling method GetSymbolXmlDoc (used for <summary> and <returns>) does not have this problem, because it routes through ProcessXmlContent, which special-cases see and rebuilds the tag:

string processedContent = ProcessXmlContent(tagElement);
return FormattableStringHelpers.FromString(processedContent);

So summaries keep their crefs and parameters lose them.

Reproduction

var xml = """
    <doc><param name="p">Please note <see cref="T:Azure.Foo"/> is the base class.
                The available derived classes include <see cref="T:Azure.A"/> and <see cref="T:Azure.B"/>.</param></doc>
    """;
var p = XDocument.Parse(xml).Descendants("param").First();
Console.WriteLine(p.Value.Trim());

Actual:

Please note  is the base class.
            The available derived classes include  and .

Expected: the <see cref="..."/> references preserved, as ProcessXmlContent already does for summaries.

Real-world impact

This is observable in shipped Azure SDK output. sdk/network/Azure.ResourceManager.Network/src/Generated/ArmNetworkModelFactory.cs on Azure/azure-sdk-for-net@30d826a5d9b contains, inside <param name="ruleCollections">:

/// Please note  is the base class. According to the scenario, a derived class of the base class might need to be assigned here, ...
/// The available derived classes include  and .

Two separate defects compound here:

  1. Lost crefs (this issue) — the type references are gone and cannot be recovered, because the doc is reconstituted from text rather than from structured data. Note that even the summary path is lossy in a weaker sense: FormattableStringHelpers.FromString yields a zero-argument FormattableString, so a cref survives only as literal text, never as a CSharpType reference.
  2. Retained indentation.Trim() only trims the ends, so the writer's continuation indentation is preserved inside the string and re-indented on the next regeneration, growing by 12 spaces per round-trip. Tracked for the management emitter in [Mgmt generator] Model factory regeneration cumulatively adds whitespace to multiline XML docs Azure/azure-sdk-for-net#62444 and fixed there ([mgmt] Regenerate model factory back-compat docs instead of reusing last contract docs Azure/azure-sdk-for-net#62446) by regenerating parameter docs from the current model instead of reusing last-contract text.

Fixing (1) would be a matter of calling ProcessXmlContent(paramElement) rather than paramElement.Value, which would also make the two extraction paths consistent.

Checklist

  • Follow our Code of Conduct
  • Check that there isn't already an issue that reports the same bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingemitter:client:csharpIssue for the C# client emitter: @typespec/http-client-csharp

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions