From 8861cc513a3d847ad6513695cdfa8fa90181b09f Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 22 May 2025 15:04:47 +0100 Subject: [PATCH 1/3] Handle langword for see elements Handle `` references in XML documentation as inline code. Resolves #61042. --- src/OpenApi/gen/XmlComments/XmlComment.cs | 46 ++++++++++++++++--- .../CompletenessTests.cs | 34 ++++++++++++++ ...ApiXmlCommentSupport.generated.verified.cs | 4 +- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/OpenApi/gen/XmlComments/XmlComment.cs b/src/OpenApi/gen/XmlComments/XmlComment.cs index be47fa913d82..5aac299371b7 100644 --- a/src/OpenApi/gen/XmlComments/XmlComment.cs +++ b/src/OpenApi/gen/XmlComments/XmlComment.cs @@ -35,8 +35,11 @@ private XmlComment(Compilation compilation, string xml) // Transform triple slash comment var doc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); - ResolveCrefLink(compilation, doc, $"//{DocumentationCommentXmlNames.SeeAlsoElementName}[@cref]"); - ResolveCrefLink(compilation, doc, $"//{DocumentationCommentXmlNames.SeeElementName}[@cref]"); + ResolveCrefLink(compilation, doc, DocumentationCommentXmlNames.SeeAlsoElementName); + ResolveCrefLink(compilation, doc, DocumentationCommentXmlNames.SeeElementName); + + ResolveLangKeyword(doc, DocumentationCommentXmlNames.SeeElementName); + // Resolve and tags into bullets ResolveListTags(doc); // Resolve tags into code blocks @@ -171,18 +174,20 @@ private static void ResolveParaTags(XDocument document) /// /// The compilation to resolve type symbol declarations from. /// The target node to process crefs in. - /// The node type to process crefs for, can be `see` or `seealso`. - private static void ResolveCrefLink(Compilation compilation, XNode node, string nodeSelector) + /// The node type to process crefs for, can be `see` or `seealso`. + private static void ResolveCrefLink(Compilation compilation, XNode node, string elementName) { - if (node == null || string.IsNullOrEmpty(nodeSelector)) + if (node == null || string.IsNullOrEmpty(elementName)) { return; } - var nodes = node.XPathSelectElements(nodeSelector + "[@cref]").ToArray(); + var attributeName = DocumentationCommentXmlNames.CrefAttributeName; + + var nodes = node.XPathSelectElements($"//{elementName}[@{attributeName}]").ToArray(); foreach (var item in nodes) { - var cref = item.Attribute(DocumentationCommentXmlNames.CrefAttributeName).Value; + var cref = item.Attribute(attributeName).Value; if (string.IsNullOrEmpty(cref)) { continue; @@ -197,6 +202,33 @@ private static void ResolveCrefLink(Compilation compilation, XNode node, string } } + /// + /// Resolves the links in the XML documentation into type names. + /// + /// The target node to process crefs in. + /// The node type to process crefs for, can be `see` or `seealso`. + private static void ResolveLangKeyword(XNode node, string elementName) + { + if (node == null || string.IsNullOrEmpty(elementName)) + { + return; + } + + var attributeName = DocumentationCommentXmlNames.LangwordAttributeName; + + var nodes = node.XPathSelectElements($"//{elementName}[@{attributeName}]").ToArray(); + foreach (var item in nodes) + { + var langword = item.Attribute(attributeName).Value; + if (string.IsNullOrEmpty(langword)) + { + continue; + } + + item.ReplaceWith(new XText($"`{langword}`")); + } + } + private static IEnumerable GetMultipleExampleNodes(XPathNavigator navigator, string selector) { var iterator = navigator.Select(selector); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs index cb3dce2068d4..b51dce51624e 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Net.Http; +using Microsoft.OpenApi.Models; namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests; @@ -437,6 +438,39 @@ public static T GetGenericValue(T para) return para; } } + +/// +/// A class that implements the interface. +/// +public class DisposableType : IDisposable +{ + /// + /// Finalizes an instance of the class. + /// + ~DisposableType() + { + Dispose(false); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + // No-op + } +} """; var generator = new XmlCommentGenerator(); await SnapshotTestHelper.Verify(source, generator, out var compilation); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs index 1eb103609a36..5012ebb84426 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs @@ -148,7 +148,8 @@ type as a cref attribute. generic types to open generics for use in typeof expressions.", null, null, null, null, false, null, null, null)); cache.Add(@"T:ParamsAndParamRefs", new XmlComment(@"This shows examples of typeparamref and typeparam tags", null, null, null, null, false, null, null, null)); - cache.Add(@"P:ExampleClass.Label", new XmlComment(null, null, @" The string? ExampleClass.Label is a + cache.Add(@"T:DisposableType", new XmlComment(@"A class that implements the IDisposable interface.", null, null, null, null, false, null, null, null)); + cache.Add(@"P:ExampleClass.Label", new XmlComment(null, null, @" The string? ExampleClass.Label is a `string` that you use for a label. Note that there isn't a way to provide a ""cref"" to each accessor, only to the property itself.", null, @"The `Label` property represents a label @@ -191,6 +192,7 @@ that implement this interface when the method as a cref attribute. The parameter and return value are both of an arbitrary type, T", null, null, false, null, null, null)); + cache.Add(@"M:DisposableType.Dispose", new XmlComment(null, null, null, null, null, false, null, null, null)); return cache; } From 4d14c339f6ba1ac8864793251df5d3ed70641f6c Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 22 May 2025 15:07:35 +0100 Subject: [PATCH 2/3] Remove redundant using Added back by merge/rebase but not needed. --- .../CompletenessTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs index b51dce51624e..d28673403b22 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Net.Http; -using Microsoft.OpenApi.Models; namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests; From 747213746ef53146a847c7fcff97b5bb84da600e Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Thu, 22 May 2025 17:29:32 +0100 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Safia Abdalla --- src/OpenApi/gen/XmlComments/XmlComment.cs | 4 ++-- .../CompletenessTests.cs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/OpenApi/gen/XmlComments/XmlComment.cs b/src/OpenApi/gen/XmlComments/XmlComment.cs index 5aac299371b7..0ec1f073f9b2 100644 --- a/src/OpenApi/gen/XmlComments/XmlComment.cs +++ b/src/OpenApi/gen/XmlComments/XmlComment.cs @@ -203,10 +203,10 @@ private static void ResolveCrefLink(Compilation compilation, XNode node, string } /// - /// Resolves the links in the XML documentation into type names. + /// Resolves the links in the XML documentation into language keywords. /// /// The target node to process crefs in. - /// The node type to process crefs for, can be `see` or `seealso`. + /// The node type to process langwords for, can be `see` or `seealso`. private static void ResolveLangKeyword(XNode node, string elementName) { if (node == null || string.IsNullOrEmpty(elementName)) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs index d28673403b22..82ae70006c10 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs @@ -464,6 +464,7 @@ public void Dispose() /// /// to release both managed and unmanaged resources; /// to release only unmanaged resources. + /// to indicate a no-op. /// protected virtual void Dispose(bool disposing) {