From afede74db19e600d37db0e30145b5b35e794839f Mon Sep 17 00:00:00 2001
From: Safia Abdalla <safia@safia.rocks>
Date: Mon, 24 Mar 2025 15:15:33 -0700
Subject: [PATCH 1/7] Harden handling for generic type arguments in source
 generator

---
 src/OpenApi/gen/XmlComments/MemberKey.cs      | 232 +++++++++++++-----
 .../CompletenessTests.cs                      |  88 +++++++
 ...ApiXmlCommentSupport.generated.verified.cs |  14 +-
 ...ApiXmlCommentSupport.generated.verified.cs |   4 +-
 4 files changed, 271 insertions(+), 67 deletions(-)

diff --git a/src/OpenApi/gen/XmlComments/MemberKey.cs b/src/OpenApi/gen/XmlComments/MemberKey.cs
index 9117d02af393..3d492feb095b 100644
--- a/src/OpenApi/gen/XmlComments/MemberKey.cs
+++ b/src/OpenApi/gen/XmlComments/MemberKey.cs
@@ -3,8 +3,8 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
-using System.Text;
 using Microsoft.CodeAnalysis;
 
 namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Xml;
@@ -14,14 +14,19 @@ internal sealed record MemberKey(
     MemberType MemberKind,
     string? Name,
     string? ReturnType,
-    string[]? Parameters) : IEquatable<MemberKey>
+    List<string>? Parameters) : IEquatable<MemberKey>
 {
-    private static readonly SymbolDisplayFormat _typeKeyFormat = new(
+    private static readonly SymbolDisplayFormat _withTypeParametersFormat = new(
         globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
         typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
         genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters);
 
-    public static MemberKey FromMethodSymbol(IMethodSymbol method, Compilation compilation)
+    private static readonly SymbolDisplayFormat _sansTypeParametersFormat = new(
+        globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
+        typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
+        genericsOptions: SymbolDisplayGenericsOptions.None);
+
+    public static MemberKey? FromMethodSymbol(IMethodSymbol method, Compilation compilation)
     {
         string returnType;
         if (method.ReturnsVoid)
@@ -44,94 +49,193 @@ public static MemberKey FromMethodSymbol(IMethodSymbol method, Compilation compi
                 }
             }
 
-            returnType = actualReturnType.TypeKind == TypeKind.TypeParameter
-                ? "typeof(object)"
-                : $"typeof({ReplaceGenericArguments(actualReturnType.ToDisplayString(_typeKeyFormat))})";
+            if (actualReturnType.TypeKind == TypeKind.TypeParameter)
+            {
+                returnType = "typeof(object)";
+            }
+            else if (TryGetFormattedTypeName(actualReturnType, out var formattedReturnType))
+            {
+                returnType = $"typeof({formattedReturnType})";
+            }
+            else
+            {
+                return null;
+            }
         }
 
         // Handle extension methods by skipping the 'this' parameter
-        var parameters = method.Parameters
-            .Where(p => !p.IsThis)
-            .Select(p =>
+        List<string> parameters = [];
+        foreach (var parameter in method.Parameters)
+        {
+            if (parameter.IsThis)
+            {
+                continue;
+            }
+
+            if (parameter.Type.TypeKind == TypeKind.TypeParameter)
+            {
+                parameters.Add("typeof(object)");
+            }
+            else if (parameter.IsParams && parameter.Type is IArrayTypeSymbol arrayType)
             {
-                if (p.Type.TypeKind == TypeKind.TypeParameter)
+                if (TryGetFormattedTypeName(arrayType.ElementType, out var formattedArrayType))
                 {
-                    return "typeof(object)";
+                    parameters.Add($"typeof({formattedArrayType}[])");
                 }
-
-                // For params arrays, use the array type
-                if (p.IsParams && p.Type is IArrayTypeSymbol arrayType)
+                else
                 {
-                    return $"typeof({ReplaceGenericArguments(arrayType.ToDisplayString(_typeKeyFormat))})";
+                    return null;
                 }
+            }
+            else if (TryGetFormattedTypeName(parameter.Type, out var formattedParameterType))
+            {
+                parameters.Add($"typeof({formattedParameterType})");
+            }
+            else
+            {
+                return null;
+            }
+        }
 
-                return $"typeof({ReplaceGenericArguments(p.Type.ToDisplayString(_typeKeyFormat))})";
-            })
-            .ToArray();
-
-        // For generic methods, use the containing type with generic parameters
-        var declaringType = method.ContainingType;
-        var typeDisplay = declaringType.ToDisplayString(_typeKeyFormat);
-
-        // If the method is in a generic type, we need to handle the type parameters
-        if (declaringType.IsGenericType)
+        if (TryGetFormattedTypeName(method.ContainingType, out var formattedDeclaringType))
         {
-            typeDisplay = ReplaceGenericArguments(typeDisplay);
+            return new MemberKey(
+                $"typeof({formattedDeclaringType})",
+                MemberType.Method,
+                method.MetadataName,  // Use MetadataName to match runtime MethodInfo.Name
+                returnType,
+                parameters);
         }
-
-        return new MemberKey(
-            $"typeof({typeDisplay})",
-            MemberType.Method,
-            method.MetadataName,  // Use MetadataName to match runtime MethodInfo.Name
-            returnType,
-            parameters);
+        return null;
     }
 
-    public static MemberKey FromPropertySymbol(IPropertySymbol property)
+    public static MemberKey? FromPropertySymbol(IPropertySymbol property)
     {
-        return new MemberKey(
-            $"typeof({ReplaceGenericArguments(property.ContainingType.ToDisplayString(_typeKeyFormat))})",
-            MemberType.Property,
-            property.Name,
-            null,
-            null);
+        if (TryGetFormattedTypeName(property.ContainingType, out var typeName))
+        {
+            return new MemberKey(
+                $"typeof({typeName})",
+                MemberType.Property,
+                property.Name,
+                null,
+                null);
+        }
+        return null;
     }
 
-    public static MemberKey FromTypeSymbol(INamedTypeSymbol type)
+    public static MemberKey? FromTypeSymbol(INamedTypeSymbol type)
     {
-        return new MemberKey(
-            $"typeof({ReplaceGenericArguments(type.ToDisplayString(_typeKeyFormat))})",
-            MemberType.Type,
-            null,
-            null,
-            null);
+        if (TryGetFormattedTypeName(type, out var typeName))
+        {
+            return new MemberKey(
+                $"typeof({typeName})",
+                MemberType.Type,
+                null,
+                null,
+                null);
+        }
+        return null;
     }
 
     /// Supports replacing generic type arguments to support use of open
     /// generics in `typeof` expressions for the declaring type.
-    private static string ReplaceGenericArguments(string typeName)
+    private static bool TryGetFormattedTypeName(ITypeSymbol typeSymbol, [NotNullWhen(true)] out string? typeName, bool isNestedCall = false)
     {
-        var stack = new Stack<int>();
-        var result = new StringBuilder(typeName);
-        for (var i = 0; i < result.Length; i++)
+        if (typeSymbol is INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } nullableType)
         {
-            if (result[i] == '<')
+            typeName = typeSymbol.ToDisplayString(_withTypeParametersFormat);
+            return true;
+        }
+
+        // Handle tuples specially since they are represented as generic
+        // ValueTuple types and trigger the logic for handling generics in
+        // nested values.
+        if (typeSymbol is INamedTypeSymbol { IsTupleType: true } namedType)
+        {
+            return TryHandleTupleType(namedType, out typeName);
+        }
+
+        if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType)
+        {
+            // If any of the type arguments are type parameters, then they have not
+            // been substituted for a concrete type and we need to model them as open
+            // generics if possible to avoid emitting a type with type parameters that
+            // cannot be used in a typeof expression.
+            var hasTypeParameters = genericType.TypeArguments.Any(t => t.TypeKind == TypeKind.TypeParameter);
+            var baseTypeName = genericType.ToDisplayString(_sansTypeParametersFormat);
+
+            if (!hasTypeParameters)
+            {
+                var typeArgStrings = new List<string>();
+                var allArgumentsResolved = true;
+
+                // Loop through each type argument to handle nested generics.
+                foreach (var typeArg in genericType.TypeArguments)
+                {
+                    if (TryGetFormattedTypeName(typeArg, out var argTypeName, isNestedCall: true))
+                    {
+                        typeArgStrings.Add(argTypeName);
+                    }
+                    else
+                    {
+                        typeName = null;
+                        return false;
+                    }
+                }
+
+                if (allArgumentsResolved)
+                {
+                    typeName = $"{baseTypeName}<{string.Join(", ", typeArgStrings)}>";
+                    return true;
+                }
+            }
+            else
             {
-                stack.Push(i);
+                if (isNestedCall)
+                {
+                    // If this is a nested call, we can't use open generics so there's no way
+                    // for us to emit a member key. Return false and skip over this type in the code
+                    // generation.
+                    typeName = null;
+                    return false;
+                }
+
+                // If we got here, we can successfully emit a member key for the open generic type.
+                var genericArgumentsCount = genericType.TypeArguments.Length;
+                var openGenericsPlaceholder = "<" + new string(',', genericArgumentsCount - 1) + ">";
+
+                typeName = baseTypeName + openGenericsPlaceholder;
+                return true;
             }
-            else if (result[i] == '>' && stack.Count > 0)
+        }
+
+        typeName = typeSymbol.ToDisplayString(_withTypeParametersFormat);
+        return true;
+    }
+
+    private static bool TryHandleTupleType(INamedTypeSymbol tupleType, [NotNullWhen(true)] out string? typeName)
+    {
+        List<string> elementTypes = [];
+        foreach (var element in tupleType.TupleElements)
+        {
+            if (element.Type.TypeKind == TypeKind.TypeParameter)
             {
-                var start = stack.Pop();
-                // Replace everything between < and > with empty strings separated by commas
-                var segment = result.ToString(start + 1, i - start - 1);
-                var commaCount = segment.Count(c => c == ',');
-                var replacement = new string(',', commaCount);
-                result.Remove(start + 1, i - start - 1);
-                result.Insert(start + 1, replacement);
-                i = start + replacement.Length + 1;
+                elementTypes.Add("object");
+            }
+            else
+            {
+                // Process each tuple element and handle nested generics
+                if (!TryGetFormattedTypeName(element.Type, out var elementTypeName, isNestedCall: true))
+                {
+                    typeName = null;
+                    return false;
+                }
+                elementTypes.Add(elementTypeName);
             }
         }
-        return result.ToString();
+
+        typeName = $"global::System.ValueTuple<{string.Join(", ", elementTypes)}>";
+        return true;
     }
 }
 
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 d3749372ad4a..56cd6714b89f 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs
@@ -17,6 +17,7 @@ public async Task SupportsAllXmlTagsOnSchemas()
     {
         var source = """
 using System;
+using System.Collections.Generic;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.Extensions.DependencyInjection;
@@ -36,6 +37,7 @@ public async Task SupportsAllXmlTagsOnSchemas()
 app.MapPost("/inherit-only-returns", (InheritOnlyReturns returns) => { });
 app.MapPost("/inherit-all-but-remarks", (InheritAllButRemarks remarks) => { });
 app.MapPost("/generic-class", (GenericClass<string> generic) => { });
+app.MapPost("/generic-parent", (GenericParent parent) => { });
 app.MapPost("/params-and-param-refs", (ParamsAndParamRefs refs) => { });
 
 
@@ -335,6 +337,84 @@ public class GenericClass<T>
     // Fields and members.
 }
 
+/// <summary>
+/// This class validates the behavior for mapping
+/// generic types to open generics for use in
+/// typeof expressions.
+/// </summary>
+public class GenericParent
+{
+    /// <summary>
+    /// This property is a nullable value type.
+    /// </summary>
+    public int? Id { get; set; }
+
+    /// <summary>
+    /// This property is a nullable reference type.
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// This property is a generic type containing a tuple.
+    /// </summary>
+    public Task<(int, string)> TaskOfTupleProp { get; set; }
+
+    /// <summary>
+    /// This property is a tuple with a generic type inside.
+    /// </summary>
+    public (int, Dictionary<int, string>) TupleWithGenericProp { get; set; }
+
+    /// <summary>
+    /// This method returns a generic type containing a tuple.
+    /// </summary>
+    public static Task<(int, string)> GetTaskOfTuple()
+    {
+        return Task.FromResult((1, "test"));
+    }
+
+    /// <summary>
+    /// This method returns a tuple with a generic type inside.
+    /// </summary>
+    public static (int, Dictionary<int, string>) GetTupleOfTask()
+    {
+        return (1, new Dictionary<int, string>());
+    }
+
+    /// <summary>
+    /// This method return a tuple with a generic type containing a
+    /// type parameter inside.
+    /// </summary>
+    public static (int, Dictionary<int, T>) GetTupleOfTask1<T>()
+    {
+        return (1, new Dictionary<int, T>());
+    }
+
+    /// <summary>
+    /// This method return a tuple with a generic type containing a
+    /// type parameter inside.
+    /// </summary>
+    public static (T, Dictionary<int, string>) GetTupleOfTask2<T>()
+    {
+        return (default, new Dictionary<int, string>());
+    }
+
+    /// <summary>
+    /// This method returns a nested generic with all types resolved.
+    /// </summary>
+    public static Dictionary<int, Dictionary<int, string>> GetNestedGeneric()
+    {
+        return new Dictionary<int, Dictionary<int, string>>();
+    }
+
+    /// <summary>
+    /// This method returns a nested generic with a type parameter.
+    /// </summary>
+    public static Dictionary<int, Dictionary<int, T>> GetNestedGeneric1<T>()
+    {
+        return new Dictionary<int, Dictionary<int, T>>();
+    }
+}
+
 /// <summary>
 /// This shows examples of typeparamref and typeparam tags
 /// </summary>
@@ -394,6 +474,14 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document =>
             var genericClass = path.RequestBody.Content["application/json"].Schema;
             Assert.Equal("This is a generic class.", genericClass.Description);
 
+            path = document.Paths["/generic-parent"].Operations[OperationType.Post];
+            var genericParent = path.RequestBody.Content["application/json"].Schema;
+            Assert.Equal("This class validates the behavior for mapping\ngeneric types to open generics for use in\ntypeof expressions.", genericParent.Description, ignoreLineEndingDifferences: true);
+            Assert.Equal("This property is a nullable value type.", genericParent.Properties["id"].Description);
+            Assert.Equal("This property is a nullable reference type.", genericParent.Properties["name"].Description);
+            Assert.Equal("This property is a generic type containing a tuple.", genericParent.Properties["taskOfTupleProp"].Description);
+            Assert.Equal("This property is a tuple with a generic type inside.", genericParent.Properties["tupleWithGenericProp"].Description);
+
             path = document.Paths["/params-and-param-refs"].Operations[OperationType.Post];
             var paramsAndParamRefs = path.RequestBody.Content["application/json"].Schema;
             Assert.Equal("This shows examples of typeparamref and typeparam tags", paramsAndParamRefs.Description);
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 d2cfab1740bc..6fa5449a547d 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
@@ -258,6 +258,9 @@ in this interface would contain docs that you want
 type as a cref attribute.
 In generic classes and methods, you'll often want to reference the
 generic type, or the type parameter.", null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Type, null, null, []), new XmlComment(@"This class validates the behavior for mapping
+generic types to open generics for use in
+typeof expressions.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::ParamsAndParamRefs), MemberType.Type, null, null, []), new XmlComment(@"This shows examples of typeparamref and typeparam tags", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Property, "Label", null, []), new XmlComment(null, null, @"    The string? ExampleClass.Label is a <see langword=""string"" />
     that you use for a label.
@@ -266,12 +269,16 @@ Note that there isn't a way to provide a ""cref"" to
 for this instance.", false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::Person), MemberType.Property, "FirstName", null, []), new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::Person), MemberType.Property, "LastName", null, []), new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "Id", null, []), new XmlComment(@"This property is a nullable value type.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "Name", null, []), new XmlComment(@"This property is a nullable reference type.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TaskOfTupleProp", null, []), new XmlComment(@"This property is a generic type containing a tuple.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TupleWithGenericProp", null, []), new XmlComment(@"This property is a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "Add", typeof(global::System.Int32), [typeof(global::System.Int32), typeof(global::System.Int32)]), new XmlComment(@"Adds two integers and returns the result.", null, null, @"The sum of two integers.", null, false, [@"    ```int c = Math.Add(4, 5);
 if (c &gt; 10)
 {
     Console.WriteLine(c);
 }```"], [new XmlParameterComment(@"left", @"The left operand of the addition.", null, false), new XmlParameterComment(@"right", @"The right operand of the addition.", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "AddAsync", typeof(global::System.Threading.Tasks.Task<>), [typeof(global::System.Int32), typeof(global::System.Int32)]), new XmlComment(@"This method is an example of a method that
+            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "AddAsync", typeof(global::System.Threading.Tasks.Task<global::System.Int32>), [typeof(global::System.Int32), typeof(global::System.Int32)]), new XmlComment(@"This method is an example of a method that
 returns an awaitable item.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "DoNothingAsync", typeof(global::System.Threading.Tasks.Task), []), new XmlComment(@"This method is an example of a method that
 returns a Task which should map to a void return type.", null, null, null, null, false, null, null, null));
@@ -285,6 +292,11 @@ that implement this interface when the
             _cache.Add(new MemberKey(typeof(global::InheritAllButRemarks), MemberType.Method, "MyParentMethod", typeof(global::System.Boolean), [typeof(global::System.Boolean)]), new XmlComment(@"In this example, this summary is visible on all the methods.", null, @"The remarks can be inherited by other methods
 using the xpath expression.", @"A boolean", null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::InheritAllButRemarks), MemberType.Method, "MyChildMethod", typeof(global::System.Boolean), []), new XmlComment(@"In this example, this summary is visible on all the methods.", null, null, @"A boolean", null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetTaskOfTuple", typeof(global::System.Threading.Tasks.Task<global::System.ValueTuple<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method returns a generic type containing a tuple.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetTupleOfTask", typeof(global::System.ValueTuple<global::System.Int32, global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method returns a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetTupleOfTask2", typeof(global::System.ValueTuple<object, global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method return a tuple with a generic type containing a
+type parameter inside.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetNestedGeneric", typeof(global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method returns a nested generic with all types resolved.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::ParamsAndParamRefs), MemberType.Method, "GetGenericValue", typeof(object), [typeof(object)]), new XmlComment(@"The GetGenericValue method.", null, @"This sample shows how to specify the T ParamsAndParamRefs.GetGenericValue&lt;T&gt;(T para)
 method as a cref attribute.
 The parameter and return value are both of an arbitrary type,
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
index 0d60c7298d22..01d8e2e6c2f3 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
@@ -189,8 +189,8 @@ private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
             _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get", typeof(global::System.String), []), new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get2", typeof(global::System.String), [typeof(global::System.String)]), new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
             _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get3", typeof(global::System.String), [typeof(global::System.String)]), new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", @"Testy McTester", false)], null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get4", typeof(global::Microsoft.AspNetCore.Http.HttpResults.NotFound<>), []), new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get5", typeof(global::Microsoft.AspNetCore.Http.HttpResults.Results<,,>), []), new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"200", @"Indicates that the value is even.", @""), new XmlResponseComment(@"201", @"Indicates that the value is less than 50.", @""), new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get4", typeof(global::Microsoft.AspNetCore.Http.HttpResults.NotFound<global::System.String>), []), new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get5", typeof(global::Microsoft.AspNetCore.Http.HttpResults.Results<global::Microsoft.AspNetCore.Http.HttpResults.NotFound<global::System.String>, global::Microsoft.AspNetCore.Http.HttpResults.Ok<global::System.String>, global::Microsoft.AspNetCore.Http.HttpResults.Created>), []), new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"200", @"Indicates that the value is even.", @""), new XmlResponseComment(@"201", @"Indicates that the value is less than 50.", @""), new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
             _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Post6", typeof(global::Microsoft.AspNetCore.Http.IResult), [typeof(global::User)]), new XmlComment(@"Creates a new user.", null, @"Sample request:
     POST /6
     {

From a49c0b89fa1edb940213cbf32cf2280b44e187f3 Mon Sep 17 00:00:00 2001
From: Safia Abdalla <safia@safia.rocks>
Date: Tue, 25 Mar 2025 17:14:31 -0700
Subject: [PATCH 2/7] Address feedback

---
 src/OpenApi/gen/XmlCommentGenerator.Parser.cs |   4 +-
 src/OpenApi/gen/XmlComments/MemberKey.cs      |  23 +---
 .../CompletenessTests.cs                      |   6 +
 .../OperationTests.MinimalApis.cs             | 122 ++++++++++++++++++
 ...ApiXmlCommentSupport.generated.verified.cs |   1 +
 ...ApiXmlCommentSupport.generated.verified.cs |   9 ++
 6 files changed, 145 insertions(+), 20 deletions(-)

diff --git a/src/OpenApi/gen/XmlCommentGenerator.Parser.cs b/src/OpenApi/gen/XmlCommentGenerator.Parser.cs
index 022fe4a67427..9b25cf9dd63c 100644
--- a/src/OpenApi/gen/XmlCommentGenerator.Parser.cs
+++ b/src/OpenApi/gen/XmlCommentGenerator.Parser.cs
@@ -94,7 +94,7 @@ public sealed partial class XmlCommentGenerator
             if (DocumentationCommentId.GetFirstSymbolForDeclarationId(name, compilation) is ISymbol symbol &&
                 // Only include symbols that are declared in the application assembly or are
                 // accessible from the application assembly.
-                (SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, input.Compilation.Assembly) || symbol.IsAccessibleType()) &&
+                (SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, compilation.Assembly) || symbol.IsAccessibleType()) &&
                 // Skip static classes that are just containers for members with annotations
                 // since they cannot be instantiated.
                 symbol is not INamedTypeSymbol { TypeKind: TypeKind.Class, IsStatic: true })
@@ -104,7 +104,7 @@ public sealed partial class XmlCommentGenerator
                 {
                     var memberKey = symbol switch
                     {
-                        IMethodSymbol methodSymbol => MemberKey.FromMethodSymbol(methodSymbol, input.Compilation),
+                        IMethodSymbol methodSymbol => MemberKey.FromMethodSymbol(methodSymbol),
                         IPropertySymbol propertySymbol => MemberKey.FromPropertySymbol(propertySymbol),
                         INamedTypeSymbol typeSymbol => MemberKey.FromTypeSymbol(typeSymbol),
                         _ => null
diff --git a/src/OpenApi/gen/XmlComments/MemberKey.cs b/src/OpenApi/gen/XmlComments/MemberKey.cs
index 3d492feb095b..5e2da02053b5 100644
--- a/src/OpenApi/gen/XmlComments/MemberKey.cs
+++ b/src/OpenApi/gen/XmlComments/MemberKey.cs
@@ -21,12 +21,12 @@ internal sealed record MemberKey(
         typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
         genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters);
 
-    private static readonly SymbolDisplayFormat _sansTypeParametersFormat = new(
+    private static readonly SymbolDisplayFormat _withoutTypeParametersFormat = new(
         globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
         typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
         genericsOptions: SymbolDisplayGenericsOptions.None);
 
-    public static MemberKey? FromMethodSymbol(IMethodSymbol method, Compilation compilation)
+    public static MemberKey? FromMethodSymbol(IMethodSymbol method)
     {
         string returnType;
         if (method.ReturnsVoid)
@@ -35,20 +35,7 @@ internal sealed record MemberKey(
         }
         else
         {
-            // Handle Task/ValueTask for async methods
             var actualReturnType = method.ReturnType;
-            if (method.IsAsync && actualReturnType is INamedTypeSymbol namedType)
-            {
-                if (namedType.TypeArguments.Length > 0)
-                {
-                    actualReturnType = namedType.TypeArguments[0];
-                }
-                else
-                {
-                    actualReturnType = compilation.GetSpecialType(SpecialType.System_Void);
-                }
-            }
-
             if (actualReturnType.TypeKind == TypeKind.TypeParameter)
             {
                 returnType = "typeof(object)";
@@ -162,7 +149,7 @@ private static bool TryGetFormattedTypeName(ITypeSymbol typeSymbol, [NotNullWhen
             // generics if possible to avoid emitting a type with type parameters that
             // cannot be used in a typeof expression.
             var hasTypeParameters = genericType.TypeArguments.Any(t => t.TypeKind == TypeKind.TypeParameter);
-            var baseTypeName = genericType.ToDisplayString(_sansTypeParametersFormat);
+            var typeNameWithoutGenerics = genericType.ToDisplayString(_withoutTypeParametersFormat);
 
             if (!hasTypeParameters)
             {
@@ -185,7 +172,7 @@ private static bool TryGetFormattedTypeName(ITypeSymbol typeSymbol, [NotNullWhen
 
                 if (allArgumentsResolved)
                 {
-                    typeName = $"{baseTypeName}<{string.Join(", ", typeArgStrings)}>";
+                    typeName = $"{typeNameWithoutGenerics}<{string.Join(", ", typeArgStrings)}>";
                     return true;
                 }
             }
@@ -204,7 +191,7 @@ private static bool TryGetFormattedTypeName(ITypeSymbol typeSymbol, [NotNullWhen
                 var genericArgumentsCount = genericType.TypeArguments.Length;
                 var openGenericsPlaceholder = "<" + new string(',', genericArgumentsCount - 1) + ">";
 
-                typeName = baseTypeName + openGenericsPlaceholder;
+                typeName = typeNameWithoutGenerics + openGenericsPlaceholder;
                 return true;
             }
         }
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 56cd6714b89f..72e6382a2d56 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs
@@ -364,6 +364,11 @@ public class GenericParent
     /// </summary>
     public (int, Dictionary<int, string>) TupleWithGenericProp { get; set; }
 
+    /// <summary>
+    /// This property is a tuple with a nested generic type inside.
+    /// </summary>
+    public (int, Dictionary<int, Dictionary<string, int>>) TupleWithNestedGenericProp { get; set; }
+
     /// <summary>
     /// This method returns a generic type containing a tuple.
     /// </summary>
@@ -481,6 +486,7 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document =>
             Assert.Equal("This property is a nullable reference type.", genericParent.Properties["name"].Description);
             Assert.Equal("This property is a generic type containing a tuple.", genericParent.Properties["taskOfTupleProp"].Description);
             Assert.Equal("This property is a tuple with a generic type inside.", genericParent.Properties["tupleWithGenericProp"].Description);
+            Assert.Equal("This property is a tuple with a nested generic type inside.", genericParent.Properties["tupleWithNestedGenericProp"].Description);
 
             path = document.Paths["/params-and-param-refs"].Operations[OperationType.Post];
             var paramsAndParamRefs = path.RequestBody.Content["application/json"].Schema;
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
index 76fd57471901..8a93fd22aeb5 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
@@ -14,6 +14,8 @@ public async Task SupportsXmlCommentsOnOperationsFromMinimalApis()
     {
         var source = """
 using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.AspNetCore.Http.HttpResults;
@@ -32,6 +34,15 @@ public async Task SupportsXmlCommentsOnOperationsFromMinimalApis()
 app.MapGet("/5", RouteHandlerExtensionMethods.Get5);
 app.MapPost("/6", RouteHandlerExtensionMethods.Post6);
 app.MapPut("/7", RouteHandlerExtensionMethods.Put7);
+app.MapGet("/8", RouteHandlerExtensionMethods.Get8);
+app.MapGet("/9", RouteHandlerExtensionMethods.Get9);
+app.MapGet("/10", RouteHandlerExtensionMethods.Get10);
+app.MapGet("/11", RouteHandlerExtensionMethods.Get11);
+app.MapGet("/12", RouteHandlerExtensionMethods.Get12);
+app.MapGet("/13", RouteHandlerExtensionMethods.Get13);
+app.MapGet("/14", RouteHandlerExtensionMethods.Get14);
+app.MapGet("/15", RouteHandlerExtensionMethods.Get15);
+app.MapPost("/16", RouteHandlerExtensionMethods.Post16);
 
 app.Run();
 
@@ -114,6 +125,74 @@ public static IResult Put7(int? id, string uuid)
     {
         return TypedResults.NoContent();
     }
+
+    /// <summary>
+    /// A summary of Get8.
+    /// </summary>
+    public static async Task Get8()
+    {
+        await Task.Delay(1000);
+        return;
+    }
+    /// <summary>
+    /// A summary of Get9.
+    /// </summary>
+    public static async ValueTask Get9()
+    {
+        await Task.Delay(1000);
+        return;
+    }
+    /// <summary>
+    /// A summary of Get10.
+    /// </summary>
+    public static Task Get10()
+    {
+        return Task.CompletedTask;
+    }
+    /// <summary>
+    /// A summary of Get11.
+    /// </summary>
+    public static ValueTask Get11()
+    {
+        return ValueTask.CompletedTask;
+    }
+    /// <summary>
+    /// A summary of Get12.
+    /// </summary>
+    public static Task<string> Get12()
+    {
+        return Task.FromResult("Hello, World!");
+    }
+    /// <summary>
+    /// A summary of Get13.
+    /// </summary>
+    public static ValueTask<string> Get13()
+    {
+        return new ValueTask<string>("Hello, World!");
+    }
+    /// <summary>
+    /// A summary of Get14.
+    /// </summary>
+    public static async Task<Holder<string>> Get14()
+    {
+        await Task.Delay(1000);
+        return new Holder<string> { Value = "Hello, World!" };
+    }
+    /// <summary>
+    /// A summary of Get15.
+    /// </summary>
+    public static Task<Holder<string>> Get15()
+    {
+        return Task.FromResult(new Holder<string> { Value = "Hello, World!" });
+    }
+
+    /// <summary>
+    /// A summary of Post16.
+    /// </summary>
+    public static void Post16(Example example)
+    {
+        return;
+    }
 }
 
 public class User
@@ -121,6 +200,22 @@ public class User
     public string Username { get; set; } = string.Empty;
     public string Email { get; set; } = string.Empty;
 }
+
+public class Holder<T>
+{
+    public T Value { get; set; } = default!;
+}
+
+public class Example : Task<int>
+{
+    public Example(Func<int> function) : base(function)
+    {
+    }
+
+    public Example(Func<object?, int> function, object? state) : base(function, state)
+    {
+    }
+}
 """;
         var generator = new XmlCommentGenerator();
         await SnapshotTestHelper.Verify(source, generator, out var compilation);
@@ -159,6 +254,33 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document =>
             var idParam = path7.Parameters.First(p => p.Name == "id");
             Assert.True(idParam.Deprecated);
             Assert.Equal("Legacy ID parameter - use uuid instead.", idParam.Description);
+
+            var path8 = document.Paths["/8"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get8.", path8.Summary);
+
+            var path9 = document.Paths["/9"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get9.", path9.Summary);
+
+            var path10 = document.Paths["/10"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get10.", path10.Summary);
+
+            var path11 = document.Paths["/11"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get11.", path11.Summary);
+
+            var path12 = document.Paths["/12"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get12.", path12.Summary);
+
+            var path13 = document.Paths["/13"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get13.", path13.Summary);
+
+            var path14 = document.Paths["/14"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get14.", path14.Summary);
+
+            var path15 = document.Paths["/15"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get15.", path15.Summary);
+
+            var path16 = document.Paths["/16"].Operations[OperationType.Post];
+            Assert.Equal("A summary of Post16.", path16.Summary);
         });
     }
 }
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 6fa5449a547d..63585c34425a 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
@@ -273,6 +273,7 @@ Note that there isn't a way to provide a ""cref"" to
             _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "Name", null, []), new XmlComment(@"This property is a nullable reference type.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TaskOfTupleProp", null, []), new XmlComment(@"This property is a generic type containing a tuple.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TupleWithGenericProp", null, []), new XmlComment(@"This property is a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TupleWithNestedGenericProp", null, []), new XmlComment(@"This property is a tuple with a nested generic type inside.", null, null, null, null, false, null, null, null));
             _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "Add", typeof(global::System.Int32), [typeof(global::System.Int32), typeof(global::System.Int32)]), new XmlComment(@"Adds two integers and returns the result.", null, null, @"The sum of two integers.", null, false, [@"    ```int c = Math.Add(4, 5);
 if (c &gt; 10)
 {
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
index 01d8e2e6c2f3..e1b46c103eb2 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
@@ -198,6 +198,15 @@ private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
         ""email"": ""john@example.com""
     }", null, null, false, null, [new XmlParameterComment(@"user", @"The user information.", @"{""username"": ""johndoe"", ""email"": ""john@example.com""}", false)], [new XmlResponseComment(@"201", @"Successfully created the user.", @""), new XmlResponseComment(@"400", @"If the user data is invalid.", @"")]));
             _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Put7", typeof(global::Microsoft.AspNetCore.Http.IResult), [typeof(global::System.Int32?), typeof(global::System.String)]), new XmlComment(@"Updates an existing record.", null, null, null, null, false, null, [new XmlParameterComment(@"id", @"Legacy ID parameter - use uuid instead.", null, true), new XmlParameterComment(@"uuid", @"Unique identifier for the record.", null, false)], [new XmlResponseComment(@"204", @"Update successful.", @""), new XmlResponseComment(@"404", @"Legacy response - will be removed.", @"")]));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get8", typeof(global::System.Threading.Tasks.Task), []), new XmlComment(@"A summary of Get8.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get9", typeof(global::System.Threading.Tasks.ValueTask), []), new XmlComment(@"A summary of Get9.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get10", typeof(global::System.Threading.Tasks.Task), []), new XmlComment(@"A summary of Get10.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get11", typeof(global::System.Threading.Tasks.ValueTask), []), new XmlComment(@"A summary of Get11.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get12", typeof(global::System.Threading.Tasks.Task<global::System.String>), []), new XmlComment(@"A summary of Get12.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get13", typeof(global::System.Threading.Tasks.ValueTask<global::System.String>), []), new XmlComment(@"A summary of Get13.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get14", typeof(global::System.Threading.Tasks.Task<global::Holder<global::System.String>>), []), new XmlComment(@"A summary of Get14.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get15", typeof(global::System.Threading.Tasks.Task<global::Holder<global::System.String>>), []), new XmlComment(@"A summary of Get15.", null, null, null, null, false, null, null, null));
+            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Post16", typeof(void), [typeof(global::Example)]), new XmlComment(@"A summary of Post16.", null, null, null, null, false, null, null, null));
 
             return _cache;
         }

From c59d911ba6cf22dbef1f5f072502816e6cffb505 Mon Sep 17 00:00:00 2001
From: Safia Abdalla <safia@safia.rocks>
Date: Wed, 26 Mar 2025 10:12:28 -0700
Subject: [PATCH 3/7] Switch to documentation IDs for cache keys

---
 .../gen/XmlCommentGenerator.Emitter.cs        | 291 ++++++++------
 src/OpenApi/gen/XmlCommentGenerator.Parser.cs |  16 +-
 src/OpenApi/gen/XmlComments/MemberKey.cs      | 234 -----------
 ...ApiXmlCommentSupport.generated.verified.cs | 269 ++++++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 327 ++++++++-------
 ...ApiXmlCommentSupport.generated.verified.cs | 378 ++++++++++--------
 ...ApiXmlCommentSupport.generated.verified.cs | 277 +++++++------
 ...ApiXmlCommentSupport.generated.verified.cs | 313 +++++++++------
 ...ApiXmlCommentSupport.generated.verified.cs | 331 ++++++++-------
 9 files changed, 1270 insertions(+), 1166 deletions(-)
 delete mode 100644 src/OpenApi/gen/XmlComments/MemberKey.cs

diff --git a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
index edb9643dd1cc..ec630c1f7e98 100644
--- a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
+++ b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
@@ -8,7 +8,6 @@
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.AspNetCore.OpenApi.SourceGenerators.Xml;
 using System.Threading;
-using System.Linq;
 
 namespace Microsoft.AspNetCore.OpenApi.SourceGenerators;
 
@@ -50,6 +49,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Reflection;
+    using System.Text;
     using System.Text.Json;
     using System.Text.Json.Nodes;
     using System.Threading;
@@ -80,148 +80,198 @@ file record XmlComment(
     file record XmlResponseComment(string Code, string? Description, string? Example);
 
     {{GeneratedCodeAttribute}}
-    file sealed record MemberKey(
-        Type? DeclaringType,
-        MemberType MemberKind,
-        string? Name,
-        Type? ReturnType,
-        Type[]? Parameters) : IEquatable<MemberKey>
+    file static class XmlCommentCache
     {
-        public bool Equals(MemberKey? other)
-        {
-            if (other is null) return false;
-
-            // Check member kind
-            if (MemberKind != other.MemberKind) return false;
+        private static Dictionary<string, XmlComment>? _cache;
+        public static Dictionary<string, XmlComment> Cache => _cache ??= GenerateCacheEntries();
 
-            // Check declaring type, handling generic types
-            if (!TypesEqual(DeclaringType, other.DeclaringType)) return false;
-
-            // Check name
-            if (Name != other.Name) return false;
+        private static Dictionary<string, XmlComment> GenerateCacheEntries()
+        {
+            var _cache = new Dictionary<string, XmlComment>();
+{{commentsFromXmlFile}}
+{{commentsFromCompilation}}
+            return _cache;
+        }
+    }
 
-            // For methods, check return type and parameters
-            if (MemberKind == MemberType.Method)
+    file static class DocumentationCommentIdHelper
+    {
+        /// <summary>
+        /// Generates a documentation comment ID for a type.
+        /// Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
+        /// </summary>
+        public static string CreateDocumentationId(this Type type)
+        {
+            if (type == null)
             {
-                if (!TypesEqual(ReturnType, other.ReturnType)) return false;
-                if (Parameters is null || other.Parameters is null) return false;
-                if (Parameters.Length != other.Parameters.Length) return false;
-
-                for (int i = 0; i < Parameters.Length; i++)
-                {
-                    if (!TypesEqual(Parameters[i], other.Parameters[i])) return false;
-                }
+                throw new ArgumentNullException(nameof(type));
             }
 
-            return true;
+            return "T:" + GetTypeDocId(type, includeGenericArguments: false, omitGenericArity: false);
         }
 
-        private static bool TypesEqual(Type? type1, Type? type2)
+        /// <summary>
+        /// Generates a documentation comment ID for a property.
+        /// Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
+        /// </summary>
+        public static string CreateDocumentationId(this PropertyInfo property)
         {
-            if (type1 == type2) return true;
-            if (type1 == null || type2 == null) return false;
-
-            if (type1.IsGenericType && type2.IsGenericType)
+            if (property == null)
             {
-                return type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition();
+                throw new ArgumentNullException(nameof(property));
             }
 
-            return type1 == type2;
-        }
+            var sb = new StringBuilder();
+            sb.Append("P:");
 
-        public override int GetHashCode()
-        {
-            var hash = new HashCode();
-            hash.Add(GetTypeHashCode(DeclaringType));
-            hash.Add(MemberKind);
-            hash.Add(Name);
+            if (property.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(property.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
+
+            sb.Append('.');
+            sb.Append(property.Name);
 
-            if (MemberKind == MemberType.Method)
+            // For indexers, include the parameter list.
+            var indexParams = property.GetIndexParameters();
+            if (indexParams.Length > 0)
             {
-                hash.Add(GetTypeHashCode(ReturnType));
-                if (Parameters is not null)
-                {
-                    foreach (var param in Parameters)
-                    {
-                        hash.Add(GetTypeHashCode(param));
-                    }
-                }
+                sb.Append('(');
+                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                sb.Append(')');
             }
 
-            return hash.ToHashCode();
+            return sb.ToString();
         }
 
-        private static int GetTypeHashCode(Type? type)
+        /// <summary>
+        /// Generates a documentation comment ID for a method (or constructor).
+        /// For example:
+        ///   M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
+        ///   M:Namespace.ContainingType.#ctor(ParamType)
+        /// </summary>
+        public static string CreateDocumentationId(this MethodInfo method)
         {
-            if (type == null) return 0;
-            return type.IsGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode();
-        }
+            System.Diagnostics.Debugger.Break();
+            if (method == null)
+            {
+                throw new ArgumentNullException(nameof(method));
+            }
 
-        public static MemberKey FromMethodInfo(MethodInfo method)
-        {
-            return new MemberKey(
-                method.DeclaringType,
-                MemberType.Method,
-                method.Name,
-                method.ReturnType.IsGenericParameter ? typeof(object) : method.ReturnType,
-                method.GetParameters().Select(p => p.ParameterType.IsGenericParameter ? typeof(object) : p.ParameterType).ToArray());
-        }
+            var sb = new StringBuilder();
+            sb.Append("M:");
 
-        public static MemberKey FromPropertyInfo(PropertyInfo property)
-        {
-            return new MemberKey(
-                property.DeclaringType,
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
+            // Append the fully qualified name of the declaring type.
+            if (method.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(method.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
 
-        public static MemberKey FromTypeInfo(Type type)
-        {
-            return new MemberKey(
-                type,
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-    }
+            sb.Append('.');
 
-    file enum MemberType
-    {
-        Type,
-        Property,
-        Method
-    }
+            // Append the method name, handling constructors specially.
+            if (method.IsConstructor)
+            {
+                sb.Append(method.IsStatic ? "#cctor" : "#ctor");
+            }
+            else
+            {
+                sb.Append(method.Name);
+                if (method.IsGenericMethod)
+                {
+                    sb.Append("``");
+                    sb.Append(method.GetGenericArguments().Length);
+                }
+            }
 
-    {{GeneratedCodeAttribute}}
-    file static class XmlCommentCache
-    {
-        private static Dictionary<MemberKey, XmlComment>? _cache;
-        public static Dictionary<MemberKey, XmlComment> Cache => _cache ??= GenerateCacheEntries();
+            // Append the parameter list, if any.
+            var parameters = method.GetParameters();
+            if (parameters.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                sb.Append(')');
+            }
 
-        private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
-        {
-            var _cache = new Dictionary<MemberKey, XmlComment>();
-{{commentsFromXmlFile}}
-{{commentsFromCompilation}}
-            return _cache;
+            // Append the return type after a '~' (if the method returns a value).
+            if (method.ReturnType != typeof(void))
+            {
+                sb.Append('~');
+                // Omit the generic arity for the return type.
+                sb.Append(GetTypeDocId(method.ReturnType, includeGenericArguments: true, omitGenericArity: true));
+            }
+
+            return sb.ToString();
         }
 
-        internal static bool TryGetXmlComment(Type? type, MethodInfo? methodInfo, [NotNullWhen(true)] out XmlComment? xmlComment)
+        /// <summary>
+        /// Generates a documentation ID string for a type.
+        /// This method handles nested types (replacing '+' with '.'),
+        /// generic types, arrays, pointers, by-ref types, and generic parameters.
+        /// The <paramref name="includeGenericArguments"/> flag controls whether
+        /// constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
+        /// controls whether the generic arity marker (e.g. "`1") is appended.
+        /// </summary>
+        private static string GetTypeDocId(Type type, bool includeGenericArguments, bool omitGenericArity)
         {
-            if (methodInfo is null)
+            if (type.IsGenericParameter)
             {
-                return Cache.TryGetValue(new MemberKey(type, MemberType.Property, null, null, null), out xmlComment);
+                // Use `` for method-level generic parameters and ` for type-level.
+                if (type.DeclaringMethod != null)
+                {
+                    return "``" + type.GenericParameterPosition;
+                }
+                else if (type.DeclaringType != null)
+                {
+                    return "`" + type.GenericParameterPosition;
+                }
+                else
+                {
+                    return type.Name;
+                }
             }
 
-            return Cache.TryGetValue(MemberKey.FromMethodInfo(methodInfo), out xmlComment);
-        }
+            if (type.IsArray && type.GetElementType() is { } elementType)
+            {
+                int rank = type.GetArrayRank();
+                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
+                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+            }
 
-        internal static bool TryGetXmlComment(Type? type, string? memberName, [NotNullWhen(true)] out XmlComment? xmlComment)
-        {
-            return Cache.TryGetValue(new MemberKey(type, memberName is null ? MemberType.Type : MemberType.Property, memberName, null, null), out xmlComment);
+            if (type.IsGenericType)
+            {
+                // Get the generic type definition.
+                Type genericDef = type.GetGenericTypeDefinition();
+                string defName = genericDef.FullName != null
+                    ? genericDef.FullName.Replace('+', '.')
+                    : genericDef.Name;
+                // Remove any existing generic arity marker.
+                int backtickIndex = defName.IndexOf('`');
+                if (backtickIndex >= 0)
+                {
+                    defName = defName.Substring(0, backtickIndex);
+                }
+                // Append the generic arity unless omitted.
+                if (!omitGenericArity)
+                {
+                    int arity = genericDef.GetGenericArguments().Length;
+                    defName += "`" + arity;
+                }
+                // If requested, append the constructed generic arguments.
+                if (includeGenericArguments && !type.IsGenericTypeDefinition)
+                {
+                    Type[] typeArgs = type.GetGenericArguments();
+                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
+                    return defName + "{" + args + "}";
+                }
+                else
+                {
+                    return defName;
+                }
+            }
+
+            // For non-generic types, use FullName (if available) and replace nested type separators.
+            return (type.FullName ?? type.Name).Replace('+', '.');
         }
     }
 
@@ -238,7 +288,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
             {
                 return Task.CompletedTask;
             }
-            if (XmlCommentCache.TryGetXmlComment(methodInfo.DeclaringType, methodInfo, out var methodComment))
+            if (XmlCommentCache.Cache.TryGetValue(methodInfo.CreateDocumentationId(), out var methodComment))
             {
                 if (methodComment.Summary is { } summary)
                 {
@@ -311,7 +361,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
         {
             if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
             {
-                if (XmlCommentCache.TryGetXmlComment(propertyInfo.DeclaringType, propertyInfo.Name, out var propertyComment))
+                if (XmlCommentCache.Cache.TryGetValue(propertyInfo.CreateDocumentationId(), out var propertyComment))
                 {
                     schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
                     if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -320,7 +370,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
                     }
                 }
             }
-            if (XmlCommentCache.TryGetXmlComment(context.JsonTypeInfo.Type, (string?)null, out var typeComment))
+            if (XmlCommentCache.Cache.TryGetValue(context.JsonTypeInfo.Type.CreateDocumentationId(), out var typeComment))
             {
                 schema.Description = typeComment.Summary;
                 if (typeComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -434,7 +484,7 @@ internal static string GenerateAddOpenApiInterceptions(ImmutableArray<(AddOpenAp
         return writer.ToString();
     }
 
-    internal static string EmitCommentsCache(IEnumerable<(MemberKey MemberKey, XmlComment? Comment)> comments, CancellationToken cancellationToken)
+    internal static string EmitCommentsCache(IEnumerable<(string MemberKey, XmlComment? Comment)> comments, CancellationToken cancellationToken)
     {
         var writer = new StringWriter();
         var codeWriter = new CodeWriter(writer, baseIndent: 3);
@@ -442,21 +492,10 @@ internal static string EmitCommentsCache(IEnumerable<(MemberKey MemberKey, XmlCo
         {
             if (comment is not null)
             {
-                codeWriter.WriteLine($"_cache.Add(new MemberKey(" +
-                    $"{FormatLiteralOrNull(memberKey.DeclaringType)}, " +
-                    $"MemberType.{memberKey.MemberKind}, " +
-                    $"{FormatLiteralOrNull(memberKey.Name, true)}, " +
-                    $"{FormatLiteralOrNull(memberKey.ReturnType)}, " +
-                    $"[{(memberKey.Parameters != null ? string.Join(", ", memberKey.Parameters.Select(p => SymbolDisplay.FormatLiteral(p, false))) : "")}]), " +
-                    $"{EmitSourceGeneratedXmlComment(comment)});");
+                codeWriter.WriteLine($"_cache.Add({FormatStringForCode(memberKey)}, {EmitSourceGeneratedXmlComment(comment)});");
             }
         }
         return writer.ToString();
-
-        static string FormatLiteralOrNull(string? input, bool quote = false)
-        {
-            return input == null ? "null" : SymbolDisplay.FormatLiteral(input, quote);
-        }
     }
 
     private static string FormatStringForCode(string? input)
diff --git a/src/OpenApi/gen/XmlCommentGenerator.Parser.cs b/src/OpenApi/gen/XmlCommentGenerator.Parser.cs
index 9b25cf9dd63c..0463486167df 100644
--- a/src/OpenApi/gen/XmlCommentGenerator.Parser.cs
+++ b/src/OpenApi/gen/XmlCommentGenerator.Parser.cs
@@ -83,12 +83,12 @@ public sealed partial class XmlCommentGenerator
         return comments;
     }
 
-    internal static IEnumerable<(MemberKey, XmlComment?)> ParseComments(
+    internal static IEnumerable<(string, XmlComment?)> ParseComments(
         (List<(string, string)> RawComments, Compilation Compilation) input,
         CancellationToken cancellationToken)
     {
         var compilation = input.Compilation;
-        var comments = new List<(MemberKey, XmlComment?)>();
+        var comments = new List<(string, XmlComment?)>();
         foreach (var (name, value) in input.RawComments)
         {
             if (DocumentationCommentId.GetFirstSymbolForDeclarationId(name, compilation) is ISymbol symbol &&
@@ -102,17 +102,7 @@ public sealed partial class XmlCommentGenerator
                 var parsedComment = XmlComment.Parse(symbol, compilation, value, cancellationToken);
                 if (parsedComment is not null)
                 {
-                    var memberKey = symbol switch
-                    {
-                        IMethodSymbol methodSymbol => MemberKey.FromMethodSymbol(methodSymbol),
-                        IPropertySymbol propertySymbol => MemberKey.FromPropertySymbol(propertySymbol),
-                        INamedTypeSymbol typeSymbol => MemberKey.FromTypeSymbol(typeSymbol),
-                        _ => null
-                    };
-                    if (memberKey is not null)
-                    {
-                        comments.Add((memberKey, parsedComment));
-                    }
+                    comments.Add((name, parsedComment));
                 }
             }
         }
diff --git a/src/OpenApi/gen/XmlComments/MemberKey.cs b/src/OpenApi/gen/XmlComments/MemberKey.cs
deleted file mode 100644
index 5e2da02053b5..000000000000
--- a/src/OpenApi/gen/XmlComments/MemberKey.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using Microsoft.CodeAnalysis;
-
-namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Xml;
-
-internal sealed record MemberKey(
-    string? DeclaringType,
-    MemberType MemberKind,
-    string? Name,
-    string? ReturnType,
-    List<string>? Parameters) : IEquatable<MemberKey>
-{
-    private static readonly SymbolDisplayFormat _withTypeParametersFormat = new(
-        globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
-        typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
-        genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters);
-
-    private static readonly SymbolDisplayFormat _withoutTypeParametersFormat = new(
-        globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
-        typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
-        genericsOptions: SymbolDisplayGenericsOptions.None);
-
-    public static MemberKey? FromMethodSymbol(IMethodSymbol method)
-    {
-        string returnType;
-        if (method.ReturnsVoid)
-        {
-            returnType = "typeof(void)";
-        }
-        else
-        {
-            var actualReturnType = method.ReturnType;
-            if (actualReturnType.TypeKind == TypeKind.TypeParameter)
-            {
-                returnType = "typeof(object)";
-            }
-            else if (TryGetFormattedTypeName(actualReturnType, out var formattedReturnType))
-            {
-                returnType = $"typeof({formattedReturnType})";
-            }
-            else
-            {
-                return null;
-            }
-        }
-
-        // Handle extension methods by skipping the 'this' parameter
-        List<string> parameters = [];
-        foreach (var parameter in method.Parameters)
-        {
-            if (parameter.IsThis)
-            {
-                continue;
-            }
-
-            if (parameter.Type.TypeKind == TypeKind.TypeParameter)
-            {
-                parameters.Add("typeof(object)");
-            }
-            else if (parameter.IsParams && parameter.Type is IArrayTypeSymbol arrayType)
-            {
-                if (TryGetFormattedTypeName(arrayType.ElementType, out var formattedArrayType))
-                {
-                    parameters.Add($"typeof({formattedArrayType}[])");
-                }
-                else
-                {
-                    return null;
-                }
-            }
-            else if (TryGetFormattedTypeName(parameter.Type, out var formattedParameterType))
-            {
-                parameters.Add($"typeof({formattedParameterType})");
-            }
-            else
-            {
-                return null;
-            }
-        }
-
-        if (TryGetFormattedTypeName(method.ContainingType, out var formattedDeclaringType))
-        {
-            return new MemberKey(
-                $"typeof({formattedDeclaringType})",
-                MemberType.Method,
-                method.MetadataName,  // Use MetadataName to match runtime MethodInfo.Name
-                returnType,
-                parameters);
-        }
-        return null;
-    }
-
-    public static MemberKey? FromPropertySymbol(IPropertySymbol property)
-    {
-        if (TryGetFormattedTypeName(property.ContainingType, out var typeName))
-        {
-            return new MemberKey(
-                $"typeof({typeName})",
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
-        return null;
-    }
-
-    public static MemberKey? FromTypeSymbol(INamedTypeSymbol type)
-    {
-        if (TryGetFormattedTypeName(type, out var typeName))
-        {
-            return new MemberKey(
-                $"typeof({typeName})",
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-        return null;
-    }
-
-    /// Supports replacing generic type arguments to support use of open
-    /// generics in `typeof` expressions for the declaring type.
-    private static bool TryGetFormattedTypeName(ITypeSymbol typeSymbol, [NotNullWhen(true)] out string? typeName, bool isNestedCall = false)
-    {
-        if (typeSymbol is INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } nullableType)
-        {
-            typeName = typeSymbol.ToDisplayString(_withTypeParametersFormat);
-            return true;
-        }
-
-        // Handle tuples specially since they are represented as generic
-        // ValueTuple types and trigger the logic for handling generics in
-        // nested values.
-        if (typeSymbol is INamedTypeSymbol { IsTupleType: true } namedType)
-        {
-            return TryHandleTupleType(namedType, out typeName);
-        }
-
-        if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType)
-        {
-            // If any of the type arguments are type parameters, then they have not
-            // been substituted for a concrete type and we need to model them as open
-            // generics if possible to avoid emitting a type with type parameters that
-            // cannot be used in a typeof expression.
-            var hasTypeParameters = genericType.TypeArguments.Any(t => t.TypeKind == TypeKind.TypeParameter);
-            var typeNameWithoutGenerics = genericType.ToDisplayString(_withoutTypeParametersFormat);
-
-            if (!hasTypeParameters)
-            {
-                var typeArgStrings = new List<string>();
-                var allArgumentsResolved = true;
-
-                // Loop through each type argument to handle nested generics.
-                foreach (var typeArg in genericType.TypeArguments)
-                {
-                    if (TryGetFormattedTypeName(typeArg, out var argTypeName, isNestedCall: true))
-                    {
-                        typeArgStrings.Add(argTypeName);
-                    }
-                    else
-                    {
-                        typeName = null;
-                        return false;
-                    }
-                }
-
-                if (allArgumentsResolved)
-                {
-                    typeName = $"{typeNameWithoutGenerics}<{string.Join(", ", typeArgStrings)}>";
-                    return true;
-                }
-            }
-            else
-            {
-                if (isNestedCall)
-                {
-                    // If this is a nested call, we can't use open generics so there's no way
-                    // for us to emit a member key. Return false and skip over this type in the code
-                    // generation.
-                    typeName = null;
-                    return false;
-                }
-
-                // If we got here, we can successfully emit a member key for the open generic type.
-                var genericArgumentsCount = genericType.TypeArguments.Length;
-                var openGenericsPlaceholder = "<" + new string(',', genericArgumentsCount - 1) + ">";
-
-                typeName = typeNameWithoutGenerics + openGenericsPlaceholder;
-                return true;
-            }
-        }
-
-        typeName = typeSymbol.ToDisplayString(_withTypeParametersFormat);
-        return true;
-    }
-
-    private static bool TryHandleTupleType(INamedTypeSymbol tupleType, [NotNullWhen(true)] out string? typeName)
-    {
-        List<string> elementTypes = [];
-        foreach (var element in tupleType.TupleElements)
-        {
-            if (element.Type.TypeKind == TypeKind.TypeParameter)
-            {
-                elementTypes.Add("object");
-            }
-            else
-            {
-                // Process each tuple element and handle nested generics
-                if (!TryGetFormattedTypeName(element.Type, out var elementTypeName, isNestedCall: true))
-                {
-                    typeName = null;
-                    return false;
-                }
-                elementTypes.Add(elementTypeName);
-            }
-        }
-
-        typeName = $"global::System.ValueTuple<{string.Join(", ", elementTypes)}>";
-        return true;
-    }
-}
-
-internal enum MemberType
-{
-    Type,
-    Property,
-    Method
-}
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
index 87e413e5e8fe..4c8e15e45d4e 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
@@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Reflection;
+    using System.Text;
     using System.Text.Json;
     using System.Text.Json.Nodes;
     using System.Threading;
@@ -61,148 +62,198 @@ file record XmlComment(
     file record XmlResponseComment(string Code, string? Description, string? Example);
 
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file sealed record MemberKey(
-        Type? DeclaringType,
-        MemberType MemberKind,
-        string? Name,
-        Type? ReturnType,
-        Type[]? Parameters) : IEquatable<MemberKey>
+    file static class XmlCommentCache
     {
-        public bool Equals(MemberKey? other)
-        {
-            if (other is null) return false;
+        private static Dictionary<string, XmlComment>? _cache;
+        public static Dictionary<string, XmlComment> Cache => _cache ??= GenerateCacheEntries();
 
-            // Check member kind
-            if (MemberKind != other.MemberKind) return false;
+        private static Dictionary<string, XmlComment> GenerateCacheEntries()
+        {
+            var _cache = new Dictionary<string, XmlComment>();
 
-            // Check declaring type, handling generic types
-            if (!TypesEqual(DeclaringType, other.DeclaringType)) return false;
 
-            // Check name
-            if (Name != other.Name) return false;
+            return _cache;
+        }
+    }
 
-            // For methods, check return type and parameters
-            if (MemberKind == MemberType.Method)
+    file static class DocumentationCommentIdHelper
+    {
+        /// <summary>
+        /// Generates a documentation comment ID for a type.
+        /// Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
+        /// </summary>
+        public static string CreateDocumentationId(this Type type)
+        {
+            if (type == null)
             {
-                if (!TypesEqual(ReturnType, other.ReturnType)) return false;
-                if (Parameters is null || other.Parameters is null) return false;
-                if (Parameters.Length != other.Parameters.Length) return false;
-
-                for (int i = 0; i < Parameters.Length; i++)
-                {
-                    if (!TypesEqual(Parameters[i], other.Parameters[i])) return false;
-                }
+                throw new ArgumentNullException(nameof(type));
             }
 
-            return true;
+            return "T:" + GetTypeDocId(type, includeGenericArguments: false, omitGenericArity: false);
         }
 
-        private static bool TypesEqual(Type? type1, Type? type2)
+        /// <summary>
+        /// Generates a documentation comment ID for a property.
+        /// Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
+        /// </summary>
+        public static string CreateDocumentationId(this PropertyInfo property)
         {
-            if (type1 == type2) return true;
-            if (type1 == null || type2 == null) return false;
-
-            if (type1.IsGenericType && type2.IsGenericType)
+            if (property == null)
             {
-                return type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition();
+                throw new ArgumentNullException(nameof(property));
             }
 
-            return type1 == type2;
-        }
-
-        public override int GetHashCode()
-        {
-            var hash = new HashCode();
-            hash.Add(GetTypeHashCode(DeclaringType));
-            hash.Add(MemberKind);
-            hash.Add(Name);
+            var sb = new StringBuilder();
+            sb.Append("P:");
 
-            if (MemberKind == MemberType.Method)
+            if (property.DeclaringType != null)
             {
-                hash.Add(GetTypeHashCode(ReturnType));
-                if (Parameters is not null)
-                {
-                    foreach (var param in Parameters)
-                    {
-                        hash.Add(GetTypeHashCode(param));
-                    }
-                }
+                sb.Append(GetTypeDocId(property.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
             }
 
-            return hash.ToHashCode();
-        }
+            sb.Append('.');
+            sb.Append(property.Name);
 
-        private static int GetTypeHashCode(Type? type)
-        {
-            if (type == null) return 0;
-            return type.IsGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode();
-        }
+            // For indexers, include the parameter list.
+            var indexParams = property.GetIndexParameters();
+            if (indexParams.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                sb.Append(')');
+            }
 
-        public static MemberKey FromMethodInfo(MethodInfo method)
-        {
-            return new MemberKey(
-                method.DeclaringType,
-                MemberType.Method,
-                method.Name,
-                method.ReturnType.IsGenericParameter ? typeof(object) : method.ReturnType,
-                method.GetParameters().Select(p => p.ParameterType.IsGenericParameter ? typeof(object) : p.ParameterType).ToArray());
+            return sb.ToString();
         }
 
-        public static MemberKey FromPropertyInfo(PropertyInfo property)
+        /// <summary>
+        /// Generates a documentation comment ID for a method (or constructor).
+        /// For example:
+        ///   M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
+        ///   M:Namespace.ContainingType.#ctor(ParamType)
+        /// </summary>
+        public static string CreateDocumentationId(this MethodInfo method)
         {
-            return new MemberKey(
-                property.DeclaringType,
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
+            System.Diagnostics.Debugger.Break();
+            if (method == null)
+            {
+                throw new ArgumentNullException(nameof(method));
+            }
 
-        public static MemberKey FromTypeInfo(Type type)
-        {
-            return new MemberKey(
-                type,
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-    }
+            var sb = new StringBuilder();
+            sb.Append("M:");
 
-    file enum MemberType
-    {
-        Type,
-        Property,
-        Method
-    }
+            // Append the fully qualified name of the declaring type.
+            if (method.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(method.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
 
-    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file static class XmlCommentCache
-    {
-        private static Dictionary<MemberKey, XmlComment>? _cache;
-        public static Dictionary<MemberKey, XmlComment> Cache => _cache ??= GenerateCacheEntries();
+            sb.Append('.');
 
-        private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
-        {
-            var _cache = new Dictionary<MemberKey, XmlComment>();
+            // Append the method name, handling constructors specially.
+            if (method.IsConstructor)
+            {
+                sb.Append(method.IsStatic ? "#cctor" : "#ctor");
+            }
+            else
+            {
+                sb.Append(method.Name);
+                if (method.IsGenericMethod)
+                {
+                    sb.Append("``");
+                    sb.Append(method.GetGenericArguments().Length);
+                }
+            }
+
+            // Append the parameter list, if any.
+            var parameters = method.GetParameters();
+            if (parameters.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                sb.Append(')');
+            }
 
+            // Append the return type after a '~' (if the method returns a value).
+            if (method.ReturnType != typeof(void))
+            {
+                sb.Append('~');
+                // Omit the generic arity for the return type.
+                sb.Append(GetTypeDocId(method.ReturnType, includeGenericArguments: true, omitGenericArity: true));
+            }
 
-            return _cache;
+            return sb.ToString();
         }
 
-        internal static bool TryGetXmlComment(Type? type, MethodInfo? methodInfo, [NotNullWhen(true)] out XmlComment? xmlComment)
+        /// <summary>
+        /// Generates a documentation ID string for a type.
+        /// This method handles nested types (replacing '+' with '.'),
+        /// generic types, arrays, pointers, by-ref types, and generic parameters.
+        /// The <paramref name="includeGenericArguments"/> flag controls whether
+        /// constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
+        /// controls whether the generic arity marker (e.g. "`1") is appended.
+        /// </summary>
+        private static string GetTypeDocId(Type type, bool includeGenericArguments, bool omitGenericArity)
         {
-            if (methodInfo is null)
+            if (type.IsGenericParameter)
+            {
+                // Use `` for method-level generic parameters and ` for type-level.
+                if (type.DeclaringMethod != null)
+                {
+                    return "``" + type.GenericParameterPosition;
+                }
+                else if (type.DeclaringType != null)
+                {
+                    return "`" + type.GenericParameterPosition;
+                }
+                else
+                {
+                    return type.Name;
+                }
+            }
+
+            if (type.IsArray && type.GetElementType() is { } elementType)
             {
-                return Cache.TryGetValue(new MemberKey(type, MemberType.Property, null, null, null), out xmlComment);
+                int rank = type.GetArrayRank();
+                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
+                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
             }
 
-            return Cache.TryGetValue(MemberKey.FromMethodInfo(methodInfo), out xmlComment);
-        }
+            if (type.IsGenericType)
+            {
+                // Get the generic type definition.
+                Type genericDef = type.GetGenericTypeDefinition();
+                string defName = genericDef.FullName != null
+                    ? genericDef.FullName.Replace('+', '.')
+                    : genericDef.Name;
+                // Remove any existing generic arity marker.
+                int backtickIndex = defName.IndexOf('`');
+                if (backtickIndex >= 0)
+                {
+                    defName = defName.Substring(0, backtickIndex);
+                }
+                // Append the generic arity unless omitted.
+                if (!omitGenericArity)
+                {
+                    int arity = genericDef.GetGenericArguments().Length;
+                    defName += "`" + arity;
+                }
+                // If requested, append the constructed generic arguments.
+                if (includeGenericArguments && !type.IsGenericTypeDefinition)
+                {
+                    Type[] typeArgs = type.GetGenericArguments();
+                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
+                    return defName + "{" + args + "}";
+                }
+                else
+                {
+                    return defName;
+                }
+            }
 
-        internal static bool TryGetXmlComment(Type? type, string? memberName, [NotNullWhen(true)] out XmlComment? xmlComment)
-        {
-            return Cache.TryGetValue(new MemberKey(type, memberName is null ? MemberType.Type : MemberType.Property, memberName, null, null), out xmlComment);
+            // For non-generic types, use FullName (if available) and replace nested type separators.
+            return (type.FullName ?? type.Name).Replace('+', '.');
         }
     }
 
@@ -219,7 +270,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
             {
                 return Task.CompletedTask;
             }
-            if (XmlCommentCache.TryGetXmlComment(methodInfo.DeclaringType, methodInfo, out var methodComment))
+            if (XmlCommentCache.Cache.TryGetValue(methodInfo.CreateDocumentationId(), out var methodComment))
             {
                 if (methodComment.Summary is { } summary)
                 {
@@ -292,7 +343,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
         {
             if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
             {
-                if (XmlCommentCache.TryGetXmlComment(propertyInfo.DeclaringType, propertyInfo.Name, out var propertyComment))
+                if (XmlCommentCache.Cache.TryGetValue(propertyInfo.CreateDocumentationId(), out var propertyComment))
                 {
                     schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
                     if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -301,7 +352,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
                     }
                 }
             }
-            if (XmlCommentCache.TryGetXmlComment(context.JsonTypeInfo.Type, (string?)null, out var typeComment))
+            if (XmlCommentCache.Cache.TryGetValue(context.JsonTypeInfo.Type.CreateDocumentationId(), out var typeComment))
             {
                 schema.Description = typeComment.Summary;
                 if (typeComment.Examples?.FirstOrDefault() is { } jsonString)
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
index a48807355ff7..67f9b58ae525 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
@@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Reflection;
+    using System.Text;
     using System.Text.Json;
     using System.Text.Json.Nodes;
     using System.Threading;
@@ -61,177 +62,227 @@ file record XmlComment(
     file record XmlResponseComment(string Code, string? Description, string? Example);
 
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file sealed record MemberKey(
-        Type? DeclaringType,
-        MemberType MemberKind,
-        string? Name,
-        Type? ReturnType,
-        Type[]? Parameters) : IEquatable<MemberKey>
+    file static class XmlCommentCache
     {
-        public bool Equals(MemberKey? other)
-        {
-            if (other is null) return false;
+        private static Dictionary<string, XmlComment>? _cache;
+        public static Dictionary<string, XmlComment> Cache => _cache ??= GenerateCacheEntries();
 
-            // Check member kind
-            if (MemberKind != other.MemberKind) return false;
+        private static Dictionary<string, XmlComment> GenerateCacheEntries()
+        {
+            var _cache = new Dictionary<string, XmlComment>();
+            _cache.Add(@"T:ClassLibrary.Todo", new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:ClassLibrary.Project", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:ClassLibrary.Project.#ctor(System.String,System.String)", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:ClassLibrary.ProjectBoard.BoardItem", new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:ClassLibrary.ProjectBoard.BoardItem.Name", new XmlComment(@"The identifier of the board item. Defaults to ""name"".", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:ClassLibrary.ProjectRecord", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
+            _cache.Add(@"M:ClassLibrary.ProjectRecord.#ctor(System.String,System.String)", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
+            _cache.Add(@"P:ClassLibrary.ProjectRecord.Name", new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:ClassLibrary.ProjectRecord.Description", new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:ClassLibrary.TodoWithDescription.Id", new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:ClassLibrary.TodoWithDescription.Name", new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
+            _cache.Add(@"P:ClassLibrary.TodoWithDescription.Description", new XmlComment(@"A description of the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.BooleanType", new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.IntegerType", new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.LongType", new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DoubleType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.FloatType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DateTimeType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DateOnlyType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.StringType", new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.GuidType", new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.TimeOnlyType", new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.TimeSpanType", new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.ByteType", new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DecimalType", new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
+            _cache.Add(@"P:ClassLibrary.TypeWithExamples.UriType", new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
+            _cache.Add(@"P:ClassLibrary.Holder`1.Value", new XmlComment(@"The value to hold.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:ClassLibrary.Endpoints.ExternalMethod(System.String)", new XmlComment(@"An external method.", null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the tester. Defaults to ""Tester"".", null, false)], null));
+            _cache.Add(@"M:ClassLibrary.Endpoints.CreateHolder``1(``0)", new XmlComment(@"Creates a holder for the specified value.", null, null, @"A holder for the specified value.", null, false, [@"{ value: 42 }"], [new XmlParameterComment(@"value", @"The value to hold.", null, false)], null));
 
-            // Check declaring type, handling generic types
-            if (!TypesEqual(DeclaringType, other.DeclaringType)) return false;
 
-            // Check name
-            if (Name != other.Name) return false;
+            return _cache;
+        }
+    }
 
-            // For methods, check return type and parameters
-            if (MemberKind == MemberType.Method)
+    file static class DocumentationCommentIdHelper
+    {
+        /// <summary>
+        /// Generates a documentation comment ID for a type.
+        /// Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
+        /// </summary>
+        public static string CreateDocumentationId(this Type type)
+        {
+            if (type == null)
             {
-                if (!TypesEqual(ReturnType, other.ReturnType)) return false;
-                if (Parameters is null || other.Parameters is null) return false;
-                if (Parameters.Length != other.Parameters.Length) return false;
-
-                for (int i = 0; i < Parameters.Length; i++)
-                {
-                    if (!TypesEqual(Parameters[i], other.Parameters[i])) return false;
-                }
+                throw new ArgumentNullException(nameof(type));
             }
 
-            return true;
+            return "T:" + GetTypeDocId(type, includeGenericArguments: false, omitGenericArity: false);
         }
 
-        private static bool TypesEqual(Type? type1, Type? type2)
+        /// <summary>
+        /// Generates a documentation comment ID for a property.
+        /// Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
+        /// </summary>
+        public static string CreateDocumentationId(this PropertyInfo property)
         {
-            if (type1 == type2) return true;
-            if (type1 == null || type2 == null) return false;
-
-            if (type1.IsGenericType && type2.IsGenericType)
+            if (property == null)
             {
-                return type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition();
+                throw new ArgumentNullException(nameof(property));
             }
 
-            return type1 == type2;
-        }
-
-        public override int GetHashCode()
-        {
-            var hash = new HashCode();
-            hash.Add(GetTypeHashCode(DeclaringType));
-            hash.Add(MemberKind);
-            hash.Add(Name);
+            var sb = new StringBuilder();
+            sb.Append("P:");
 
-            if (MemberKind == MemberType.Method)
+            if (property.DeclaringType != null)
             {
-                hash.Add(GetTypeHashCode(ReturnType));
-                if (Parameters is not null)
-                {
-                    foreach (var param in Parameters)
-                    {
-                        hash.Add(GetTypeHashCode(param));
-                    }
-                }
+                sb.Append(GetTypeDocId(property.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
             }
 
-            return hash.ToHashCode();
-        }
+            sb.Append('.');
+            sb.Append(property.Name);
 
-        private static int GetTypeHashCode(Type? type)
-        {
-            if (type == null) return 0;
-            return type.IsGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode();
-        }
+            // For indexers, include the parameter list.
+            var indexParams = property.GetIndexParameters();
+            if (indexParams.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                sb.Append(')');
+            }
 
-        public static MemberKey FromMethodInfo(MethodInfo method)
-        {
-            return new MemberKey(
-                method.DeclaringType,
-                MemberType.Method,
-                method.Name,
-                method.ReturnType.IsGenericParameter ? typeof(object) : method.ReturnType,
-                method.GetParameters().Select(p => p.ParameterType.IsGenericParameter ? typeof(object) : p.ParameterType).ToArray());
+            return sb.ToString();
         }
 
-        public static MemberKey FromPropertyInfo(PropertyInfo property)
+        /// <summary>
+        /// Generates a documentation comment ID for a method (or constructor).
+        /// For example:
+        ///   M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
+        ///   M:Namespace.ContainingType.#ctor(ParamType)
+        /// </summary>
+        public static string CreateDocumentationId(this MethodInfo method)
         {
-            return new MemberKey(
-                property.DeclaringType,
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
+            System.Diagnostics.Debugger.Break();
+            if (method == null)
+            {
+                throw new ArgumentNullException(nameof(method));
+            }
 
-        public static MemberKey FromTypeInfo(Type type)
-        {
-            return new MemberKey(
-                type,
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-    }
+            var sb = new StringBuilder();
+            sb.Append("M:");
 
-    file enum MemberType
-    {
-        Type,
-        Property,
-        Method
-    }
+            // Append the fully qualified name of the declaring type.
+            if (method.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(method.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
 
-    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file static class XmlCommentCache
-    {
-        private static Dictionary<MemberKey, XmlComment>? _cache;
-        public static Dictionary<MemberKey, XmlComment> Cache => _cache ??= GenerateCacheEntries();
+            sb.Append('.');
 
-        private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
-        {
-            var _cache = new Dictionary<MemberKey, XmlComment>();
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.Todo), MemberType.Type, null, null, []), new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.Project), MemberType.Type, null, null, []), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.Project), MemberType.Method, ".ctor", typeof(void), [typeof(global::System.String), typeof(global::System.String)]), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.ProjectBoard.BoardItem), MemberType.Type, null, null, []), new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.ProjectBoard.BoardItem), MemberType.Property, "Name", null, []), new XmlComment(@"The identifier of the board item. Defaults to ""name"".", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.ProjectRecord), MemberType.Type, null, null, []), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.ProjectRecord), MemberType.Method, ".ctor", typeof(void), [typeof(global::System.String), typeof(global::System.String)]), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.ProjectRecord), MemberType.Property, "Name", null, []), new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.ProjectRecord), MemberType.Property, "Description", null, []), new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TodoWithDescription), MemberType.Property, "Id", null, []), new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TodoWithDescription), MemberType.Property, "Name", null, []), new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TodoWithDescription), MemberType.Property, "Description", null, []), new XmlComment(@"A description of the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "BooleanType", null, []), new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "IntegerType", null, []), new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "LongType", null, []), new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "DoubleType", null, []), new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "FloatType", null, []), new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "DateTimeType", null, []), new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "DateOnlyType", null, []), new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "StringType", null, []), new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "GuidType", null, []), new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "TimeOnlyType", null, []), new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "TimeSpanType", null, []), new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "ByteType", null, []), new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "DecimalType", null, []), new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.TypeWithExamples), MemberType.Property, "UriType", null, []), new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.Holder<>), MemberType.Property, "Value", null, []), new XmlComment(@"The value to hold.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.Endpoints), MemberType.Method, "ExternalMethod", typeof(void), [typeof(global::System.String)]), new XmlComment(@"An external method.", null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the tester. Defaults to ""Tester"".", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::ClassLibrary.Endpoints), MemberType.Method, "CreateHolder", typeof(global::ClassLibrary.Holder<>), [typeof(object)]), new XmlComment(@"Creates a holder for the specified value.", null, null, @"A holder for the specified value.", null, false, [@"{ value: 42 }"], [new XmlParameterComment(@"value", @"The value to hold.", null, false)], null));
+            // Append the method name, handling constructors specially.
+            if (method.IsConstructor)
+            {
+                sb.Append(method.IsStatic ? "#cctor" : "#ctor");
+            }
+            else
+            {
+                sb.Append(method.Name);
+                if (method.IsGenericMethod)
+                {
+                    sb.Append("``");
+                    sb.Append(method.GetGenericArguments().Length);
+                }
+            }
+
+            // Append the parameter list, if any.
+            var parameters = method.GetParameters();
+            if (parameters.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                sb.Append(')');
+            }
 
+            // Append the return type after a '~' (if the method returns a value).
+            if (method.ReturnType != typeof(void))
+            {
+                sb.Append('~');
+                // Omit the generic arity for the return type.
+                sb.Append(GetTypeDocId(method.ReturnType, includeGenericArguments: true, omitGenericArity: true));
+            }
 
-            return _cache;
+            return sb.ToString();
         }
 
-        internal static bool TryGetXmlComment(Type? type, MethodInfo? methodInfo, [NotNullWhen(true)] out XmlComment? xmlComment)
+        /// <summary>
+        /// Generates a documentation ID string for a type.
+        /// This method handles nested types (replacing '+' with '.'),
+        /// generic types, arrays, pointers, by-ref types, and generic parameters.
+        /// The <paramref name="includeGenericArguments"/> flag controls whether
+        /// constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
+        /// controls whether the generic arity marker (e.g. "`1") is appended.
+        /// </summary>
+        private static string GetTypeDocId(Type type, bool includeGenericArguments, bool omitGenericArity)
         {
-            if (methodInfo is null)
+            if (type.IsGenericParameter)
+            {
+                // Use `` for method-level generic parameters and ` for type-level.
+                if (type.DeclaringMethod != null)
+                {
+                    return "``" + type.GenericParameterPosition;
+                }
+                else if (type.DeclaringType != null)
+                {
+                    return "`" + type.GenericParameterPosition;
+                }
+                else
+                {
+                    return type.Name;
+                }
+            }
+
+            if (type.IsArray && type.GetElementType() is { } elementType)
             {
-                return Cache.TryGetValue(new MemberKey(type, MemberType.Property, null, null, null), out xmlComment);
+                int rank = type.GetArrayRank();
+                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
+                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
             }
 
-            return Cache.TryGetValue(MemberKey.FromMethodInfo(methodInfo), out xmlComment);
-        }
+            if (type.IsGenericType)
+            {
+                // Get the generic type definition.
+                Type genericDef = type.GetGenericTypeDefinition();
+                string defName = genericDef.FullName != null
+                    ? genericDef.FullName.Replace('+', '.')
+                    : genericDef.Name;
+                // Remove any existing generic arity marker.
+                int backtickIndex = defName.IndexOf('`');
+                if (backtickIndex >= 0)
+                {
+                    defName = defName.Substring(0, backtickIndex);
+                }
+                // Append the generic arity unless omitted.
+                if (!omitGenericArity)
+                {
+                    int arity = genericDef.GetGenericArguments().Length;
+                    defName += "`" + arity;
+                }
+                // If requested, append the constructed generic arguments.
+                if (includeGenericArguments && !type.IsGenericTypeDefinition)
+                {
+                    Type[] typeArgs = type.GetGenericArguments();
+                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
+                    return defName + "{" + args + "}";
+                }
+                else
+                {
+                    return defName;
+                }
+            }
 
-        internal static bool TryGetXmlComment(Type? type, string? memberName, [NotNullWhen(true)] out XmlComment? xmlComment)
-        {
-            return Cache.TryGetValue(new MemberKey(type, memberName is null ? MemberType.Type : MemberType.Property, memberName, null, null), out xmlComment);
+            // For non-generic types, use FullName (if available) and replace nested type separators.
+            return (type.FullName ?? type.Name).Replace('+', '.');
         }
     }
 
@@ -248,7 +299,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
             {
                 return Task.CompletedTask;
             }
-            if (XmlCommentCache.TryGetXmlComment(methodInfo.DeclaringType, methodInfo, out var methodComment))
+            if (XmlCommentCache.Cache.TryGetValue(methodInfo.CreateDocumentationId(), out var methodComment))
             {
                 if (methodComment.Summary is { } summary)
                 {
@@ -321,7 +372,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
         {
             if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
             {
-                if (XmlCommentCache.TryGetXmlComment(propertyInfo.DeclaringType, propertyInfo.Name, out var propertyComment))
+                if (XmlCommentCache.Cache.TryGetValue(propertyInfo.CreateDocumentationId(), out var propertyComment))
                 {
                     schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
                     if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -330,7 +381,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
                     }
                 }
             }
-            if (XmlCommentCache.TryGetXmlComment(context.JsonTypeInfo.Type, (string?)null, out var typeComment))
+            if (XmlCommentCache.Cache.TryGetValue(context.JsonTypeInfo.Type.CreateDocumentationId(), out var typeComment))
             {
                 schema.Description = typeComment.Summary;
                 if (typeComment.Examples?.FirstOrDefault() is { } jsonString)
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 63585c34425a..2125789f1369 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
@@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Reflection;
+    using System.Text;
     using System.Text.Json;
     using System.Text.Json.Nodes;
     using System.Threading;
@@ -60,133 +61,17 @@ file record XmlComment(
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
     file record XmlResponseComment(string Code, string? Description, string? Example);
 
-    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file sealed record MemberKey(
-        Type? DeclaringType,
-        MemberType MemberKind,
-        string? Name,
-        Type? ReturnType,
-        Type[]? Parameters) : IEquatable<MemberKey>
-    {
-        public bool Equals(MemberKey? other)
-        {
-            if (other is null) return false;
-
-            // Check member kind
-            if (MemberKind != other.MemberKind) return false;
-
-            // Check declaring type, handling generic types
-            if (!TypesEqual(DeclaringType, other.DeclaringType)) return false;
-
-            // Check name
-            if (Name != other.Name) return false;
-
-            // For methods, check return type and parameters
-            if (MemberKind == MemberType.Method)
-            {
-                if (!TypesEqual(ReturnType, other.ReturnType)) return false;
-                if (Parameters is null || other.Parameters is null) return false;
-                if (Parameters.Length != other.Parameters.Length) return false;
-
-                for (int i = 0; i < Parameters.Length; i++)
-                {
-                    if (!TypesEqual(Parameters[i], other.Parameters[i])) return false;
-                }
-            }
-
-            return true;
-        }
-
-        private static bool TypesEqual(Type? type1, Type? type2)
-        {
-            if (type1 == type2) return true;
-            if (type1 == null || type2 == null) return false;
-
-            if (type1.IsGenericType && type2.IsGenericType)
-            {
-                return type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition();
-            }
-
-            return type1 == type2;
-        }
-
-        public override int GetHashCode()
-        {
-            var hash = new HashCode();
-            hash.Add(GetTypeHashCode(DeclaringType));
-            hash.Add(MemberKind);
-            hash.Add(Name);
-
-            if (MemberKind == MemberType.Method)
-            {
-                hash.Add(GetTypeHashCode(ReturnType));
-                if (Parameters is not null)
-                {
-                    foreach (var param in Parameters)
-                    {
-                        hash.Add(GetTypeHashCode(param));
-                    }
-                }
-            }
-
-            return hash.ToHashCode();
-        }
-
-        private static int GetTypeHashCode(Type? type)
-        {
-            if (type == null) return 0;
-            return type.IsGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode();
-        }
-
-        public static MemberKey FromMethodInfo(MethodInfo method)
-        {
-            return new MemberKey(
-                method.DeclaringType,
-                MemberType.Method,
-                method.Name,
-                method.ReturnType.IsGenericParameter ? typeof(object) : method.ReturnType,
-                method.GetParameters().Select(p => p.ParameterType.IsGenericParameter ? typeof(object) : p.ParameterType).ToArray());
-        }
-
-        public static MemberKey FromPropertyInfo(PropertyInfo property)
-        {
-            return new MemberKey(
-                property.DeclaringType,
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
-
-        public static MemberKey FromTypeInfo(Type type)
-        {
-            return new MemberKey(
-                type,
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-    }
-
-    file enum MemberType
-    {
-        Type,
-        Property,
-        Method
-    }
-
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
     file static class XmlCommentCache
     {
-        private static Dictionary<MemberKey, XmlComment>? _cache;
-        public static Dictionary<MemberKey, XmlComment> Cache => _cache ??= GenerateCacheEntries();
+        private static Dictionary<string, XmlComment>? _cache;
+        public static Dictionary<string, XmlComment> Cache => _cache ??= GenerateCacheEntries();
 
-        private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
+        private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<MemberKey, XmlComment>();
+            var _cache = new Dictionary<string, XmlComment>();
 
-            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Type, null, null, []), new XmlComment(@"Every class and member should have a one sentence
+            _cache.Add(@"T:ExampleClass", new XmlComment(@"Every class and member should have a one sentence
 summary describing its purpose.", null, @"     You can expand on that one sentence summary to
      provide more information for readers. In this case,
      the `ExampleClass` provides different C#
@@ -230,95 +115,264 @@ would typically use the ""term"" element.
 
 Note: paragraphs are double spaced. Use the *br*
 tag for single spaced lines.", null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::Person), MemberType.Type, null, null, []), new XmlComment(@"This is an example of a positional record.", null, @"There isn't a way to add XML comments for properties
+            _cache.Add(@"T:Person", new XmlComment(@"This is an example of a positional record.", null, @"There isn't a way to add XML comments for properties
 created for positional records, yet. The language
 design team is still considering what tags should
 be supported, and where. Currently, you can use
 the ""param"" tag to describe the parameters to the
 primary constructor.", null, null, false, null, [new XmlParameterComment(@"FirstName", @"This tag will apply to the primary constructor parameter.", null, false), new XmlParameterComment(@"LastName", @"This tag will apply to the primary constructor parameter.", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::MainClass), MemberType.Type, null, null, []), new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
+            _cache.Add(@"T:MainClass", new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
 In this example, these comments also explain the
 general information about the derived class.", null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::DerivedClass), MemberType.Type, null, null, []), new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
+            _cache.Add(@"T:DerivedClass", new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
 In this example, these comments also explain the
 general information about the derived class.", null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ITestInterface), MemberType.Type, null, null, []), new XmlComment(@"This interface would describe all the methods in
+            _cache.Add(@"T:ITestInterface", new XmlComment(@"This interface would describe all the methods in
 its contract.", null, @"While elided for brevity, each method or property
 in this interface would contain docs that you want
 to duplicate in each implementing class.", null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ImplementingClass), MemberType.Type, null, null, []), new XmlComment(@"This interface would describe all the methods in
+            _cache.Add(@"T:ImplementingClass", new XmlComment(@"This interface would describe all the methods in
 its contract.", null, @"While elided for brevity, each method or property
 in this interface would contain docs that you want
 to duplicate in each implementing class.", null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::InheritOnlyReturns), MemberType.Type, null, null, []), new XmlComment(@"This class shows hows you can ""inherit"" the doc
+            _cache.Add(@"T:InheritOnlyReturns", new XmlComment(@"This class shows hows you can ""inherit"" the doc
 comments from one method in another method.", null, @"You can inherit all comments, or only a specific tag,
 represented by an xpath expression.", null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::InheritAllButRemarks), MemberType.Type, null, null, []), new XmlComment(@"This class shows an example of sharing comments across methods.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericClass<>), MemberType.Type, null, null, []), new XmlComment(@"This is a generic class.", null, @"This example shows how to specify the GenericClass&lt;T&gt;
+            _cache.Add(@"T:InheritAllButRemarks", new XmlComment(@"This class shows an example of sharing comments across methods.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:GenericClass`1", new XmlComment(@"This is a generic class.", null, @"This example shows how to specify the GenericClass&lt;T&gt;
 type as a cref attribute.
 In generic classes and methods, you'll often want to reference the
 generic type, or the type parameter.", null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Type, null, null, []), new XmlComment(@"This class validates the behavior for mapping
+            _cache.Add(@"T:GenericParent", new XmlComment(@"This class validates the behavior for mapping
 generic types to open generics for use in
 typeof expressions.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ParamsAndParamRefs), MemberType.Type, null, null, []), new XmlComment(@"This shows examples of typeparamref and typeparam tags", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Property, "Label", null, []), new XmlComment(null, null, @"    The string? ExampleClass.Label is a <see langword=""string"" />
+            _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 <see langword=""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
 for this instance.", false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::Person), MemberType.Property, "FirstName", null, []), new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::Person), MemberType.Property, "LastName", null, []), new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "Id", null, []), new XmlComment(@"This property is a nullable value type.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "Name", null, []), new XmlComment(@"This property is a nullable reference type.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TaskOfTupleProp", null, []), new XmlComment(@"This property is a generic type containing a tuple.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TupleWithGenericProp", null, []), new XmlComment(@"This property is a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Property, "TupleWithNestedGenericProp", null, []), new XmlComment(@"This property is a tuple with a nested generic type inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "Add", typeof(global::System.Int32), [typeof(global::System.Int32), typeof(global::System.Int32)]), new XmlComment(@"Adds two integers and returns the result.", null, null, @"The sum of two integers.", null, false, [@"    ```int c = Math.Add(4, 5);
+            _cache.Add(@"P:Person.FirstName", new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:Person.LastName", new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:GenericParent.Id", new XmlComment(@"This property is a nullable value type.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:GenericParent.Name", new XmlComment(@"This property is a nullable reference type.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:GenericParent.TaskOfTupleProp", new XmlComment(@"This property is a generic type containing a tuple.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:GenericParent.TupleWithGenericProp", new XmlComment(@"This property is a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:GenericParent.TupleWithNestedGenericProp", new XmlComment(@"This property is a tuple with a nested generic type inside.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:ExampleClass.Add(System.Int32,System.Int32)~System.Int32", new XmlComment(@"Adds two integers and returns the result.", null, null, @"The sum of two integers.", null, false, [@"    ```int c = Math.Add(4, 5);
 if (c &gt; 10)
 {
     Console.WriteLine(c);
 }```"], [new XmlParameterComment(@"left", @"The left operand of the addition.", null, false), new XmlParameterComment(@"right", @"The right operand of the addition.", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "AddAsync", typeof(global::System.Threading.Tasks.Task<global::System.Int32>), [typeof(global::System.Int32), typeof(global::System.Int32)]), new XmlComment(@"This method is an example of a method that
+            _cache.Add(@"M:ExampleClass.AddAsync(System.Int32,System.Int32)~System.Threading.Tasks.Task{System.Int32}", new XmlComment(@"This method is an example of a method that
 returns an awaitable item.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "DoNothingAsync", typeof(global::System.Threading.Tasks.Task), []), new XmlComment(@"This method is an example of a method that
+            _cache.Add(@"M:ExampleClass.DoNothingAsync~System.Threading.Tasks.Task", new XmlComment(@"This method is an example of a method that
 returns a Task which should map to a void return type.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ExampleClass), MemberType.Method, "AddNumbers", typeof(global::System.Int32), [typeof(global::System.Int32[])]), new XmlComment(@"This method is an example of a method that consumes
+            _cache.Add(@"M:ExampleClass.AddNumbers(System.Int32[])~System.Int32", new XmlComment(@"This method is an example of a method that consumes
 an params array.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ITestInterface), MemberType.Method, "Method", typeof(global::System.Int32), [typeof(global::System.Int32)]), new XmlComment(@"This method is part of the test interface.", null, @"This content would be inherited by classes
+            _cache.Add(@"M:ITestInterface.Method(System.Int32)~System.Int32", new XmlComment(@"This method is part of the test interface.", null, @"This content would be inherited by classes
 that implement this interface when the
 implementing class uses ""inheritdoc""", @"The value of arg", null, false, null, [new XmlParameterComment(@"arg", @"The argument to the method", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::InheritOnlyReturns), MemberType.Method, "MyParentMethod", typeof(global::System.Boolean), [typeof(global::System.Boolean)]), new XmlComment(@"In this example, this summary is only visible for this method.", null, null, @"A boolean", null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::InheritOnlyReturns), MemberType.Method, "MyChildMethod", typeof(global::System.Boolean), []), new XmlComment(null, null, null, @"A boolean", null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::InheritAllButRemarks), MemberType.Method, "MyParentMethod", typeof(global::System.Boolean), [typeof(global::System.Boolean)]), new XmlComment(@"In this example, this summary is visible on all the methods.", null, @"The remarks can be inherited by other methods
+            _cache.Add(@"M:InheritOnlyReturns.MyParentMethod(System.Boolean)~System.Boolean", new XmlComment(@"In this example, this summary is only visible for this method.", null, null, @"A boolean", null, false, null, null, null));
+            _cache.Add(@"M:InheritOnlyReturns.MyChildMethod~System.Boolean", new XmlComment(null, null, null, @"A boolean", null, false, null, null, null));
+            _cache.Add(@"M:InheritAllButRemarks.MyParentMethod(System.Boolean)~System.Boolean", new XmlComment(@"In this example, this summary is visible on all the methods.", null, @"The remarks can be inherited by other methods
 using the xpath expression.", @"A boolean", null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::InheritAllButRemarks), MemberType.Method, "MyChildMethod", typeof(global::System.Boolean), []), new XmlComment(@"In this example, this summary is visible on all the methods.", null, null, @"A boolean", null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetTaskOfTuple", typeof(global::System.Threading.Tasks.Task<global::System.ValueTuple<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method returns a generic type containing a tuple.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetTupleOfTask", typeof(global::System.ValueTuple<global::System.Int32, global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method returns a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetTupleOfTask2", typeof(global::System.ValueTuple<object, global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method return a tuple with a generic type containing a
+            _cache.Add(@"M:InheritAllButRemarks.MyChildMethod~System.Boolean", new XmlComment(@"In this example, this summary is visible on all the methods.", null, null, @"A boolean", null, false, null, null, null));
+            _cache.Add(@"M:GenericParent.GetTaskOfTuple~System.Threading.Tasks.Task{System.ValueTuple{System.Int32,System.String}}", new XmlComment(@"This method returns a generic type containing a tuple.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:GenericParent.GetTupleOfTask~System.ValueTuple{System.Int32,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method returns a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:GenericParent.GetTupleOfTask1``1~System.ValueTuple{System.Int32,System.Collections.Generic.Dictionary{System.Int32,``0}}", new XmlComment(@"This method return a tuple with a generic type containing a
+type parameter inside.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:GenericParent.GetTupleOfTask2``1~System.ValueTuple{``0,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method return a tuple with a generic type containing a
 type parameter inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::GenericParent), MemberType.Method, "GetNestedGeneric", typeof(global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.Collections.Generic.Dictionary<global::System.Int32, global::System.String>>), []), new XmlComment(@"This method returns a nested generic with all types resolved.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ParamsAndParamRefs), MemberType.Method, "GetGenericValue", typeof(object), [typeof(object)]), new XmlComment(@"The GetGenericValue method.", null, @"This sample shows how to specify the T ParamsAndParamRefs.GetGenericValue&lt;T&gt;(T para)
+            _cache.Add(@"M:GenericParent.GetNestedGeneric~System.Collections.Generic.Dictionary{System.Int32,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method returns a nested generic with all types resolved.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:GenericParent.GetNestedGeneric1``1~System.Collections.Generic.Dictionary{System.Int32,System.Collections.Generic.Dictionary{System.Int32,``0}}", new XmlComment(@"This method returns a nested generic with a type parameter.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:ParamsAndParamRefs.GetGenericValue``1(``0)~``0", new XmlComment(@"The GetGenericValue method.", null, @"This sample shows how to specify the T ParamsAndParamRefs.GetGenericValue&lt;T&gt;(T para)
 method as a cref attribute.
 The parameter and return value are both of an arbitrary type,
 T", null, null, false, null, null, null));
 
             return _cache;
         }
+    }
 
-        internal static bool TryGetXmlComment(Type? type, MethodInfo? methodInfo, [NotNullWhen(true)] out XmlComment? xmlComment)
+    file static class DocumentationCommentIdHelper
+    {
+        /// <summary>
+        /// Generates a documentation comment ID for a type.
+        /// Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
+        /// </summary>
+        public static string CreateDocumentationId(this Type type)
         {
-            if (methodInfo is null)
+            if (type == null)
+            {
+                throw new ArgumentNullException(nameof(type));
+            }
+
+            return "T:" + GetTypeDocId(type, includeGenericArguments: false, omitGenericArity: false);
+        }
+
+        /// <summary>
+        /// Generates a documentation comment ID for a property.
+        /// Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
+        /// </summary>
+        public static string CreateDocumentationId(this PropertyInfo property)
+        {
+            if (property == null)
+            {
+                throw new ArgumentNullException(nameof(property));
+            }
+
+            var sb = new StringBuilder();
+            sb.Append("P:");
+
+            if (property.DeclaringType != null)
             {
-                return Cache.TryGetValue(new MemberKey(type, MemberType.Property, null, null, null), out xmlComment);
+                sb.Append(GetTypeDocId(property.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
             }
 
-            return Cache.TryGetValue(MemberKey.FromMethodInfo(methodInfo), out xmlComment);
+            sb.Append('.');
+            sb.Append(property.Name);
+
+            // For indexers, include the parameter list.
+            var indexParams = property.GetIndexParameters();
+            if (indexParams.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                sb.Append(')');
+            }
+
+            return sb.ToString();
         }
 
-        internal static bool TryGetXmlComment(Type? type, string? memberName, [NotNullWhen(true)] out XmlComment? xmlComment)
+        /// <summary>
+        /// Generates a documentation comment ID for a method (or constructor).
+        /// For example:
+        ///   M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
+        ///   M:Namespace.ContainingType.#ctor(ParamType)
+        /// </summary>
+        public static string CreateDocumentationId(this MethodInfo method)
         {
-            return Cache.TryGetValue(new MemberKey(type, memberName is null ? MemberType.Type : MemberType.Property, memberName, null, null), out xmlComment);
+            System.Diagnostics.Debugger.Break();
+            if (method == null)
+            {
+                throw new ArgumentNullException(nameof(method));
+            }
+
+            var sb = new StringBuilder();
+            sb.Append("M:");
+
+            // Append the fully qualified name of the declaring type.
+            if (method.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(method.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
+
+            sb.Append('.');
+
+            // Append the method name, handling constructors specially.
+            if (method.IsConstructor)
+            {
+                sb.Append(method.IsStatic ? "#cctor" : "#ctor");
+            }
+            else
+            {
+                sb.Append(method.Name);
+                if (method.IsGenericMethod)
+                {
+                    sb.Append("``");
+                    sb.Append(method.GetGenericArguments().Length);
+                }
+            }
+
+            // Append the parameter list, if any.
+            var parameters = method.GetParameters();
+            if (parameters.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                sb.Append(')');
+            }
+
+            // Append the return type after a '~' (if the method returns a value).
+            if (method.ReturnType != typeof(void))
+            {
+                sb.Append('~');
+                // Omit the generic arity for the return type.
+                sb.Append(GetTypeDocId(method.ReturnType, includeGenericArguments: true, omitGenericArity: true));
+            }
+
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// Generates a documentation ID string for a type.
+        /// This method handles nested types (replacing '+' with '.'),
+        /// generic types, arrays, pointers, by-ref types, and generic parameters.
+        /// The <paramref name="includeGenericArguments"/> flag controls whether
+        /// constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
+        /// controls whether the generic arity marker (e.g. "`1") is appended.
+        /// </summary>
+        private static string GetTypeDocId(Type type, bool includeGenericArguments, bool omitGenericArity)
+        {
+            if (type.IsGenericParameter)
+            {
+                // Use `` for method-level generic parameters and ` for type-level.
+                if (type.DeclaringMethod != null)
+                {
+                    return "``" + type.GenericParameterPosition;
+                }
+                else if (type.DeclaringType != null)
+                {
+                    return "`" + type.GenericParameterPosition;
+                }
+                else
+                {
+                    return type.Name;
+                }
+            }
+
+            if (type.IsArray && type.GetElementType() is { } elementType)
+            {
+                int rank = type.GetArrayRank();
+                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
+                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+            }
+
+            if (type.IsGenericType)
+            {
+                // Get the generic type definition.
+                Type genericDef = type.GetGenericTypeDefinition();
+                string defName = genericDef.FullName != null
+                    ? genericDef.FullName.Replace('+', '.')
+                    : genericDef.Name;
+                // Remove any existing generic arity marker.
+                int backtickIndex = defName.IndexOf('`');
+                if (backtickIndex >= 0)
+                {
+                    defName = defName.Substring(0, backtickIndex);
+                }
+                // Append the generic arity unless omitted.
+                if (!omitGenericArity)
+                {
+                    int arity = genericDef.GetGenericArguments().Length;
+                    defName += "`" + arity;
+                }
+                // If requested, append the constructed generic arguments.
+                if (includeGenericArguments && !type.IsGenericTypeDefinition)
+                {
+                    Type[] typeArgs = type.GetGenericArguments();
+                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
+                    return defName + "{" + args + "}";
+                }
+                else
+                {
+                    return defName;
+                }
+            }
+
+            // For non-generic types, use FullName (if available) and replace nested type separators.
+            return (type.FullName ?? type.Name).Replace('+', '.');
         }
     }
 
@@ -335,7 +389,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
             {
                 return Task.CompletedTask;
             }
-            if (XmlCommentCache.TryGetXmlComment(methodInfo.DeclaringType, methodInfo, out var methodComment))
+            if (XmlCommentCache.Cache.TryGetValue(methodInfo.CreateDocumentationId(), out var methodComment))
             {
                 if (methodComment.Summary is { } summary)
                 {
@@ -408,7 +462,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
         {
             if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
             {
-                if (XmlCommentCache.TryGetXmlComment(propertyInfo.DeclaringType, propertyInfo.Name, out var propertyComment))
+                if (XmlCommentCache.Cache.TryGetValue(propertyInfo.CreateDocumentationId(), out var propertyComment))
                 {
                     schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
                     if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -417,7 +471,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
                     }
                 }
             }
-            if (XmlCommentCache.TryGetXmlComment(context.JsonTypeInfo.Type, (string?)null, out var typeComment))
+            if (XmlCommentCache.Cache.TryGetValue(context.JsonTypeInfo.Type.CreateDocumentationId(), out var typeComment))
             {
                 schema.Description = typeComment.Summary;
                 if (typeComment.Examples?.FirstOrDefault() is { } jsonString)
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
index 8b1e2c5e6d90..577e6c7c502b 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
@@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Reflection;
+    using System.Text;
     using System.Text.Json;
     using System.Text.Json.Nodes;
     using System.Threading;
@@ -61,152 +62,202 @@ file record XmlComment(
     file record XmlResponseComment(string Code, string? Description, string? Example);
 
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file sealed record MemberKey(
-        Type? DeclaringType,
-        MemberType MemberKind,
-        string? Name,
-        Type? ReturnType,
-        Type[]? Parameters) : IEquatable<MemberKey>
+    file static class XmlCommentCache
     {
-        public bool Equals(MemberKey? other)
-        {
-            if (other is null) return false;
+        private static Dictionary<string, XmlComment>? _cache;
+        public static Dictionary<string, XmlComment> Cache => _cache ??= GenerateCacheEntries();
 
-            // Check member kind
-            if (MemberKind != other.MemberKind) return false;
+        private static Dictionary<string, XmlComment> GenerateCacheEntries()
+        {
+            var _cache = new Dictionary<string, XmlComment>();
 
-            // Check declaring type, handling generic types
-            if (!TypesEqual(DeclaringType, other.DeclaringType)) return false;
+            _cache.Add(@"M:TestController.Get~System.String", new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
+            _cache.Add(@"M:Test2Controller.Get(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
+            _cache.Add(@"M:Test2Controller.Get(System.Int32)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"id", @"The id associated with the request.", null, false)], null));
+            _cache.Add(@"M:Test2Controller.Post(Todo)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"todo", @"The todo to insert into the database.", null, false)], null));
 
-            // Check name
-            if (Name != other.Name) return false;
+            return _cache;
+        }
+    }
 
-            // For methods, check return type and parameters
-            if (MemberKind == MemberType.Method)
+    file static class DocumentationCommentIdHelper
+    {
+        /// <summary>
+        /// Generates a documentation comment ID for a type.
+        /// Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
+        /// </summary>
+        public static string CreateDocumentationId(this Type type)
+        {
+            if (type == null)
             {
-                if (!TypesEqual(ReturnType, other.ReturnType)) return false;
-                if (Parameters is null || other.Parameters is null) return false;
-                if (Parameters.Length != other.Parameters.Length) return false;
-
-                for (int i = 0; i < Parameters.Length; i++)
-                {
-                    if (!TypesEqual(Parameters[i], other.Parameters[i])) return false;
-                }
+                throw new ArgumentNullException(nameof(type));
             }
 
-            return true;
+            return "T:" + GetTypeDocId(type, includeGenericArguments: false, omitGenericArity: false);
         }
 
-        private static bool TypesEqual(Type? type1, Type? type2)
+        /// <summary>
+        /// Generates a documentation comment ID for a property.
+        /// Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
+        /// </summary>
+        public static string CreateDocumentationId(this PropertyInfo property)
         {
-            if (type1 == type2) return true;
-            if (type1 == null || type2 == null) return false;
-
-            if (type1.IsGenericType && type2.IsGenericType)
+            if (property == null)
             {
-                return type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition();
+                throw new ArgumentNullException(nameof(property));
             }
 
-            return type1 == type2;
-        }
-
-        public override int GetHashCode()
-        {
-            var hash = new HashCode();
-            hash.Add(GetTypeHashCode(DeclaringType));
-            hash.Add(MemberKind);
-            hash.Add(Name);
+            var sb = new StringBuilder();
+            sb.Append("P:");
 
-            if (MemberKind == MemberType.Method)
+            if (property.DeclaringType != null)
             {
-                hash.Add(GetTypeHashCode(ReturnType));
-                if (Parameters is not null)
-                {
-                    foreach (var param in Parameters)
-                    {
-                        hash.Add(GetTypeHashCode(param));
-                    }
-                }
+                sb.Append(GetTypeDocId(property.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
             }
 
-            return hash.ToHashCode();
-        }
+            sb.Append('.');
+            sb.Append(property.Name);
 
-        private static int GetTypeHashCode(Type? type)
-        {
-            if (type == null) return 0;
-            return type.IsGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode();
-        }
+            // For indexers, include the parameter list.
+            var indexParams = property.GetIndexParameters();
+            if (indexParams.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                sb.Append(')');
+            }
 
-        public static MemberKey FromMethodInfo(MethodInfo method)
-        {
-            return new MemberKey(
-                method.DeclaringType,
-                MemberType.Method,
-                method.Name,
-                method.ReturnType.IsGenericParameter ? typeof(object) : method.ReturnType,
-                method.GetParameters().Select(p => p.ParameterType.IsGenericParameter ? typeof(object) : p.ParameterType).ToArray());
+            return sb.ToString();
         }
 
-        public static MemberKey FromPropertyInfo(PropertyInfo property)
+        /// <summary>
+        /// Generates a documentation comment ID for a method (or constructor).
+        /// For example:
+        ///   M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
+        ///   M:Namespace.ContainingType.#ctor(ParamType)
+        /// </summary>
+        public static string CreateDocumentationId(this MethodInfo method)
         {
-            return new MemberKey(
-                property.DeclaringType,
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
+            System.Diagnostics.Debugger.Break();
+            if (method == null)
+            {
+                throw new ArgumentNullException(nameof(method));
+            }
 
-        public static MemberKey FromTypeInfo(Type type)
-        {
-            return new MemberKey(
-                type,
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-    }
+            var sb = new StringBuilder();
+            sb.Append("M:");
 
-    file enum MemberType
-    {
-        Type,
-        Property,
-        Method
-    }
+            // Append the fully qualified name of the declaring type.
+            if (method.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(method.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
 
-    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file static class XmlCommentCache
-    {
-        private static Dictionary<MemberKey, XmlComment>? _cache;
-        public static Dictionary<MemberKey, XmlComment> Cache => _cache ??= GenerateCacheEntries();
+            sb.Append('.');
 
-        private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
-        {
-            var _cache = new Dictionary<MemberKey, XmlComment>();
+            // Append the method name, handling constructors specially.
+            if (method.IsConstructor)
+            {
+                sb.Append(method.IsStatic ? "#cctor" : "#ctor");
+            }
+            else
+            {
+                sb.Append(method.Name);
+                if (method.IsGenericMethod)
+                {
+                    sb.Append("``");
+                    sb.Append(method.GetGenericArguments().Length);
+                }
+            }
+
+            // Append the parameter list, if any.
+            var parameters = method.GetParameters();
+            if (parameters.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                sb.Append(')');
+            }
 
-            _cache.Add(new MemberKey(typeof(global::TestController), MemberType.Method, "Get", typeof(global::System.String), []), new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::Test2Controller), MemberType.Method, "Get", typeof(global::System.String), [typeof(global::System.String)]), new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
-            _cache.Add(new MemberKey(typeof(global::Test2Controller), MemberType.Method, "Get", typeof(global::System.String), [typeof(global::System.Int32)]), new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"id", @"The id associated with the request.", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::Test2Controller), MemberType.Method, "Post", typeof(global::System.String), [typeof(global::Todo)]), new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"todo", @"The todo to insert into the database.", null, false)], null));
+            // Append the return type after a '~' (if the method returns a value).
+            if (method.ReturnType != typeof(void))
+            {
+                sb.Append('~');
+                // Omit the generic arity for the return type.
+                sb.Append(GetTypeDocId(method.ReturnType, includeGenericArguments: true, omitGenericArity: true));
+            }
 
-            return _cache;
+            return sb.ToString();
         }
 
-        internal static bool TryGetXmlComment(Type? type, MethodInfo? methodInfo, [NotNullWhen(true)] out XmlComment? xmlComment)
+        /// <summary>
+        /// Generates a documentation ID string for a type.
+        /// This method handles nested types (replacing '+' with '.'),
+        /// generic types, arrays, pointers, by-ref types, and generic parameters.
+        /// The <paramref name="includeGenericArguments"/> flag controls whether
+        /// constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
+        /// controls whether the generic arity marker (e.g. "`1") is appended.
+        /// </summary>
+        private static string GetTypeDocId(Type type, bool includeGenericArguments, bool omitGenericArity)
         {
-            if (methodInfo is null)
+            if (type.IsGenericParameter)
+            {
+                // Use `` for method-level generic parameters and ` for type-level.
+                if (type.DeclaringMethod != null)
+                {
+                    return "``" + type.GenericParameterPosition;
+                }
+                else if (type.DeclaringType != null)
+                {
+                    return "`" + type.GenericParameterPosition;
+                }
+                else
+                {
+                    return type.Name;
+                }
+            }
+
+            if (type.IsArray && type.GetElementType() is { } elementType)
             {
-                return Cache.TryGetValue(new MemberKey(type, MemberType.Property, null, null, null), out xmlComment);
+                int rank = type.GetArrayRank();
+                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
+                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
             }
 
-            return Cache.TryGetValue(MemberKey.FromMethodInfo(methodInfo), out xmlComment);
-        }
+            if (type.IsGenericType)
+            {
+                // Get the generic type definition.
+                Type genericDef = type.GetGenericTypeDefinition();
+                string defName = genericDef.FullName != null
+                    ? genericDef.FullName.Replace('+', '.')
+                    : genericDef.Name;
+                // Remove any existing generic arity marker.
+                int backtickIndex = defName.IndexOf('`');
+                if (backtickIndex >= 0)
+                {
+                    defName = defName.Substring(0, backtickIndex);
+                }
+                // Append the generic arity unless omitted.
+                if (!omitGenericArity)
+                {
+                    int arity = genericDef.GetGenericArguments().Length;
+                    defName += "`" + arity;
+                }
+                // If requested, append the constructed generic arguments.
+                if (includeGenericArguments && !type.IsGenericTypeDefinition)
+                {
+                    Type[] typeArgs = type.GetGenericArguments();
+                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
+                    return defName + "{" + args + "}";
+                }
+                else
+                {
+                    return defName;
+                }
+            }
 
-        internal static bool TryGetXmlComment(Type? type, string? memberName, [NotNullWhen(true)] out XmlComment? xmlComment)
-        {
-            return Cache.TryGetValue(new MemberKey(type, memberName is null ? MemberType.Type : MemberType.Property, memberName, null, null), out xmlComment);
+            // For non-generic types, use FullName (if available) and replace nested type separators.
+            return (type.FullName ?? type.Name).Replace('+', '.');
         }
     }
 
@@ -223,7 +274,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
             {
                 return Task.CompletedTask;
             }
-            if (XmlCommentCache.TryGetXmlComment(methodInfo.DeclaringType, methodInfo, out var methodComment))
+            if (XmlCommentCache.Cache.TryGetValue(methodInfo.CreateDocumentationId(), out var methodComment))
             {
                 if (methodComment.Summary is { } summary)
                 {
@@ -296,7 +347,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
         {
             if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
             {
-                if (XmlCommentCache.TryGetXmlComment(propertyInfo.DeclaringType, propertyInfo.Name, out var propertyComment))
+                if (XmlCommentCache.Cache.TryGetValue(propertyInfo.CreateDocumentationId(), out var propertyComment))
                 {
                     schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
                     if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -305,7 +356,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
                     }
                 }
             }
-            if (XmlCommentCache.TryGetXmlComment(context.JsonTypeInfo.Type, (string?)null, out var typeComment))
+            if (XmlCommentCache.Cache.TryGetValue(context.JsonTypeInfo.Type.CreateDocumentationId(), out var typeComment))
             {
                 schema.Description = typeComment.Summary;
                 if (typeComment.Examples?.FirstOrDefault() is { } jsonString)
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
index e1b46c103eb2..3f6903f3304a 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
@@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Reflection;
+    using System.Text;
     using System.Text.Json;
     using System.Text.Json.Nodes;
     using System.Threading;
@@ -61,169 +62,219 @@ file record XmlComment(
     file record XmlResponseComment(string Code, string? Description, string? Example);
 
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file sealed record MemberKey(
-        Type? DeclaringType,
-        MemberType MemberKind,
-        string? Name,
-        Type? ReturnType,
-        Type[]? Parameters) : IEquatable<MemberKey>
+    file static class XmlCommentCache
     {
-        public bool Equals(MemberKey? other)
-        {
-            if (other is null) return false;
-
-            // Check member kind
-            if (MemberKind != other.MemberKind) return false;
+        private static Dictionary<string, XmlComment>? _cache;
+        public static Dictionary<string, XmlComment> Cache => _cache ??= GenerateCacheEntries();
 
-            // Check declaring type, handling generic types
-            if (!TypesEqual(DeclaringType, other.DeclaringType)) return false;
+        private static Dictionary<string, XmlComment> GenerateCacheEntries()
+        {
+            var _cache = new Dictionary<string, XmlComment>();
+
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get~System.String", new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get2(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get3(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", @"Testy McTester", false)], null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get4~Microsoft.AspNetCore.Http.HttpResults.NotFound{System.String}", new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get5~Microsoft.AspNetCore.Http.HttpResults.Results{Microsoft.AspNetCore.Http.HttpResults.NotFound{System.String},Microsoft.AspNetCore.Http.HttpResults.Ok{System.String},Microsoft.AspNetCore.Http.HttpResults.Created}", new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"200", @"Indicates that the value is even.", @""), new XmlResponseComment(@"201", @"Indicates that the value is less than 50.", @""), new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Post6(User)~Microsoft.AspNetCore.Http.IResult", new XmlComment(@"Creates a new user.", null, @"Sample request:
+    POST /6
+    {
+        ""username"": ""johndoe"",
+        ""email"": ""john@example.com""
+    }", null, null, false, null, [new XmlParameterComment(@"user", @"The user information.", @"{""username"": ""johndoe"", ""email"": ""john@example.com""}", false)], [new XmlResponseComment(@"201", @"Successfully created the user.", @""), new XmlResponseComment(@"400", @"If the user data is invalid.", @"")]));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Put7(System.Nullable{System.Int32},System.String)~Microsoft.AspNetCore.Http.IResult", new XmlComment(@"Updates an existing record.", null, null, null, null, false, null, [new XmlParameterComment(@"id", @"Legacy ID parameter - use uuid instead.", null, true), new XmlParameterComment(@"uuid", @"Unique identifier for the record.", null, false)], [new XmlResponseComment(@"204", @"Update successful.", @""), new XmlResponseComment(@"404", @"Legacy response - will be removed.", @"")]));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get8~System.Threading.Tasks.Task", new XmlComment(@"A summary of Get8.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get9~System.Threading.Tasks.ValueTask", new XmlComment(@"A summary of Get9.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get10~System.Threading.Tasks.Task", new XmlComment(@"A summary of Get10.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get11~System.Threading.Tasks.ValueTask", new XmlComment(@"A summary of Get11.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get12~System.Threading.Tasks.Task{System.String}", new XmlComment(@"A summary of Get12.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get13~System.Threading.Tasks.ValueTask{System.String}", new XmlComment(@"A summary of Get13.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get14~System.Threading.Tasks.Task{Holder{System.String}}", new XmlComment(@"A summary of Get14.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Get15~System.Threading.Tasks.Task{Holder{System.String}}", new XmlComment(@"A summary of Get15.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"M:RouteHandlerExtensionMethods.Post16(Example)", new XmlComment(@"A summary of Post16.", null, null, null, null, false, null, null, null));
 
-            // Check name
-            if (Name != other.Name) return false;
+            return _cache;
+        }
+    }
 
-            // For methods, check return type and parameters
-            if (MemberKind == MemberType.Method)
+    file static class DocumentationCommentIdHelper
+    {
+        /// <summary>
+        /// Generates a documentation comment ID for a type.
+        /// Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
+        /// </summary>
+        public static string CreateDocumentationId(this Type type)
+        {
+            if (type == null)
             {
-                if (!TypesEqual(ReturnType, other.ReturnType)) return false;
-                if (Parameters is null || other.Parameters is null) return false;
-                if (Parameters.Length != other.Parameters.Length) return false;
-
-                for (int i = 0; i < Parameters.Length; i++)
-                {
-                    if (!TypesEqual(Parameters[i], other.Parameters[i])) return false;
-                }
+                throw new ArgumentNullException(nameof(type));
             }
 
-            return true;
+            return "T:" + GetTypeDocId(type, includeGenericArguments: false, omitGenericArity: false);
         }
 
-        private static bool TypesEqual(Type? type1, Type? type2)
+        /// <summary>
+        /// Generates a documentation comment ID for a property.
+        /// Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
+        /// </summary>
+        public static string CreateDocumentationId(this PropertyInfo property)
         {
-            if (type1 == type2) return true;
-            if (type1 == null || type2 == null) return false;
-
-            if (type1.IsGenericType && type2.IsGenericType)
+            if (property == null)
             {
-                return type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition();
+                throw new ArgumentNullException(nameof(property));
             }
 
-            return type1 == type2;
-        }
+            var sb = new StringBuilder();
+            sb.Append("P:");
 
-        public override int GetHashCode()
-        {
-            var hash = new HashCode();
-            hash.Add(GetTypeHashCode(DeclaringType));
-            hash.Add(MemberKind);
-            hash.Add(Name);
+            if (property.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(property.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
+
+            sb.Append('.');
+            sb.Append(property.Name);
 
-            if (MemberKind == MemberType.Method)
+            // For indexers, include the parameter list.
+            var indexParams = property.GetIndexParameters();
+            if (indexParams.Length > 0)
             {
-                hash.Add(GetTypeHashCode(ReturnType));
-                if (Parameters is not null)
-                {
-                    foreach (var param in Parameters)
-                    {
-                        hash.Add(GetTypeHashCode(param));
-                    }
-                }
+                sb.Append('(');
+                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                sb.Append(')');
             }
 
-            return hash.ToHashCode();
+            return sb.ToString();
         }
 
-        private static int GetTypeHashCode(Type? type)
+        /// <summary>
+        /// Generates a documentation comment ID for a method (or constructor).
+        /// For example:
+        ///   M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
+        ///   M:Namespace.ContainingType.#ctor(ParamType)
+        /// </summary>
+        public static string CreateDocumentationId(this MethodInfo method)
         {
-            if (type == null) return 0;
-            return type.IsGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode();
-        }
+            System.Diagnostics.Debugger.Break();
+            if (method == null)
+            {
+                throw new ArgumentNullException(nameof(method));
+            }
 
-        public static MemberKey FromMethodInfo(MethodInfo method)
-        {
-            return new MemberKey(
-                method.DeclaringType,
-                MemberType.Method,
-                method.Name,
-                method.ReturnType.IsGenericParameter ? typeof(object) : method.ReturnType,
-                method.GetParameters().Select(p => p.ParameterType.IsGenericParameter ? typeof(object) : p.ParameterType).ToArray());
-        }
+            var sb = new StringBuilder();
+            sb.Append("M:");
 
-        public static MemberKey FromPropertyInfo(PropertyInfo property)
-        {
-            return new MemberKey(
-                property.DeclaringType,
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
+            // Append the fully qualified name of the declaring type.
+            if (method.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(method.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
 
-        public static MemberKey FromTypeInfo(Type type)
-        {
-            return new MemberKey(
-                type,
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-    }
+            sb.Append('.');
 
-    file enum MemberType
-    {
-        Type,
-        Property,
-        Method
-    }
+            // Append the method name, handling constructors specially.
+            if (method.IsConstructor)
+            {
+                sb.Append(method.IsStatic ? "#cctor" : "#ctor");
+            }
+            else
+            {
+                sb.Append(method.Name);
+                if (method.IsGenericMethod)
+                {
+                    sb.Append("``");
+                    sb.Append(method.GetGenericArguments().Length);
+                }
+            }
 
-    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file static class XmlCommentCache
-    {
-        private static Dictionary<MemberKey, XmlComment>? _cache;
-        public static Dictionary<MemberKey, XmlComment> Cache => _cache ??= GenerateCacheEntries();
+            // Append the parameter list, if any.
+            var parameters = method.GetParameters();
+            if (parameters.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                sb.Append(')');
+            }
 
-        private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
-        {
-            var _cache = new Dictionary<MemberKey, XmlComment>();
-
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get", typeof(global::System.String), []), new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get2", typeof(global::System.String), [typeof(global::System.String)]), new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get3", typeof(global::System.String), [typeof(global::System.String)]), new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", @"Testy McTester", false)], null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get4", typeof(global::Microsoft.AspNetCore.Http.HttpResults.NotFound<global::System.String>), []), new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get5", typeof(global::Microsoft.AspNetCore.Http.HttpResults.Results<global::Microsoft.AspNetCore.Http.HttpResults.NotFound<global::System.String>, global::Microsoft.AspNetCore.Http.HttpResults.Ok<global::System.String>, global::Microsoft.AspNetCore.Http.HttpResults.Created>), []), new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"200", @"Indicates that the value is even.", @""), new XmlResponseComment(@"201", @"Indicates that the value is less than 50.", @""), new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Post6", typeof(global::Microsoft.AspNetCore.Http.IResult), [typeof(global::User)]), new XmlComment(@"Creates a new user.", null, @"Sample request:
-    POST /6
-    {
-        ""username"": ""johndoe"",
-        ""email"": ""john@example.com""
-    }", null, null, false, null, [new XmlParameterComment(@"user", @"The user information.", @"{""username"": ""johndoe"", ""email"": ""john@example.com""}", false)], [new XmlResponseComment(@"201", @"Successfully created the user.", @""), new XmlResponseComment(@"400", @"If the user data is invalid.", @"")]));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Put7", typeof(global::Microsoft.AspNetCore.Http.IResult), [typeof(global::System.Int32?), typeof(global::System.String)]), new XmlComment(@"Updates an existing record.", null, null, null, null, false, null, [new XmlParameterComment(@"id", @"Legacy ID parameter - use uuid instead.", null, true), new XmlParameterComment(@"uuid", @"Unique identifier for the record.", null, false)], [new XmlResponseComment(@"204", @"Update successful.", @""), new XmlResponseComment(@"404", @"Legacy response - will be removed.", @"")]));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get8", typeof(global::System.Threading.Tasks.Task), []), new XmlComment(@"A summary of Get8.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get9", typeof(global::System.Threading.Tasks.ValueTask), []), new XmlComment(@"A summary of Get9.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get10", typeof(global::System.Threading.Tasks.Task), []), new XmlComment(@"A summary of Get10.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get11", typeof(global::System.Threading.Tasks.ValueTask), []), new XmlComment(@"A summary of Get11.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get12", typeof(global::System.Threading.Tasks.Task<global::System.String>), []), new XmlComment(@"A summary of Get12.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get13", typeof(global::System.Threading.Tasks.ValueTask<global::System.String>), []), new XmlComment(@"A summary of Get13.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get14", typeof(global::System.Threading.Tasks.Task<global::Holder<global::System.String>>), []), new XmlComment(@"A summary of Get14.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Get15", typeof(global::System.Threading.Tasks.Task<global::Holder<global::System.String>>), []), new XmlComment(@"A summary of Get15.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::RouteHandlerExtensionMethods), MemberType.Method, "Post16", typeof(void), [typeof(global::Example)]), new XmlComment(@"A summary of Post16.", null, null, null, null, false, null, null, null));
+            // Append the return type after a '~' (if the method returns a value).
+            if (method.ReturnType != typeof(void))
+            {
+                sb.Append('~');
+                // Omit the generic arity for the return type.
+                sb.Append(GetTypeDocId(method.ReturnType, includeGenericArguments: true, omitGenericArity: true));
+            }
 
-            return _cache;
+            return sb.ToString();
         }
 
-        internal static bool TryGetXmlComment(Type? type, MethodInfo? methodInfo, [NotNullWhen(true)] out XmlComment? xmlComment)
+        /// <summary>
+        /// Generates a documentation ID string for a type.
+        /// This method handles nested types (replacing '+' with '.'),
+        /// generic types, arrays, pointers, by-ref types, and generic parameters.
+        /// The <paramref name="includeGenericArguments"/> flag controls whether
+        /// constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
+        /// controls whether the generic arity marker (e.g. "`1") is appended.
+        /// </summary>
+        private static string GetTypeDocId(Type type, bool includeGenericArguments, bool omitGenericArity)
         {
-            if (methodInfo is null)
+            if (type.IsGenericParameter)
             {
-                return Cache.TryGetValue(new MemberKey(type, MemberType.Property, null, null, null), out xmlComment);
+                // Use `` for method-level generic parameters and ` for type-level.
+                if (type.DeclaringMethod != null)
+                {
+                    return "``" + type.GenericParameterPosition;
+                }
+                else if (type.DeclaringType != null)
+                {
+                    return "`" + type.GenericParameterPosition;
+                }
+                else
+                {
+                    return type.Name;
+                }
             }
 
-            return Cache.TryGetValue(MemberKey.FromMethodInfo(methodInfo), out xmlComment);
-        }
+            if (type.IsArray && type.GetElementType() is { } elementType)
+            {
+                int rank = type.GetArrayRank();
+                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
+                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+            }
 
-        internal static bool TryGetXmlComment(Type? type, string? memberName, [NotNullWhen(true)] out XmlComment? xmlComment)
-        {
-            return Cache.TryGetValue(new MemberKey(type, memberName is null ? MemberType.Type : MemberType.Property, memberName, null, null), out xmlComment);
+            if (type.IsGenericType)
+            {
+                // Get the generic type definition.
+                Type genericDef = type.GetGenericTypeDefinition();
+                string defName = genericDef.FullName != null
+                    ? genericDef.FullName.Replace('+', '.')
+                    : genericDef.Name;
+                // Remove any existing generic arity marker.
+                int backtickIndex = defName.IndexOf('`');
+                if (backtickIndex >= 0)
+                {
+                    defName = defName.Substring(0, backtickIndex);
+                }
+                // Append the generic arity unless omitted.
+                if (!omitGenericArity)
+                {
+                    int arity = genericDef.GetGenericArguments().Length;
+                    defName += "`" + arity;
+                }
+                // If requested, append the constructed generic arguments.
+                if (includeGenericArguments && !type.IsGenericTypeDefinition)
+                {
+                    Type[] typeArgs = type.GetGenericArguments();
+                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
+                    return defName + "{" + args + "}";
+                }
+                else
+                {
+                    return defName;
+                }
+            }
+
+            // For non-generic types, use FullName (if available) and replace nested type separators.
+            return (type.FullName ?? type.Name).Replace('+', '.');
         }
     }
 
@@ -240,7 +291,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
             {
                 return Task.CompletedTask;
             }
-            if (XmlCommentCache.TryGetXmlComment(methodInfo.DeclaringType, methodInfo, out var methodComment))
+            if (XmlCommentCache.Cache.TryGetValue(methodInfo.CreateDocumentationId(), out var methodComment))
             {
                 if (methodComment.Summary is { } summary)
                 {
@@ -313,7 +364,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
         {
             if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
             {
-                if (XmlCommentCache.TryGetXmlComment(propertyInfo.DeclaringType, propertyInfo.Name, out var propertyComment))
+                if (XmlCommentCache.Cache.TryGetValue(propertyInfo.CreateDocumentationId(), out var propertyComment))
                 {
                     schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
                     if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -322,7 +373,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
                     }
                 }
             }
-            if (XmlCommentCache.TryGetXmlComment(context.JsonTypeInfo.Type, (string?)null, out var typeComment))
+            if (XmlCommentCache.Cache.TryGetValue(context.JsonTypeInfo.Type.CreateDocumentationId(), out var typeComment))
             {
                 schema.Description = typeComment.Summary;
                 if (typeComment.Examples?.FirstOrDefault() is { } jsonString)
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
index 0a5b3be9e07f..ef2565c5f3e9 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
@@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System.Diagnostics.CodeAnalysis;
     using System.Linq;
     using System.Reflection;
+    using System.Text;
     using System.Text.Json;
     using System.Text.Json.Nodes;
     using System.Threading;
@@ -61,178 +62,228 @@ file record XmlComment(
     file record XmlResponseComment(string Code, string? Description, string? Example);
 
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file sealed record MemberKey(
-        Type? DeclaringType,
-        MemberType MemberKind,
-        string? Name,
-        Type? ReturnType,
-        Type[]? Parameters) : IEquatable<MemberKey>
+    file static class XmlCommentCache
     {
-        public bool Equals(MemberKey? other)
-        {
-            if (other is null) return false;
-
-            // Check member kind
-            if (MemberKind != other.MemberKind) return false;
+        private static Dictionary<string, XmlComment>? _cache;
+        public static Dictionary<string, XmlComment> Cache => _cache ??= GenerateCacheEntries();
 
-            // Check declaring type, handling generic types
-            if (!TypesEqual(DeclaringType, other.DeclaringType)) return false;
+        private static Dictionary<string, XmlComment> GenerateCacheEntries()
+        {
+            var _cache = new Dictionary<string, XmlComment>();
+
+            _cache.Add(@"T:Todo", new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:Project", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:ProjectBoard.BoardItem", new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:ProjectBoard.ProtectedInternalElement", new XmlComment(@"Can find this XML comment.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"T:ProjectRecord", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
+            _cache.Add(@"T:User", new XmlComment(null, null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:ProjectBoard.ProtectedInternalElement.Name", new XmlComment(@"The unique identifier for the element.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:ProjectRecord.Name", new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:ProjectRecord.Description", new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:TodoWithDescription.Id", new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:TodoWithDescription.Name", new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
+            _cache.Add(@"P:TodoWithDescription.Description", new XmlComment(@"A description of the the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
+            _cache.Add(@"P:TypeWithExamples.BooleanType", new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
+            _cache.Add(@"P:TypeWithExamples.IntegerType", new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
+            _cache.Add(@"P:TypeWithExamples.LongType", new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
+            _cache.Add(@"P:TypeWithExamples.DoubleType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            _cache.Add(@"P:TypeWithExamples.FloatType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            _cache.Add(@"P:TypeWithExamples.DateTimeType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
+            _cache.Add(@"P:TypeWithExamples.DateOnlyType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
+            _cache.Add(@"P:TypeWithExamples.StringType", new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
+            _cache.Add(@"P:TypeWithExamples.GuidType", new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
+            _cache.Add(@"P:TypeWithExamples.TimeOnlyType", new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
+            _cache.Add(@"P:TypeWithExamples.TimeSpanType", new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
+            _cache.Add(@"P:TypeWithExamples.ByteType", new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
+            _cache.Add(@"P:TypeWithExamples.DecimalType", new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
+            _cache.Add(@"P:TypeWithExamples.UriType", new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
+            _cache.Add(@"P:IUser.Id", new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:IUser.Name", new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:User.Id", new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
+            _cache.Add(@"P:User.Name", new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
 
-            // Check name
-            if (Name != other.Name) return false;
+            return _cache;
+        }
+    }
 
-            // For methods, check return type and parameters
-            if (MemberKind == MemberType.Method)
+    file static class DocumentationCommentIdHelper
+    {
+        /// <summary>
+        /// Generates a documentation comment ID for a type.
+        /// Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
+        /// </summary>
+        public static string CreateDocumentationId(this Type type)
+        {
+            if (type == null)
             {
-                if (!TypesEqual(ReturnType, other.ReturnType)) return false;
-                if (Parameters is null || other.Parameters is null) return false;
-                if (Parameters.Length != other.Parameters.Length) return false;
-
-                for (int i = 0; i < Parameters.Length; i++)
-                {
-                    if (!TypesEqual(Parameters[i], other.Parameters[i])) return false;
-                }
+                throw new ArgumentNullException(nameof(type));
             }
 
-            return true;
+            return "T:" + GetTypeDocId(type, includeGenericArguments: false, omitGenericArity: false);
         }
 
-        private static bool TypesEqual(Type? type1, Type? type2)
+        /// <summary>
+        /// Generates a documentation comment ID for a property.
+        /// Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
+        /// </summary>
+        public static string CreateDocumentationId(this PropertyInfo property)
         {
-            if (type1 == type2) return true;
-            if (type1 == null || type2 == null) return false;
-
-            if (type1.IsGenericType && type2.IsGenericType)
+            if (property == null)
             {
-                return type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition();
+                throw new ArgumentNullException(nameof(property));
             }
 
-            return type1 == type2;
-        }
+            var sb = new StringBuilder();
+            sb.Append("P:");
 
-        public override int GetHashCode()
-        {
-            var hash = new HashCode();
-            hash.Add(GetTypeHashCode(DeclaringType));
-            hash.Add(MemberKind);
-            hash.Add(Name);
+            if (property.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(property.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
 
-            if (MemberKind == MemberType.Method)
+            sb.Append('.');
+            sb.Append(property.Name);
+
+            // For indexers, include the parameter list.
+            var indexParams = property.GetIndexParameters();
+            if (indexParams.Length > 0)
             {
-                hash.Add(GetTypeHashCode(ReturnType));
-                if (Parameters is not null)
-                {
-                    foreach (var param in Parameters)
-                    {
-                        hash.Add(GetTypeHashCode(param));
-                    }
-                }
+                sb.Append('(');
+                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                sb.Append(')');
             }
 
-            return hash.ToHashCode();
+            return sb.ToString();
         }
 
-        private static int GetTypeHashCode(Type? type)
+        /// <summary>
+        /// Generates a documentation comment ID for a method (or constructor).
+        /// For example:
+        ///   M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
+        ///   M:Namespace.ContainingType.#ctor(ParamType)
+        /// </summary>
+        public static string CreateDocumentationId(this MethodInfo method)
         {
-            if (type == null) return 0;
-            return type.IsGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode();
-        }
+            System.Diagnostics.Debugger.Break();
+            if (method == null)
+            {
+                throw new ArgumentNullException(nameof(method));
+            }
 
-        public static MemberKey FromMethodInfo(MethodInfo method)
-        {
-            return new MemberKey(
-                method.DeclaringType,
-                MemberType.Method,
-                method.Name,
-                method.ReturnType.IsGenericParameter ? typeof(object) : method.ReturnType,
-                method.GetParameters().Select(p => p.ParameterType.IsGenericParameter ? typeof(object) : p.ParameterType).ToArray());
-        }
+            var sb = new StringBuilder();
+            sb.Append("M:");
 
-        public static MemberKey FromPropertyInfo(PropertyInfo property)
-        {
-            return new MemberKey(
-                property.DeclaringType,
-                MemberType.Property,
-                property.Name,
-                null,
-                null);
-        }
+            // Append the fully qualified name of the declaring type.
+            if (method.DeclaringType != null)
+            {
+                sb.Append(GetTypeDocId(method.DeclaringType, includeGenericArguments: false, omitGenericArity: false));
+            }
 
-        public static MemberKey FromTypeInfo(Type type)
-        {
-            return new MemberKey(
-                type,
-                MemberType.Type,
-                null,
-                null,
-                null);
-        }
-    }
+            sb.Append('.');
 
-    file enum MemberType
-    {
-        Type,
-        Property,
-        Method
-    }
+            // Append the method name, handling constructors specially.
+            if (method.IsConstructor)
+            {
+                sb.Append(method.IsStatic ? "#cctor" : "#ctor");
+            }
+            else
+            {
+                sb.Append(method.Name);
+                if (method.IsGenericMethod)
+                {
+                    sb.Append("``");
+                    sb.Append(method.GetGenericArguments().Length);
+                }
+            }
 
-    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
-    file static class XmlCommentCache
-    {
-        private static Dictionary<MemberKey, XmlComment>? _cache;
-        public static Dictionary<MemberKey, XmlComment> Cache => _cache ??= GenerateCacheEntries();
+            // Append the parameter list, if any.
+            var parameters = method.GetParameters();
+            if (parameters.Length > 0)
+            {
+                sb.Append('(');
+                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                sb.Append(')');
+            }
 
-        private static Dictionary<MemberKey, XmlComment> GenerateCacheEntries()
-        {
-            var _cache = new Dictionary<MemberKey, XmlComment>();
-
-            _cache.Add(new MemberKey(typeof(global::Todo), MemberType.Type, null, null, []), new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::Project), MemberType.Type, null, null, []), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ProjectBoard.BoardItem), MemberType.Type, null, null, []), new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ProjectBoard.ProtectedInternalElement), MemberType.Type, null, null, []), new XmlComment(@"Can find this XML comment.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ProjectRecord), MemberType.Type, null, null, []), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
-            _cache.Add(new MemberKey(typeof(global::User), MemberType.Type, null, null, []), new XmlComment(null, null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ProjectBoard.ProtectedInternalElement), MemberType.Property, "Name", null, []), new XmlComment(@"The unique identifier for the element.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ProjectRecord), MemberType.Property, "Name", null, []), new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::ProjectRecord), MemberType.Property, "Description", null, []), new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::TodoWithDescription), MemberType.Property, "Id", null, []), new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::TodoWithDescription), MemberType.Property, "Name", null, []), new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::TodoWithDescription), MemberType.Property, "Description", null, []), new XmlComment(@"A description of the the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "BooleanType", null, []), new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "IntegerType", null, []), new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "LongType", null, []), new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "DoubleType", null, []), new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "FloatType", null, []), new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "DateTimeType", null, []), new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "DateOnlyType", null, []), new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "StringType", null, []), new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "GuidType", null, []), new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "TimeOnlyType", null, []), new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "TimeSpanType", null, []), new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "ByteType", null, []), new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "DecimalType", null, []), new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
-            _cache.Add(new MemberKey(typeof(global::TypeWithExamples), MemberType.Property, "UriType", null, []), new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
-            _cache.Add(new MemberKey(typeof(global::IUser), MemberType.Property, "Id", null, []), new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::IUser), MemberType.Property, "Name", null, []), new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::User), MemberType.Property, "Id", null, []), new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
-            _cache.Add(new MemberKey(typeof(global::User), MemberType.Property, "Name", null, []), new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
+            // Append the return type after a '~' (if the method returns a value).
+            if (method.ReturnType != typeof(void))
+            {
+                sb.Append('~');
+                // Omit the generic arity for the return type.
+                sb.Append(GetTypeDocId(method.ReturnType, includeGenericArguments: true, omitGenericArity: true));
+            }
 
-            return _cache;
+            return sb.ToString();
         }
 
-        internal static bool TryGetXmlComment(Type? type, MethodInfo? methodInfo, [NotNullWhen(true)] out XmlComment? xmlComment)
+        /// <summary>
+        /// Generates a documentation ID string for a type.
+        /// This method handles nested types (replacing '+' with '.'),
+        /// generic types, arrays, pointers, by-ref types, and generic parameters.
+        /// The <paramref name="includeGenericArguments"/> flag controls whether
+        /// constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
+        /// controls whether the generic arity marker (e.g. "`1") is appended.
+        /// </summary>
+        private static string GetTypeDocId(Type type, bool includeGenericArguments, bool omitGenericArity)
         {
-            if (methodInfo is null)
+            if (type.IsGenericParameter)
+            {
+                // Use `` for method-level generic parameters and ` for type-level.
+                if (type.DeclaringMethod != null)
+                {
+                    return "``" + type.GenericParameterPosition;
+                }
+                else if (type.DeclaringType != null)
+                {
+                    return "`" + type.GenericParameterPosition;
+                }
+                else
+                {
+                    return type.Name;
+                }
+            }
+
+            if (type.IsArray && type.GetElementType() is { } elementType)
             {
-                return Cache.TryGetValue(new MemberKey(type, MemberType.Property, null, null, null), out xmlComment);
+                int rank = type.GetArrayRank();
+                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
+                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
             }
 
-            return Cache.TryGetValue(MemberKey.FromMethodInfo(methodInfo), out xmlComment);
-        }
+            if (type.IsGenericType)
+            {
+                // Get the generic type definition.
+                Type genericDef = type.GetGenericTypeDefinition();
+                string defName = genericDef.FullName != null
+                    ? genericDef.FullName.Replace('+', '.')
+                    : genericDef.Name;
+                // Remove any existing generic arity marker.
+                int backtickIndex = defName.IndexOf('`');
+                if (backtickIndex >= 0)
+                {
+                    defName = defName.Substring(0, backtickIndex);
+                }
+                // Append the generic arity unless omitted.
+                if (!omitGenericArity)
+                {
+                    int arity = genericDef.GetGenericArguments().Length;
+                    defName += "`" + arity;
+                }
+                // If requested, append the constructed generic arguments.
+                if (includeGenericArguments && !type.IsGenericTypeDefinition)
+                {
+                    Type[] typeArgs = type.GetGenericArguments();
+                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
+                    return defName + "{" + args + "}";
+                }
+                else
+                {
+                    return defName;
+                }
+            }
 
-        internal static bool TryGetXmlComment(Type? type, string? memberName, [NotNullWhen(true)] out XmlComment? xmlComment)
-        {
-            return Cache.TryGetValue(new MemberKey(type, memberName is null ? MemberType.Type : MemberType.Property, memberName, null, null), out xmlComment);
+            // For non-generic types, use FullName (if available) and replace nested type separators.
+            return (type.FullName ?? type.Name).Replace('+', '.');
         }
     }
 
@@ -249,7 +300,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
             {
                 return Task.CompletedTask;
             }
-            if (XmlCommentCache.TryGetXmlComment(methodInfo.DeclaringType, methodInfo, out var methodComment))
+            if (XmlCommentCache.Cache.TryGetValue(methodInfo.CreateDocumentationId(), out var methodComment))
             {
                 if (methodComment.Summary is { } summary)
                 {
@@ -322,7 +373,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
         {
             if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
             {
-                if (XmlCommentCache.TryGetXmlComment(propertyInfo.DeclaringType, propertyInfo.Name, out var propertyComment))
+                if (XmlCommentCache.Cache.TryGetValue(propertyInfo.CreateDocumentationId(), out var propertyComment))
                 {
                     schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
                     if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
@@ -331,7 +382,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
                     }
                 }
             }
-            if (XmlCommentCache.TryGetXmlComment(context.JsonTypeInfo.Type, (string?)null, out var typeComment))
+            if (XmlCommentCache.Cache.TryGetValue(context.JsonTypeInfo.Type.CreateDocumentationId(), out var typeComment))
             {
                 schema.Description = typeComment.Summary;
                 if (typeComment.Examples?.FirstOrDefault() is { } jsonString)

From 97f3cb763ff437372dfb0e969d2112a20f2cd492 Mon Sep 17 00:00:00 2001
From: Safia Abdalla <safia@safia.rocks>
Date: Thu, 27 Mar 2025 14:30:32 -0700
Subject: [PATCH 4/7] Remove Debugger and LINQ, format ints

---
 .../gen/XmlCommentGenerator.Emitter.cs        | 95 ++++++++++++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 95 ++++++++++++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 95 ++++++++++++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 95 ++++++++++++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 95 ++++++++++++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 95 ++++++++++++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 95 ++++++++++++++-----
 7 files changed, 497 insertions(+), 168 deletions(-)

diff --git a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
index ec630c1f7e98..74684d1808d7 100644
--- a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
+++ b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
@@ -47,6 +47,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System;
     using System.Collections.Generic;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.Linq;
     using System.Reflection;
     using System.Text;
@@ -137,7 +138,15 @@ public static string CreateDocumentationId(this PropertyInfo property)
             if (indexParams.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                for (int i = 0; i < indexParams.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    sb.Append(GetTypeDocId(indexParams[i].ParameterType, includeGenericArguments: true, omitGenericArity: false));
+                }
                 sb.Append(')');
             }
 
@@ -152,7 +161,6 @@ public static string CreateDocumentationId(this PropertyInfo property)
         /// </summary>
         public static string CreateDocumentationId(this MethodInfo method)
         {
-            System.Diagnostics.Debugger.Break();
             if (method == null)
             {
                 throw new ArgumentNullException(nameof(method));
@@ -180,7 +188,7 @@ public static string CreateDocumentationId(this MethodInfo method)
                 if (method.IsGenericMethod)
                 {
                     sb.Append("``");
-                    sb.Append(method.GetGenericArguments().Length);
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", method.GetGenericArguments().Length);
                 }
             }
 
@@ -189,7 +197,16 @@ public static string CreateDocumentationId(this MethodInfo method)
             if (parameters.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    // Omit the generic arity for the parameter type.
+                    sb.Append(GetTypeDocId(parameters[i].ParameterType, includeGenericArguments: true, omitGenericArity: true));
+                }
                 sb.Append(')');
             }
 
@@ -234,40 +251,70 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
             if (type.IsArray && type.GetElementType() is { } elementType)
             {
                 int rank = type.GetArrayRank();
-                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
-                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
+                if (rank > 1)
+                {
+                    sb.Append('[');
+                    sb.Append(',', rank - 1);
+                    sb.Append(']');
+                }
+                else
+                {
+                    sb.Append("[]");
+                }
             }
 
             if (type.IsGenericType)
             {
-                // Get the generic type definition.
                 Type genericDef = type.GetGenericTypeDefinition();
-                string defName = genericDef.FullName != null
-                    ? genericDef.FullName.Replace('+', '.')
-                    : genericDef.Name;
-                // Remove any existing generic arity marker.
-                int backtickIndex = defName.IndexOf('`');
-                if (backtickIndex >= 0)
+                string fullName = genericDef.FullName ?? genericDef.Name;
+
+                var sb = new StringBuilder(fullName.Length);
+
+                // Replace '+' with '.' for nested types
+                for (var i = 0; i < fullName.Length; i++)
                 {
-                    defName = defName.Substring(0, backtickIndex);
+                    char c = fullName[i];
+                    if (c == '+')
+                    {
+                        sb.Append('.');
+                    }
+                    else if (c == '`')
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
                 }
-                // Append the generic arity unless omitted.
+
                 if (!omitGenericArity)
                 {
                     int arity = genericDef.GetGenericArguments().Length;
-                    defName += "`" + arity;
+                    sb.Append('`');
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", arity);
                 }
-                // If requested, append the constructed generic arguments.
+
                 if (includeGenericArguments && !type.IsGenericTypeDefinition)
                 {
-                    Type[] typeArgs = type.GetGenericArguments();
-                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
-                    return defName + "{" + args + "}";
-                }
-                else
-                {
-                    return defName;
+                    var typeArgs = type.GetGenericArguments();
+                    sb.Append('{');
+
+                    for (int i = 0; i < typeArgs.Length; i++)
+                    {
+                        if (i > 0)
+                        {
+                            sb.Append(',');
+                        }
+
+                        sb.Append(GetTypeDocId(typeArgs[i], includeGenericArguments, omitGenericArity));
+                    }
+
+                    sb.Append('}');
                 }
+
+                return sb.ToString();
             }
 
             // For non-generic types, use FullName (if available) and replace nested type separators.
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
index 4c8e15e45d4e..838d61b6b546 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
@@ -29,6 +29,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System;
     using System.Collections.Generic;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.Linq;
     using System.Reflection;
     using System.Text;
@@ -119,7 +120,15 @@ public static string CreateDocumentationId(this PropertyInfo property)
             if (indexParams.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                for (int i = 0; i < indexParams.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    sb.Append(GetTypeDocId(indexParams[i].ParameterType, includeGenericArguments: true, omitGenericArity: false));
+                }
                 sb.Append(')');
             }
 
@@ -134,7 +143,6 @@ public static string CreateDocumentationId(this PropertyInfo property)
         /// </summary>
         public static string CreateDocumentationId(this MethodInfo method)
         {
-            System.Diagnostics.Debugger.Break();
             if (method == null)
             {
                 throw new ArgumentNullException(nameof(method));
@@ -162,7 +170,7 @@ public static string CreateDocumentationId(this MethodInfo method)
                 if (method.IsGenericMethod)
                 {
                     sb.Append("``");
-                    sb.Append(method.GetGenericArguments().Length);
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", method.GetGenericArguments().Length);
                 }
             }
 
@@ -171,7 +179,16 @@ public static string CreateDocumentationId(this MethodInfo method)
             if (parameters.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    // Omit the generic arity for the parameter type.
+                    sb.Append(GetTypeDocId(parameters[i].ParameterType, includeGenericArguments: true, omitGenericArity: true));
+                }
                 sb.Append(')');
             }
 
@@ -216,40 +233,70 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
             if (type.IsArray && type.GetElementType() is { } elementType)
             {
                 int rank = type.GetArrayRank();
-                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
-                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
+                if (rank > 1)
+                {
+                    sb.Append('[');
+                    sb.Append(',', rank - 1);
+                    sb.Append(']');
+                }
+                else
+                {
+                    sb.Append("[]");
+                }
             }
 
             if (type.IsGenericType)
             {
-                // Get the generic type definition.
                 Type genericDef = type.GetGenericTypeDefinition();
-                string defName = genericDef.FullName != null
-                    ? genericDef.FullName.Replace('+', '.')
-                    : genericDef.Name;
-                // Remove any existing generic arity marker.
-                int backtickIndex = defName.IndexOf('`');
-                if (backtickIndex >= 0)
+                string fullName = genericDef.FullName ?? genericDef.Name;
+
+                var sb = new StringBuilder(fullName.Length);
+
+                // Replace '+' with '.' for nested types
+                for (var i = 0; i < fullName.Length; i++)
                 {
-                    defName = defName.Substring(0, backtickIndex);
+                    char c = fullName[i];
+                    if (c == '+')
+                    {
+                        sb.Append('.');
+                    }
+                    else if (c == '`')
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
                 }
-                // Append the generic arity unless omitted.
+
                 if (!omitGenericArity)
                 {
                     int arity = genericDef.GetGenericArguments().Length;
-                    defName += "`" + arity;
+                    sb.Append('`');
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", arity);
                 }
-                // If requested, append the constructed generic arguments.
+
                 if (includeGenericArguments && !type.IsGenericTypeDefinition)
                 {
-                    Type[] typeArgs = type.GetGenericArguments();
-                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
-                    return defName + "{" + args + "}";
-                }
-                else
-                {
-                    return defName;
+                    var typeArgs = type.GetGenericArguments();
+                    sb.Append('{');
+
+                    for (int i = 0; i < typeArgs.Length; i++)
+                    {
+                        if (i > 0)
+                        {
+                            sb.Append(',');
+                        }
+
+                        sb.Append(GetTypeDocId(typeArgs[i], includeGenericArguments, omitGenericArity));
+                    }
+
+                    sb.Append('}');
                 }
+
+                return sb.ToString();
             }
 
             // For non-generic types, use FullName (if available) and replace nested type separators.
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
index 67f9b58ae525..bbfbcc8ef070 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
@@ -29,6 +29,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System;
     using System.Collections.Generic;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.Linq;
     using System.Reflection;
     using System.Text;
@@ -148,7 +149,15 @@ public static string CreateDocumentationId(this PropertyInfo property)
             if (indexParams.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                for (int i = 0; i < indexParams.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    sb.Append(GetTypeDocId(indexParams[i].ParameterType, includeGenericArguments: true, omitGenericArity: false));
+                }
                 sb.Append(')');
             }
 
@@ -163,7 +172,6 @@ public static string CreateDocumentationId(this PropertyInfo property)
         /// </summary>
         public static string CreateDocumentationId(this MethodInfo method)
         {
-            System.Diagnostics.Debugger.Break();
             if (method == null)
             {
                 throw new ArgumentNullException(nameof(method));
@@ -191,7 +199,7 @@ public static string CreateDocumentationId(this MethodInfo method)
                 if (method.IsGenericMethod)
                 {
                     sb.Append("``");
-                    sb.Append(method.GetGenericArguments().Length);
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", method.GetGenericArguments().Length);
                 }
             }
 
@@ -200,7 +208,16 @@ public static string CreateDocumentationId(this MethodInfo method)
             if (parameters.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    // Omit the generic arity for the parameter type.
+                    sb.Append(GetTypeDocId(parameters[i].ParameterType, includeGenericArguments: true, omitGenericArity: true));
+                }
                 sb.Append(')');
             }
 
@@ -245,40 +262,70 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
             if (type.IsArray && type.GetElementType() is { } elementType)
             {
                 int rank = type.GetArrayRank();
-                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
-                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
+                if (rank > 1)
+                {
+                    sb.Append('[');
+                    sb.Append(',', rank - 1);
+                    sb.Append(']');
+                }
+                else
+                {
+                    sb.Append("[]");
+                }
             }
 
             if (type.IsGenericType)
             {
-                // Get the generic type definition.
                 Type genericDef = type.GetGenericTypeDefinition();
-                string defName = genericDef.FullName != null
-                    ? genericDef.FullName.Replace('+', '.')
-                    : genericDef.Name;
-                // Remove any existing generic arity marker.
-                int backtickIndex = defName.IndexOf('`');
-                if (backtickIndex >= 0)
+                string fullName = genericDef.FullName ?? genericDef.Name;
+
+                var sb = new StringBuilder(fullName.Length);
+
+                // Replace '+' with '.' for nested types
+                for (var i = 0; i < fullName.Length; i++)
                 {
-                    defName = defName.Substring(0, backtickIndex);
+                    char c = fullName[i];
+                    if (c == '+')
+                    {
+                        sb.Append('.');
+                    }
+                    else if (c == '`')
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
                 }
-                // Append the generic arity unless omitted.
+
                 if (!omitGenericArity)
                 {
                     int arity = genericDef.GetGenericArguments().Length;
-                    defName += "`" + arity;
+                    sb.Append('`');
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", arity);
                 }
-                // If requested, append the constructed generic arguments.
+
                 if (includeGenericArguments && !type.IsGenericTypeDefinition)
                 {
-                    Type[] typeArgs = type.GetGenericArguments();
-                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
-                    return defName + "{" + args + "}";
-                }
-                else
-                {
-                    return defName;
+                    var typeArgs = type.GetGenericArguments();
+                    sb.Append('{');
+
+                    for (int i = 0; i < typeArgs.Length; i++)
+                    {
+                        if (i > 0)
+                        {
+                            sb.Append(',');
+                        }
+
+                        sb.Append(GetTypeDocId(typeArgs[i], includeGenericArguments, omitGenericArity));
+                    }
+
+                    sb.Append('}');
                 }
+
+                return sb.ToString();
             }
 
             // For non-generic types, use FullName (if available) and replace nested type separators.
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 2125789f1369..57ba2072544e 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
@@ -29,6 +29,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System;
     using System.Collections.Generic;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.Linq;
     using System.Reflection;
     using System.Text;
@@ -238,7 +239,15 @@ public static string CreateDocumentationId(this PropertyInfo property)
             if (indexParams.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                for (int i = 0; i < indexParams.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    sb.Append(GetTypeDocId(indexParams[i].ParameterType, includeGenericArguments: true, omitGenericArity: false));
+                }
                 sb.Append(')');
             }
 
@@ -253,7 +262,6 @@ public static string CreateDocumentationId(this PropertyInfo property)
         /// </summary>
         public static string CreateDocumentationId(this MethodInfo method)
         {
-            System.Diagnostics.Debugger.Break();
             if (method == null)
             {
                 throw new ArgumentNullException(nameof(method));
@@ -281,7 +289,7 @@ public static string CreateDocumentationId(this MethodInfo method)
                 if (method.IsGenericMethod)
                 {
                     sb.Append("``");
-                    sb.Append(method.GetGenericArguments().Length);
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", method.GetGenericArguments().Length);
                 }
             }
 
@@ -290,7 +298,16 @@ public static string CreateDocumentationId(this MethodInfo method)
             if (parameters.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    // Omit the generic arity for the parameter type.
+                    sb.Append(GetTypeDocId(parameters[i].ParameterType, includeGenericArguments: true, omitGenericArity: true));
+                }
                 sb.Append(')');
             }
 
@@ -335,40 +352,70 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
             if (type.IsArray && type.GetElementType() is { } elementType)
             {
                 int rank = type.GetArrayRank();
-                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
-                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
+                if (rank > 1)
+                {
+                    sb.Append('[');
+                    sb.Append(',', rank - 1);
+                    sb.Append(']');
+                }
+                else
+                {
+                    sb.Append("[]");
+                }
             }
 
             if (type.IsGenericType)
             {
-                // Get the generic type definition.
                 Type genericDef = type.GetGenericTypeDefinition();
-                string defName = genericDef.FullName != null
-                    ? genericDef.FullName.Replace('+', '.')
-                    : genericDef.Name;
-                // Remove any existing generic arity marker.
-                int backtickIndex = defName.IndexOf('`');
-                if (backtickIndex >= 0)
+                string fullName = genericDef.FullName ?? genericDef.Name;
+
+                var sb = new StringBuilder(fullName.Length);
+
+                // Replace '+' with '.' for nested types
+                for (var i = 0; i < fullName.Length; i++)
                 {
-                    defName = defName.Substring(0, backtickIndex);
+                    char c = fullName[i];
+                    if (c == '+')
+                    {
+                        sb.Append('.');
+                    }
+                    else if (c == '`')
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
                 }
-                // Append the generic arity unless omitted.
+
                 if (!omitGenericArity)
                 {
                     int arity = genericDef.GetGenericArguments().Length;
-                    defName += "`" + arity;
+                    sb.Append('`');
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", arity);
                 }
-                // If requested, append the constructed generic arguments.
+
                 if (includeGenericArguments && !type.IsGenericTypeDefinition)
                 {
-                    Type[] typeArgs = type.GetGenericArguments();
-                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
-                    return defName + "{" + args + "}";
-                }
-                else
-                {
-                    return defName;
+                    var typeArgs = type.GetGenericArguments();
+                    sb.Append('{');
+
+                    for (int i = 0; i < typeArgs.Length; i++)
+                    {
+                        if (i > 0)
+                        {
+                            sb.Append(',');
+                        }
+
+                        sb.Append(GetTypeDocId(typeArgs[i], includeGenericArguments, omitGenericArity));
+                    }
+
+                    sb.Append('}');
                 }
+
+                return sb.ToString();
             }
 
             // For non-generic types, use FullName (if available) and replace nested type separators.
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
index 577e6c7c502b..dc6f6c8f4740 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
@@ -29,6 +29,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System;
     using System.Collections.Generic;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.Linq;
     using System.Reflection;
     using System.Text;
@@ -123,7 +124,15 @@ public static string CreateDocumentationId(this PropertyInfo property)
             if (indexParams.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                for (int i = 0; i < indexParams.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    sb.Append(GetTypeDocId(indexParams[i].ParameterType, includeGenericArguments: true, omitGenericArity: false));
+                }
                 sb.Append(')');
             }
 
@@ -138,7 +147,6 @@ public static string CreateDocumentationId(this PropertyInfo property)
         /// </summary>
         public static string CreateDocumentationId(this MethodInfo method)
         {
-            System.Diagnostics.Debugger.Break();
             if (method == null)
             {
                 throw new ArgumentNullException(nameof(method));
@@ -166,7 +174,7 @@ public static string CreateDocumentationId(this MethodInfo method)
                 if (method.IsGenericMethod)
                 {
                     sb.Append("``");
-                    sb.Append(method.GetGenericArguments().Length);
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", method.GetGenericArguments().Length);
                 }
             }
 
@@ -175,7 +183,16 @@ public static string CreateDocumentationId(this MethodInfo method)
             if (parameters.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    // Omit the generic arity for the parameter type.
+                    sb.Append(GetTypeDocId(parameters[i].ParameterType, includeGenericArguments: true, omitGenericArity: true));
+                }
                 sb.Append(')');
             }
 
@@ -220,40 +237,70 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
             if (type.IsArray && type.GetElementType() is { } elementType)
             {
                 int rank = type.GetArrayRank();
-                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
-                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
+                if (rank > 1)
+                {
+                    sb.Append('[');
+                    sb.Append(',', rank - 1);
+                    sb.Append(']');
+                }
+                else
+                {
+                    sb.Append("[]");
+                }
             }
 
             if (type.IsGenericType)
             {
-                // Get the generic type definition.
                 Type genericDef = type.GetGenericTypeDefinition();
-                string defName = genericDef.FullName != null
-                    ? genericDef.FullName.Replace('+', '.')
-                    : genericDef.Name;
-                // Remove any existing generic arity marker.
-                int backtickIndex = defName.IndexOf('`');
-                if (backtickIndex >= 0)
+                string fullName = genericDef.FullName ?? genericDef.Name;
+
+                var sb = new StringBuilder(fullName.Length);
+
+                // Replace '+' with '.' for nested types
+                for (var i = 0; i < fullName.Length; i++)
                 {
-                    defName = defName.Substring(0, backtickIndex);
+                    char c = fullName[i];
+                    if (c == '+')
+                    {
+                        sb.Append('.');
+                    }
+                    else if (c == '`')
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
                 }
-                // Append the generic arity unless omitted.
+
                 if (!omitGenericArity)
                 {
                     int arity = genericDef.GetGenericArguments().Length;
-                    defName += "`" + arity;
+                    sb.Append('`');
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", arity);
                 }
-                // If requested, append the constructed generic arguments.
+
                 if (includeGenericArguments && !type.IsGenericTypeDefinition)
                 {
-                    Type[] typeArgs = type.GetGenericArguments();
-                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
-                    return defName + "{" + args + "}";
-                }
-                else
-                {
-                    return defName;
+                    var typeArgs = type.GetGenericArguments();
+                    sb.Append('{');
+
+                    for (int i = 0; i < typeArgs.Length; i++)
+                    {
+                        if (i > 0)
+                        {
+                            sb.Append(',');
+                        }
+
+                        sb.Append(GetTypeDocId(typeArgs[i], includeGenericArguments, omitGenericArity));
+                    }
+
+                    sb.Append('}');
                 }
+
+                return sb.ToString();
             }
 
             // For non-generic types, use FullName (if available) and replace nested type separators.
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
index 3f6903f3304a..d8b5435edae3 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
@@ -29,6 +29,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System;
     using System.Collections.Generic;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.Linq;
     using System.Reflection;
     using System.Text;
@@ -140,7 +141,15 @@ public static string CreateDocumentationId(this PropertyInfo property)
             if (indexParams.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                for (int i = 0; i < indexParams.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    sb.Append(GetTypeDocId(indexParams[i].ParameterType, includeGenericArguments: true, omitGenericArity: false));
+                }
                 sb.Append(')');
             }
 
@@ -155,7 +164,6 @@ public static string CreateDocumentationId(this PropertyInfo property)
         /// </summary>
         public static string CreateDocumentationId(this MethodInfo method)
         {
-            System.Diagnostics.Debugger.Break();
             if (method == null)
             {
                 throw new ArgumentNullException(nameof(method));
@@ -183,7 +191,7 @@ public static string CreateDocumentationId(this MethodInfo method)
                 if (method.IsGenericMethod)
                 {
                     sb.Append("``");
-                    sb.Append(method.GetGenericArguments().Length);
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", method.GetGenericArguments().Length);
                 }
             }
 
@@ -192,7 +200,16 @@ public static string CreateDocumentationId(this MethodInfo method)
             if (parameters.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    // Omit the generic arity for the parameter type.
+                    sb.Append(GetTypeDocId(parameters[i].ParameterType, includeGenericArguments: true, omitGenericArity: true));
+                }
                 sb.Append(')');
             }
 
@@ -237,40 +254,70 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
             if (type.IsArray && type.GetElementType() is { } elementType)
             {
                 int rank = type.GetArrayRank();
-                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
-                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
+                if (rank > 1)
+                {
+                    sb.Append('[');
+                    sb.Append(',', rank - 1);
+                    sb.Append(']');
+                }
+                else
+                {
+                    sb.Append("[]");
+                }
             }
 
             if (type.IsGenericType)
             {
-                // Get the generic type definition.
                 Type genericDef = type.GetGenericTypeDefinition();
-                string defName = genericDef.FullName != null
-                    ? genericDef.FullName.Replace('+', '.')
-                    : genericDef.Name;
-                // Remove any existing generic arity marker.
-                int backtickIndex = defName.IndexOf('`');
-                if (backtickIndex >= 0)
+                string fullName = genericDef.FullName ?? genericDef.Name;
+
+                var sb = new StringBuilder(fullName.Length);
+
+                // Replace '+' with '.' for nested types
+                for (var i = 0; i < fullName.Length; i++)
                 {
-                    defName = defName.Substring(0, backtickIndex);
+                    char c = fullName[i];
+                    if (c == '+')
+                    {
+                        sb.Append('.');
+                    }
+                    else if (c == '`')
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
                 }
-                // Append the generic arity unless omitted.
+
                 if (!omitGenericArity)
                 {
                     int arity = genericDef.GetGenericArguments().Length;
-                    defName += "`" + arity;
+                    sb.Append('`');
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", arity);
                 }
-                // If requested, append the constructed generic arguments.
+
                 if (includeGenericArguments && !type.IsGenericTypeDefinition)
                 {
-                    Type[] typeArgs = type.GetGenericArguments();
-                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
-                    return defName + "{" + args + "}";
-                }
-                else
-                {
-                    return defName;
+                    var typeArgs = type.GetGenericArguments();
+                    sb.Append('{');
+
+                    for (int i = 0; i < typeArgs.Length; i++)
+                    {
+                        if (i > 0)
+                        {
+                            sb.Append(',');
+                        }
+
+                        sb.Append(GetTypeDocId(typeArgs[i], includeGenericArguments, omitGenericArity));
+                    }
+
+                    sb.Append('}');
                 }
+
+                return sb.ToString();
             }
 
             // For non-generic types, use FullName (if available) and replace nested type separators.
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
index ef2565c5f3e9..c86ff163e6c2 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
@@ -29,6 +29,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
     using System;
     using System.Collections.Generic;
     using System.Diagnostics.CodeAnalysis;
+    using System.Globalization;
     using System.Linq;
     using System.Reflection;
     using System.Text;
@@ -149,7 +150,15 @@ public static string CreateDocumentationId(this PropertyInfo property)
             if (indexParams.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", indexParams.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: false))));
+                for (int i = 0; i < indexParams.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    sb.Append(GetTypeDocId(indexParams[i].ParameterType, includeGenericArguments: true, omitGenericArity: false));
+                }
                 sb.Append(')');
             }
 
@@ -164,7 +173,6 @@ public static string CreateDocumentationId(this PropertyInfo property)
         /// </summary>
         public static string CreateDocumentationId(this MethodInfo method)
         {
-            System.Diagnostics.Debugger.Break();
             if (method == null)
             {
                 throw new ArgumentNullException(nameof(method));
@@ -192,7 +200,7 @@ public static string CreateDocumentationId(this MethodInfo method)
                 if (method.IsGenericMethod)
                 {
                     sb.Append("``");
-                    sb.Append(method.GetGenericArguments().Length);
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", method.GetGenericArguments().Length);
                 }
             }
 
@@ -201,7 +209,16 @@ public static string CreateDocumentationId(this MethodInfo method)
             if (parameters.Length > 0)
             {
                 sb.Append('(');
-                sb.Append(string.Join(",", parameters.Select(p => GetTypeDocId(p.ParameterType, includeGenericArguments: true, omitGenericArity: true))));
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(',');
+                    }
+
+                    // Omit the generic arity for the parameter type.
+                    sb.Append(GetTypeDocId(parameters[i].ParameterType, includeGenericArguments: true, omitGenericArity: true));
+                }
                 sb.Append(')');
             }
 
@@ -246,40 +263,70 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
             if (type.IsArray && type.GetElementType() is { } elementType)
             {
                 int rank = type.GetArrayRank();
-                string elementTypeId = GetTypeDocId(elementType, includeGenericArguments, omitGenericArity);
-                return rank == 1 ? elementTypeId + "[]" : elementTypeId + "[" + new string(',', rank - 1) + "]";
+                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
+                if (rank > 1)
+                {
+                    sb.Append('[');
+                    sb.Append(',', rank - 1);
+                    sb.Append(']');
+                }
+                else
+                {
+                    sb.Append("[]");
+                }
             }
 
             if (type.IsGenericType)
             {
-                // Get the generic type definition.
                 Type genericDef = type.GetGenericTypeDefinition();
-                string defName = genericDef.FullName != null
-                    ? genericDef.FullName.Replace('+', '.')
-                    : genericDef.Name;
-                // Remove any existing generic arity marker.
-                int backtickIndex = defName.IndexOf('`');
-                if (backtickIndex >= 0)
+                string fullName = genericDef.FullName ?? genericDef.Name;
+
+                var sb = new StringBuilder(fullName.Length);
+
+                // Replace '+' with '.' for nested types
+                for (var i = 0; i < fullName.Length; i++)
                 {
-                    defName = defName.Substring(0, backtickIndex);
+                    char c = fullName[i];
+                    if (c == '+')
+                    {
+                        sb.Append('.');
+                    }
+                    else if (c == '`')
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        sb.Append(c);
+                    }
                 }
-                // Append the generic arity unless omitted.
+
                 if (!omitGenericArity)
                 {
                     int arity = genericDef.GetGenericArguments().Length;
-                    defName += "`" + arity;
+                    sb.Append('`');
+                    sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", arity);
                 }
-                // If requested, append the constructed generic arguments.
+
                 if (includeGenericArguments && !type.IsGenericTypeDefinition)
                 {
-                    Type[] typeArgs = type.GetGenericArguments();
-                    string args = string.Join(",", typeArgs.Select(t => GetTypeDocId(t, includeGenericArguments, omitGenericArity)));
-                    return defName + "{" + args + "}";
-                }
-                else
-                {
-                    return defName;
+                    var typeArgs = type.GetGenericArguments();
+                    sb.Append('{');
+
+                    for (int i = 0; i < typeArgs.Length; i++)
+                    {
+                        if (i > 0)
+                        {
+                            sb.Append(',');
+                        }
+
+                        sb.Append(GetTypeDocId(typeArgs[i], includeGenericArguments, omitGenericArity));
+                    }
+
+                    sb.Append('}');
                 }
+
+                return sb.ToString();
             }
 
             // For non-generic types, use FullName (if available) and replace nested type separators.

From ba103a70be8b7bc5b053d79db51ab5061697d1c7 Mon Sep 17 00:00:00 2001
From: Safia Abdalla <safia@safia.rocks>
Date: Fri, 28 Mar 2025 14:02:02 -0700
Subject: [PATCH 5/7] Add array tests, remove unneeded formatting, rename
 _cache

---
 .../gen/XmlCommentGenerator.Emitter.cs        | 22 +-----
 .../OperationTests.MinimalApis.cs             | 13 ++++
 ...ApiXmlCommentSupport.generated.verified.cs |  4 +-
 ...ApiXmlCommentSupport.generated.verified.cs | 66 ++++++++---------
 ...ApiXmlCommentSupport.generated.verified.cs | 74 +++++++++----------
 ...ApiXmlCommentSupport.generated.verified.cs | 12 +--
 ...ApiXmlCommentSupport.generated.verified.cs | 41 +++++-----
 ...ApiXmlCommentSupport.generated.verified.cs | 68 ++++++++---------
 8 files changed, 149 insertions(+), 151 deletions(-)

diff --git a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
index 74684d1808d7..a730bcc8722e 100644
--- a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
+++ b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
@@ -88,10 +88,10 @@ file static class XmlCommentCache
 
         private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<string, XmlComment>();
+            var cache = new Dictionary<string, XmlComment>();
 {{commentsFromXmlFile}}
 {{commentsFromCompilation}}
-            return _cache;
+            return cache;
         }
     }
 
@@ -248,22 +248,6 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
                 }
             }
 
-            if (type.IsArray && type.GetElementType() is { } elementType)
-            {
-                int rank = type.GetArrayRank();
-                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
-                if (rank > 1)
-                {
-                    sb.Append('[');
-                    sb.Append(',', rank - 1);
-                    sb.Append(']');
-                }
-                else
-                {
-                    sb.Append("[]");
-                }
-            }
-
             if (type.IsGenericType)
             {
                 Type genericDef = type.GetGenericTypeDefinition();
@@ -539,7 +523,7 @@ internal static string EmitCommentsCache(IEnumerable<(string MemberKey, XmlComme
         {
             if (comment is not null)
             {
-                codeWriter.WriteLine($"_cache.Add({FormatStringForCode(memberKey)}, {EmitSourceGeneratedXmlComment(comment)});");
+                codeWriter.WriteLine($"cache.Add({FormatStringForCode(memberKey)}, {EmitSourceGeneratedXmlComment(comment)});");
             }
         }
         return writer.ToString();
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
index 8a93fd22aeb5..73d46fa6f2f4 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
@@ -43,6 +43,7 @@ public async Task SupportsXmlCommentsOnOperationsFromMinimalApis()
 app.MapGet("/14", RouteHandlerExtensionMethods.Get14);
 app.MapGet("/15", RouteHandlerExtensionMethods.Get15);
 app.MapPost("/16", RouteHandlerExtensionMethods.Post16);
+app.MapGet("/17", RouteHandlerExtensionMethods.Get17);
 
 app.Run();
 
@@ -193,6 +194,15 @@ public static void Post16(Example example)
     {
         return;
     }
+
+    /// <summary>
+    /// A summary of Get17.
+    /// </summary>
+    public static int[][] Get17(int[] args)
+    {
+        return [[1, 2, 3], [4, 5, 6], [7, 8, 9], args];
+
+    }
 }
 
 public class User
@@ -281,6 +291,9 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document =>
 
             var path16 = document.Paths["/16"].Operations[OperationType.Post];
             Assert.Equal("A summary of Post16.", path16.Summary);
+
+            var path17 = document.Paths["/17"].Operations[OperationType.Get];
+            Assert.Equal("A summary of Get17.", path17.Summary);
         });
     }
 }
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
index 838d61b6b546..fc66ff8c2b8c 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
@@ -70,10 +70,10 @@ file static class XmlCommentCache
 
         private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<string, XmlComment>();
+            var cache = new Dictionary<string, XmlComment>();
 
 
-            return _cache;
+            return cache;
         }
     }
 
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
index bbfbcc8ef070..9e4f28460a9e 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
@@ -70,39 +70,39 @@ file static class XmlCommentCache
 
         private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<string, XmlComment>();
-            _cache.Add(@"T:ClassLibrary.Todo", new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:ClassLibrary.Project", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:ClassLibrary.Project.#ctor(System.String,System.String)", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:ClassLibrary.ProjectBoard.BoardItem", new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:ClassLibrary.ProjectBoard.BoardItem.Name", new XmlComment(@"The identifier of the board item. Defaults to ""name"".", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:ClassLibrary.ProjectRecord", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
-            _cache.Add(@"M:ClassLibrary.ProjectRecord.#ctor(System.String,System.String)", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
-            _cache.Add(@"P:ClassLibrary.ProjectRecord.Name", new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:ClassLibrary.ProjectRecord.Description", new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:ClassLibrary.TodoWithDescription.Id", new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:ClassLibrary.TodoWithDescription.Name", new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
-            _cache.Add(@"P:ClassLibrary.TodoWithDescription.Description", new XmlComment(@"A description of the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.BooleanType", new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.IntegerType", new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.LongType", new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DoubleType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.FloatType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DateTimeType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DateOnlyType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.StringType", new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.GuidType", new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.TimeOnlyType", new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.TimeSpanType", new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.ByteType", new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.DecimalType", new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
-            _cache.Add(@"P:ClassLibrary.TypeWithExamples.UriType", new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
-            _cache.Add(@"P:ClassLibrary.Holder`1.Value", new XmlComment(@"The value to hold.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:ClassLibrary.Endpoints.ExternalMethod(System.String)", new XmlComment(@"An external method.", null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the tester. Defaults to ""Tester"".", null, false)], null));
-            _cache.Add(@"M:ClassLibrary.Endpoints.CreateHolder``1(``0)", new XmlComment(@"Creates a holder for the specified value.", null, null, @"A holder for the specified value.", null, false, [@"{ value: 42 }"], [new XmlParameterComment(@"value", @"The value to hold.", null, false)], null));
-
-
-            return _cache;
+            var cache = new Dictionary<string, XmlComment>();
+            cache.Add(@"T:ClassLibrary.Todo", new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:ClassLibrary.Project", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:ClassLibrary.Project.#ctor(System.String,System.String)", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:ClassLibrary.ProjectBoard.BoardItem", new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:ClassLibrary.ProjectBoard.BoardItem.Name", new XmlComment(@"The identifier of the board item. Defaults to ""name"".", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:ClassLibrary.ProjectRecord", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
+            cache.Add(@"M:ClassLibrary.ProjectRecord.#ctor(System.String,System.String)", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
+            cache.Add(@"P:ClassLibrary.ProjectRecord.Name", new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:ClassLibrary.ProjectRecord.Description", new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:ClassLibrary.TodoWithDescription.Id", new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:ClassLibrary.TodoWithDescription.Name", new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
+            cache.Add(@"P:ClassLibrary.TodoWithDescription.Description", new XmlComment(@"A description of the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.BooleanType", new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.IntegerType", new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.LongType", new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.DoubleType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.FloatType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.DateTimeType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.DateOnlyType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.StringType", new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.GuidType", new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.TimeOnlyType", new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.TimeSpanType", new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.ByteType", new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.DecimalType", new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
+            cache.Add(@"P:ClassLibrary.TypeWithExamples.UriType", new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
+            cache.Add(@"P:ClassLibrary.Holder`1.Value", new XmlComment(@"The value to hold.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:ClassLibrary.Endpoints.ExternalMethod(System.String)", new XmlComment(@"An external method.", null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the tester. Defaults to ""Tester"".", null, false)], null));
+            cache.Add(@"M:ClassLibrary.Endpoints.CreateHolder``1(``0)", new XmlComment(@"Creates a holder for the specified value.", null, null, @"A holder for the specified value.", null, false, [@"{ value: 42 }"], [new XmlParameterComment(@"value", @"The value to hold.", null, false)], null));
+
+
+            return cache;
         }
     }
 
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 57ba2072544e..bb54500a2f3b 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
@@ -70,9 +70,9 @@ file static class XmlCommentCache
 
         private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<string, XmlComment>();
+            var cache = new Dictionary<string, XmlComment>();
 
-            _cache.Add(@"T:ExampleClass", new XmlComment(@"Every class and member should have a one sentence
+            cache.Add(@"T:ExampleClass", new XmlComment(@"Every class and member should have a one sentence
 summary describing its purpose.", null, @"     You can expand on that one sentence summary to
      provide more information for readers. In this case,
      the `ExampleClass` provides different C#
@@ -116,83 +116,83 @@ would typically use the ""term"" element.
 
 Note: paragraphs are double spaced. Use the *br*
 tag for single spaced lines.", null, null, false, null, null, null));
-            _cache.Add(@"T:Person", new XmlComment(@"This is an example of a positional record.", null, @"There isn't a way to add XML comments for properties
+            cache.Add(@"T:Person", new XmlComment(@"This is an example of a positional record.", null, @"There isn't a way to add XML comments for properties
 created for positional records, yet. The language
 design team is still considering what tags should
 be supported, and where. Currently, you can use
 the ""param"" tag to describe the parameters to the
 primary constructor.", null, null, false, null, [new XmlParameterComment(@"FirstName", @"This tag will apply to the primary constructor parameter.", null, false), new XmlParameterComment(@"LastName", @"This tag will apply to the primary constructor parameter.", null, false)], null));
-            _cache.Add(@"T:MainClass", new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
+            cache.Add(@"T:MainClass", new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
 In this example, these comments also explain the
 general information about the derived class.", null, null, false, null, null, null));
-            _cache.Add(@"T:DerivedClass", new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
+            cache.Add(@"T:DerivedClass", new XmlComment(@"A summary about this class.", null, @"These remarks would explain more about this class.
 In this example, these comments also explain the
 general information about the derived class.", null, null, false, null, null, null));
-            _cache.Add(@"T:ITestInterface", new XmlComment(@"This interface would describe all the methods in
+            cache.Add(@"T:ITestInterface", new XmlComment(@"This interface would describe all the methods in
 its contract.", null, @"While elided for brevity, each method or property
 in this interface would contain docs that you want
 to duplicate in each implementing class.", null, null, false, null, null, null));
-            _cache.Add(@"T:ImplementingClass", new XmlComment(@"This interface would describe all the methods in
+            cache.Add(@"T:ImplementingClass", new XmlComment(@"This interface would describe all the methods in
 its contract.", null, @"While elided for brevity, each method or property
 in this interface would contain docs that you want
 to duplicate in each implementing class.", null, null, false, null, null, null));
-            _cache.Add(@"T:InheritOnlyReturns", new XmlComment(@"This class shows hows you can ""inherit"" the doc
+            cache.Add(@"T:InheritOnlyReturns", new XmlComment(@"This class shows hows you can ""inherit"" the doc
 comments from one method in another method.", null, @"You can inherit all comments, or only a specific tag,
 represented by an xpath expression.", null, null, false, null, null, null));
-            _cache.Add(@"T:InheritAllButRemarks", new XmlComment(@"This class shows an example of sharing comments across methods.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:GenericClass`1", new XmlComment(@"This is a generic class.", null, @"This example shows how to specify the GenericClass&lt;T&gt;
+            cache.Add(@"T:InheritAllButRemarks", new XmlComment(@"This class shows an example of sharing comments across methods.", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:GenericClass`1", new XmlComment(@"This is a generic class.", null, @"This example shows how to specify the GenericClass&lt;T&gt;
 type as a cref attribute.
 In generic classes and methods, you'll often want to reference the
 generic type, or the type parameter.", null, null, false, null, null, null));
-            _cache.Add(@"T:GenericParent", new XmlComment(@"This class validates the behavior for mapping
+            cache.Add(@"T:GenericParent", new XmlComment(@"This class validates the behavior for mapping
 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 <see langword=""string"" />
+            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 <see langword=""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
 for this instance.", false, null, null, null));
-            _cache.Add(@"P:Person.FirstName", new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:Person.LastName", new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:GenericParent.Id", new XmlComment(@"This property is a nullable value type.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:GenericParent.Name", new XmlComment(@"This property is a nullable reference type.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:GenericParent.TaskOfTupleProp", new XmlComment(@"This property is a generic type containing a tuple.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:GenericParent.TupleWithGenericProp", new XmlComment(@"This property is a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:GenericParent.TupleWithNestedGenericProp", new XmlComment(@"This property is a tuple with a nested generic type inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:ExampleClass.Add(System.Int32,System.Int32)~System.Int32", new XmlComment(@"Adds two integers and returns the result.", null, null, @"The sum of two integers.", null, false, [@"    ```int c = Math.Add(4, 5);
+            cache.Add(@"P:Person.FirstName", new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:Person.LastName", new XmlComment(@"This tag will apply to the primary constructor parameter.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:GenericParent.Id", new XmlComment(@"This property is a nullable value type.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:GenericParent.Name", new XmlComment(@"This property is a nullable reference type.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:GenericParent.TaskOfTupleProp", new XmlComment(@"This property is a generic type containing a tuple.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:GenericParent.TupleWithGenericProp", new XmlComment(@"This property is a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:GenericParent.TupleWithNestedGenericProp", new XmlComment(@"This property is a tuple with a nested generic type inside.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:ExampleClass.Add(System.Int32,System.Int32)~System.Int32", new XmlComment(@"Adds two integers and returns the result.", null, null, @"The sum of two integers.", null, false, [@"    ```int c = Math.Add(4, 5);
 if (c &gt; 10)
 {
     Console.WriteLine(c);
 }```"], [new XmlParameterComment(@"left", @"The left operand of the addition.", null, false), new XmlParameterComment(@"right", @"The right operand of the addition.", null, false)], null));
-            _cache.Add(@"M:ExampleClass.AddAsync(System.Int32,System.Int32)~System.Threading.Tasks.Task{System.Int32}", new XmlComment(@"This method is an example of a method that
+            cache.Add(@"M:ExampleClass.AddAsync(System.Int32,System.Int32)~System.Threading.Tasks.Task{System.Int32}", new XmlComment(@"This method is an example of a method that
 returns an awaitable item.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:ExampleClass.DoNothingAsync~System.Threading.Tasks.Task", new XmlComment(@"This method is an example of a method that
+            cache.Add(@"M:ExampleClass.DoNothingAsync~System.Threading.Tasks.Task", new XmlComment(@"This method is an example of a method that
 returns a Task which should map to a void return type.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:ExampleClass.AddNumbers(System.Int32[])~System.Int32", new XmlComment(@"This method is an example of a method that consumes
+            cache.Add(@"M:ExampleClass.AddNumbers(System.Int32[])~System.Int32", new XmlComment(@"This method is an example of a method that consumes
 an params array.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:ITestInterface.Method(System.Int32)~System.Int32", new XmlComment(@"This method is part of the test interface.", null, @"This content would be inherited by classes
+            cache.Add(@"M:ITestInterface.Method(System.Int32)~System.Int32", new XmlComment(@"This method is part of the test interface.", null, @"This content would be inherited by classes
 that implement this interface when the
 implementing class uses ""inheritdoc""", @"The value of arg", null, false, null, [new XmlParameterComment(@"arg", @"The argument to the method", null, false)], null));
-            _cache.Add(@"M:InheritOnlyReturns.MyParentMethod(System.Boolean)~System.Boolean", new XmlComment(@"In this example, this summary is only visible for this method.", null, null, @"A boolean", null, false, null, null, null));
-            _cache.Add(@"M:InheritOnlyReturns.MyChildMethod~System.Boolean", new XmlComment(null, null, null, @"A boolean", null, false, null, null, null));
-            _cache.Add(@"M:InheritAllButRemarks.MyParentMethod(System.Boolean)~System.Boolean", new XmlComment(@"In this example, this summary is visible on all the methods.", null, @"The remarks can be inherited by other methods
+            cache.Add(@"M:InheritOnlyReturns.MyParentMethod(System.Boolean)~System.Boolean", new XmlComment(@"In this example, this summary is only visible for this method.", null, null, @"A boolean", null, false, null, null, null));
+            cache.Add(@"M:InheritOnlyReturns.MyChildMethod~System.Boolean", new XmlComment(null, null, null, @"A boolean", null, false, null, null, null));
+            cache.Add(@"M:InheritAllButRemarks.MyParentMethod(System.Boolean)~System.Boolean", new XmlComment(@"In this example, this summary is visible on all the methods.", null, @"The remarks can be inherited by other methods
 using the xpath expression.", @"A boolean", null, false, null, null, null));
-            _cache.Add(@"M:InheritAllButRemarks.MyChildMethod~System.Boolean", new XmlComment(@"In this example, this summary is visible on all the methods.", null, null, @"A boolean", null, false, null, null, null));
-            _cache.Add(@"M:GenericParent.GetTaskOfTuple~System.Threading.Tasks.Task{System.ValueTuple{System.Int32,System.String}}", new XmlComment(@"This method returns a generic type containing a tuple.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:GenericParent.GetTupleOfTask~System.ValueTuple{System.Int32,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method returns a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:GenericParent.GetTupleOfTask1``1~System.ValueTuple{System.Int32,System.Collections.Generic.Dictionary{System.Int32,``0}}", new XmlComment(@"This method return a tuple with a generic type containing a
+            cache.Add(@"M:InheritAllButRemarks.MyChildMethod~System.Boolean", new XmlComment(@"In this example, this summary is visible on all the methods.", null, null, @"A boolean", null, false, null, null, null));
+            cache.Add(@"M:GenericParent.GetTaskOfTuple~System.Threading.Tasks.Task{System.ValueTuple{System.Int32,System.String}}", new XmlComment(@"This method returns a generic type containing a tuple.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:GenericParent.GetTupleOfTask~System.ValueTuple{System.Int32,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method returns a tuple with a generic type inside.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:GenericParent.GetTupleOfTask1``1~System.ValueTuple{System.Int32,System.Collections.Generic.Dictionary{System.Int32,``0}}", new XmlComment(@"This method return a tuple with a generic type containing a
 type parameter inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:GenericParent.GetTupleOfTask2``1~System.ValueTuple{``0,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method return a tuple with a generic type containing a
+            cache.Add(@"M:GenericParent.GetTupleOfTask2``1~System.ValueTuple{``0,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method return a tuple with a generic type containing a
 type parameter inside.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:GenericParent.GetNestedGeneric~System.Collections.Generic.Dictionary{System.Int32,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method returns a nested generic with all types resolved.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:GenericParent.GetNestedGeneric1``1~System.Collections.Generic.Dictionary{System.Int32,System.Collections.Generic.Dictionary{System.Int32,``0}}", new XmlComment(@"This method returns a nested generic with a type parameter.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:ParamsAndParamRefs.GetGenericValue``1(``0)~``0", new XmlComment(@"The GetGenericValue method.", null, @"This sample shows how to specify the T ParamsAndParamRefs.GetGenericValue&lt;T&gt;(T para)
+            cache.Add(@"M:GenericParent.GetNestedGeneric~System.Collections.Generic.Dictionary{System.Int32,System.Collections.Generic.Dictionary{System.Int32,System.String}}", new XmlComment(@"This method returns a nested generic with all types resolved.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:GenericParent.GetNestedGeneric1``1~System.Collections.Generic.Dictionary{System.Int32,System.Collections.Generic.Dictionary{System.Int32,``0}}", new XmlComment(@"This method returns a nested generic with a type parameter.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:ParamsAndParamRefs.GetGenericValue``1(``0)~``0", new XmlComment(@"The GetGenericValue method.", null, @"This sample shows how to specify the T ParamsAndParamRefs.GetGenericValue&lt;T&gt;(T para)
 method as a cref attribute.
 The parameter and return value are both of an arbitrary type,
 T", null, null, false, null, null, null));
 
-            return _cache;
+            return cache;
         }
     }
 
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
index dc6f6c8f4740..a3cba54234ac 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
@@ -70,14 +70,14 @@ file static class XmlCommentCache
 
         private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<string, XmlComment>();
+            var cache = new Dictionary<string, XmlComment>();
 
-            _cache.Add(@"M:TestController.Get~System.String", new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
-            _cache.Add(@"M:Test2Controller.Get(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
-            _cache.Add(@"M:Test2Controller.Get(System.Int32)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"id", @"The id associated with the request.", null, false)], null));
-            _cache.Add(@"M:Test2Controller.Post(Todo)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"todo", @"The todo to insert into the database.", null, false)], null));
+            cache.Add(@"M:TestController.Get~System.String", new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
+            cache.Add(@"M:Test2Controller.Get(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
+            cache.Add(@"M:Test2Controller.Get(System.Int32)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"id", @"The id associated with the request.", null, false)], null));
+            cache.Add(@"M:Test2Controller.Post(Todo)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"todo", @"The todo to insert into the database.", null, false)], null));
 
-            return _cache;
+            return cache;
         }
     }
 
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
index d8b5435edae3..442d8f3fcb64 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
@@ -70,31 +70,32 @@ file static class XmlCommentCache
 
         private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<string, XmlComment>();
-
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get~System.String", new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get2(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get3(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", @"Testy McTester", false)], null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get4~Microsoft.AspNetCore.Http.HttpResults.NotFound{System.String}", new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get5~Microsoft.AspNetCore.Http.HttpResults.Results{Microsoft.AspNetCore.Http.HttpResults.NotFound{System.String},Microsoft.AspNetCore.Http.HttpResults.Ok{System.String},Microsoft.AspNetCore.Http.HttpResults.Created}", new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"200", @"Indicates that the value is even.", @""), new XmlResponseComment(@"201", @"Indicates that the value is less than 50.", @""), new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Post6(User)~Microsoft.AspNetCore.Http.IResult", new XmlComment(@"Creates a new user.", null, @"Sample request:
+            var cache = new Dictionary<string, XmlComment>();
+
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get~System.String", new XmlComment(@"A summary of the action.", @"A description of the action.", null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get2(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", null, false)], [new XmlResponseComment(@"200", @"Returns the greeting.", @"")]));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get3(System.String)~System.String", new XmlComment(null, null, null, null, null, false, null, [new XmlParameterComment(@"name", @"The name of the person.", @"Testy McTester", false)], null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get4~Microsoft.AspNetCore.Http.HttpResults.NotFound{System.String}", new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get5~Microsoft.AspNetCore.Http.HttpResults.Results{Microsoft.AspNetCore.Http.HttpResults.NotFound{System.String},Microsoft.AspNetCore.Http.HttpResults.Ok{System.String},Microsoft.AspNetCore.Http.HttpResults.Created}", new XmlComment(null, null, null, null, null, false, null, null, [new XmlResponseComment(@"200", @"Indicates that the value is even.", @""), new XmlResponseComment(@"201", @"Indicates that the value is less than 50.", @""), new XmlResponseComment(@"404", @"Indicates that the value was not found.", @"")]));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Post6(User)~Microsoft.AspNetCore.Http.IResult", new XmlComment(@"Creates a new user.", null, @"Sample request:
     POST /6
     {
         ""username"": ""johndoe"",
         ""email"": ""john@example.com""
     }", null, null, false, null, [new XmlParameterComment(@"user", @"The user information.", @"{""username"": ""johndoe"", ""email"": ""john@example.com""}", false)], [new XmlResponseComment(@"201", @"Successfully created the user.", @""), new XmlResponseComment(@"400", @"If the user data is invalid.", @"")]));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Put7(System.Nullable{System.Int32},System.String)~Microsoft.AspNetCore.Http.IResult", new XmlComment(@"Updates an existing record.", null, null, null, null, false, null, [new XmlParameterComment(@"id", @"Legacy ID parameter - use uuid instead.", null, true), new XmlParameterComment(@"uuid", @"Unique identifier for the record.", null, false)], [new XmlResponseComment(@"204", @"Update successful.", @""), new XmlResponseComment(@"404", @"Legacy response - will be removed.", @"")]));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get8~System.Threading.Tasks.Task", new XmlComment(@"A summary of Get8.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get9~System.Threading.Tasks.ValueTask", new XmlComment(@"A summary of Get9.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get10~System.Threading.Tasks.Task", new XmlComment(@"A summary of Get10.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get11~System.Threading.Tasks.ValueTask", new XmlComment(@"A summary of Get11.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get12~System.Threading.Tasks.Task{System.String}", new XmlComment(@"A summary of Get12.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get13~System.Threading.Tasks.ValueTask{System.String}", new XmlComment(@"A summary of Get13.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get14~System.Threading.Tasks.Task{Holder{System.String}}", new XmlComment(@"A summary of Get14.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Get15~System.Threading.Tasks.Task{Holder{System.String}}", new XmlComment(@"A summary of Get15.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"M:RouteHandlerExtensionMethods.Post16(Example)", new XmlComment(@"A summary of Post16.", null, null, null, null, false, null, null, null));
-
-            return _cache;
+            cache.Add(@"M:RouteHandlerExtensionMethods.Put7(System.Nullable{System.Int32},System.String)~Microsoft.AspNetCore.Http.IResult", new XmlComment(@"Updates an existing record.", null, null, null, null, false, null, [new XmlParameterComment(@"id", @"Legacy ID parameter - use uuid instead.", null, true), new XmlParameterComment(@"uuid", @"Unique identifier for the record.", null, false)], [new XmlResponseComment(@"204", @"Update successful.", @""), new XmlResponseComment(@"404", @"Legacy response - will be removed.", @"")]));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get8~System.Threading.Tasks.Task", new XmlComment(@"A summary of Get8.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get9~System.Threading.Tasks.ValueTask", new XmlComment(@"A summary of Get9.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get10~System.Threading.Tasks.Task", new XmlComment(@"A summary of Get10.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get11~System.Threading.Tasks.ValueTask", new XmlComment(@"A summary of Get11.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get12~System.Threading.Tasks.Task{System.String}", new XmlComment(@"A summary of Get12.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get13~System.Threading.Tasks.ValueTask{System.String}", new XmlComment(@"A summary of Get13.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get14~System.Threading.Tasks.Task{Holder{System.String}}", new XmlComment(@"A summary of Get14.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get15~System.Threading.Tasks.Task{Holder{System.String}}", new XmlComment(@"A summary of Get15.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Post16(Example)", new XmlComment(@"A summary of Post16.", null, null, null, null, false, null, null, null));
+            cache.Add(@"M:RouteHandlerExtensionMethods.Get17(System.Int32[])~System.Int32[][]", new XmlComment(@"A summary of Get17.", null, null, null, null, false, null, null, null));
+
+            return cache;
         }
     }
 
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
index c86ff163e6c2..33c9a83916b4 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
@@ -70,40 +70,40 @@ file static class XmlCommentCache
 
         private static Dictionary<string, XmlComment> GenerateCacheEntries()
         {
-            var _cache = new Dictionary<string, XmlComment>();
-
-            _cache.Add(@"T:Todo", new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:Project", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:ProjectBoard.BoardItem", new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:ProjectBoard.ProtectedInternalElement", new XmlComment(@"Can find this XML comment.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"T:ProjectRecord", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
-            _cache.Add(@"T:User", new XmlComment(null, null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:ProjectBoard.ProtectedInternalElement.Name", new XmlComment(@"The unique identifier for the element.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:ProjectRecord.Name", new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:ProjectRecord.Description", new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:TodoWithDescription.Id", new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:TodoWithDescription.Name", new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
-            _cache.Add(@"P:TodoWithDescription.Description", new XmlComment(@"A description of the the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
-            _cache.Add(@"P:TypeWithExamples.BooleanType", new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
-            _cache.Add(@"P:TypeWithExamples.IntegerType", new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
-            _cache.Add(@"P:TypeWithExamples.LongType", new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
-            _cache.Add(@"P:TypeWithExamples.DoubleType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(@"P:TypeWithExamples.FloatType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
-            _cache.Add(@"P:TypeWithExamples.DateTimeType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
-            _cache.Add(@"P:TypeWithExamples.DateOnlyType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
-            _cache.Add(@"P:TypeWithExamples.StringType", new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
-            _cache.Add(@"P:TypeWithExamples.GuidType", new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
-            _cache.Add(@"P:TypeWithExamples.TimeOnlyType", new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
-            _cache.Add(@"P:TypeWithExamples.TimeSpanType", new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
-            _cache.Add(@"P:TypeWithExamples.ByteType", new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
-            _cache.Add(@"P:TypeWithExamples.DecimalType", new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
-            _cache.Add(@"P:TypeWithExamples.UriType", new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
-            _cache.Add(@"P:IUser.Id", new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:IUser.Name", new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:User.Id", new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
-            _cache.Add(@"P:User.Name", new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
-
-            return _cache;
+            var cache = new Dictionary<string, XmlComment>();
+
+            cache.Add(@"T:Todo", new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:Project", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:ProjectBoard.BoardItem", new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:ProjectBoard.ProtectedInternalElement", new XmlComment(@"Can find this XML comment.", null, null, null, null, false, null, null, null));
+            cache.Add(@"T:ProjectRecord", new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null));
+            cache.Add(@"T:User", new XmlComment(null, null, null, null, null, false, null, null, null));
+            cache.Add(@"P:ProjectBoard.ProtectedInternalElement.Name", new XmlComment(@"The unique identifier for the element.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:ProjectRecord.Name", new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:ProjectRecord.Description", new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:TodoWithDescription.Id", new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:TodoWithDescription.Name", new XmlComment(null, null, null, null, @"The name of the todo.", false, null, null, null));
+            cache.Add(@"P:TodoWithDescription.Description", new XmlComment(@"A description of the the todo.", null, null, null, @"Another description of the todo.", false, null, null, null));
+            cache.Add(@"P:TypeWithExamples.BooleanType", new XmlComment(null, null, null, null, null, false, [@"true"], null, null));
+            cache.Add(@"P:TypeWithExamples.IntegerType", new XmlComment(null, null, null, null, null, false, [@"42"], null, null));
+            cache.Add(@"P:TypeWithExamples.LongType", new XmlComment(null, null, null, null, null, false, [@"1234567890123456789"], null, null));
+            cache.Add(@"P:TypeWithExamples.DoubleType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            cache.Add(@"P:TypeWithExamples.FloatType", new XmlComment(null, null, null, null, null, false, [@"3.14"], null, null));
+            cache.Add(@"P:TypeWithExamples.DateTimeType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01T00:00:00Z"], null, null));
+            cache.Add(@"P:TypeWithExamples.DateOnlyType", new XmlComment(null, null, null, null, null, false, [@"2022-01-01"], null, null));
+            cache.Add(@"P:TypeWithExamples.StringType", new XmlComment(null, null, null, null, null, false, [@"Hello, World!"], null, null));
+            cache.Add(@"P:TypeWithExamples.GuidType", new XmlComment(null, null, null, null, null, false, [@"2d8f1eac-b5c6-4e29-8c62-4d9d75ef3d3d"], null, null));
+            cache.Add(@"P:TypeWithExamples.TimeOnlyType", new XmlComment(null, null, null, null, null, false, [@"12:30:45"], null, null));
+            cache.Add(@"P:TypeWithExamples.TimeSpanType", new XmlComment(null, null, null, null, null, false, [@"P3DT4H5M"], null, null));
+            cache.Add(@"P:TypeWithExamples.ByteType", new XmlComment(null, null, null, null, null, false, [@"255"], null, null));
+            cache.Add(@"P:TypeWithExamples.DecimalType", new XmlComment(null, null, null, null, null, false, [@"3.14159265359"], null, null));
+            cache.Add(@"P:TypeWithExamples.UriType", new XmlComment(null, null, null, null, null, false, [@"https://example.com"], null, null));
+            cache.Add(@"P:IUser.Id", new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:IUser.Name", new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:User.Id", new XmlComment(@"The unique identifier for the user.", null, null, null, null, false, null, null, null));
+            cache.Add(@"P:User.Name", new XmlComment(@"The user's display name.", null, null, null, null, false, null, null, null));
+
+            return cache;
         }
     }
 

From 32a1a9244fd07a8234fcba089ce374aad8214ff0 Mon Sep 17 00:00:00 2001
From: Safia Abdalla <safia@safia.rocks>
Date: Fri, 28 Mar 2025 16:27:18 -0700
Subject: [PATCH 6/7] Commit updated snapshots

---
 ...penApiXmlCommentSupport.generated.verified.cs | 16 ----------------
 ...penApiXmlCommentSupport.generated.verified.cs | 16 ----------------
 ...penApiXmlCommentSupport.generated.verified.cs | 16 ----------------
 ...penApiXmlCommentSupport.generated.verified.cs | 16 ----------------
 ...penApiXmlCommentSupport.generated.verified.cs | 16 ----------------
 ...penApiXmlCommentSupport.generated.verified.cs | 16 ----------------
 6 files changed, 96 deletions(-)

diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
index fc66ff8c2b8c..1eb5f70cee6a 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
@@ -230,22 +230,6 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
                 }
             }
 
-            if (type.IsArray && type.GetElementType() is { } elementType)
-            {
-                int rank = type.GetArrayRank();
-                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
-                if (rank > 1)
-                {
-                    sb.Append('[');
-                    sb.Append(',', rank - 1);
-                    sb.Append(']');
-                }
-                else
-                {
-                    sb.Append("[]");
-                }
-            }
-
             if (type.IsGenericType)
             {
                 Type genericDef = type.GetGenericTypeDefinition();
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
index 9e4f28460a9e..2232587cb744 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs
@@ -259,22 +259,6 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
                 }
             }
 
-            if (type.IsArray && type.GetElementType() is { } elementType)
-            {
-                int rank = type.GetArrayRank();
-                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
-                if (rank > 1)
-                {
-                    sb.Append('[');
-                    sb.Append(',', rank - 1);
-                    sb.Append(']');
-                }
-                else
-                {
-                    sb.Append("[]");
-                }
-            }
-
             if (type.IsGenericType)
             {
                 Type genericDef = type.GetGenericTypeDefinition();
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 bb54500a2f3b..7748d372f620 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
@@ -349,22 +349,6 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
                 }
             }
 
-            if (type.IsArray && type.GetElementType() is { } elementType)
-            {
-                int rank = type.GetArrayRank();
-                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
-                if (rank > 1)
-                {
-                    sb.Append('[');
-                    sb.Append(',', rank - 1);
-                    sb.Append(']');
-                }
-                else
-                {
-                    sb.Append("[]");
-                }
-            }
-
             if (type.IsGenericType)
             {
                 Type genericDef = type.GetGenericTypeDefinition();
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
index a3cba54234ac..7420c2923583 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
@@ -234,22 +234,6 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
                 }
             }
 
-            if (type.IsArray && type.GetElementType() is { } elementType)
-            {
-                int rank = type.GetArrayRank();
-                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
-                if (rank > 1)
-                {
-                    sb.Append('[');
-                    sb.Append(',', rank - 1);
-                    sb.Append(']');
-                }
-                else
-                {
-                    sb.Append("[]");
-                }
-            }
-
             if (type.IsGenericType)
             {
                 Type genericDef = type.GetGenericTypeDefinition();
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
index 442d8f3fcb64..6336e09f5b89 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
@@ -252,22 +252,6 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
                 }
             }
 
-            if (type.IsArray && type.GetElementType() is { } elementType)
-            {
-                int rank = type.GetArrayRank();
-                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
-                if (rank > 1)
-                {
-                    sb.Append('[');
-                    sb.Append(',', rank - 1);
-                    sb.Append(']');
-                }
-                else
-                {
-                    sb.Append("[]");
-                }
-            }
-
             if (type.IsGenericType)
             {
                 Type genericDef = type.GetGenericTypeDefinition();
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
index 33c9a83916b4..f0179d4e5ef1 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
@@ -260,22 +260,6 @@ private static string GetTypeDocId(Type type, bool includeGenericArguments, bool
                 }
             }
 
-            if (type.IsArray && type.GetElementType() is { } elementType)
-            {
-                int rank = type.GetArrayRank();
-                var sb = new StringBuilder(GetTypeDocId(elementType, includeGenericArguments, omitGenericArity));
-                if (rank > 1)
-                {
-                    sb.Append('[');
-                    sb.Append(',', rank - 1);
-                    sb.Append(']');
-                }
-                else
-                {
-                    sb.Append("[]");
-                }
-            }
-
             if (type.IsGenericType)
             {
                 Type genericDef = type.GetGenericTypeDefinition();

From 304b9720c6ae6a7154907e7256370a0086f7ecc5 Mon Sep 17 00:00:00 2001
From: Safia Abdalla <safia@safia.rocks>
Date: Fri, 28 Mar 2025 21:45:06 -0700
Subject: [PATCH 7/7] Quarantine OpenApiReference tests

---
 src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddFileTests.cs | 3 +++
 src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs  | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddFileTests.cs b/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddFileTests.cs
index 28c54b750149..f6f915bdce7a 100644
--- a/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddFileTests.cs
+++ b/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddFileTests.cs
@@ -3,6 +3,7 @@
 
 using System.Text.RegularExpressions;
 using System.Xml;
+using Microsoft.AspNetCore.InternalTesting;
 using Microsoft.DotNet.OpenApi.Tests;
 using Xunit.Abstractions;
 
@@ -143,6 +144,7 @@ public async Task OpenApi_Add_NSwagTypeScript()
     }
 
     [Fact]
+    [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/61225")]
     public async Task OpenApi_Add_FromJson()
     {
         var project = CreateBasicProject(withOpenApi: true);
@@ -183,6 +185,7 @@ public async Task OpenApi_Add_File_UseProjectOption()
     }
 
     [Fact]
+    [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/61225")]
     public async Task OpenApi_Add_MultipleTimes_OnlyOneReference()
     {
         var project = CreateBasicProject(withOpenApi: true);
diff --git a/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs b/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs
index c75900b0530a..7461c69acc01 100644
--- a/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs
+++ b/src/Tools/Microsoft.dotnet-openapi/test/OpenApiAddURLTests.cs
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Text.RegularExpressions;
+using Microsoft.AspNetCore.InternalTesting;
 using Microsoft.DotNet.OpenApi.Tests;
 using Xunit.Abstractions;
 
@@ -12,6 +13,7 @@ public class OpenApiAddURLTests : OpenApiTestBase
     public OpenApiAddURLTests(ITestOutputHelper output) : base(output) { }
 
     [Fact]
+    [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/61225")]
     public async Task OpenApi_Add_Url_WithContentDisposition()
     {
         var project = CreateBasicProject(withOpenApi: false);