Skip to content

Fix OpenAPI XML code gen #60977

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 19, 2025
Merged

Fix OpenAPI XML code gen #60977

merged 2 commits into from
Mar 19, 2025

Conversation

captainsafia
Copy link
Member

Fixes some of the issues in #60783.

@captainsafia captainsafia requested a review from a team as a code owner March 17, 2025 20:10
@ghost ghost added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Mar 17, 2025
@@ -189,4 +189,26 @@ public static IEnumerable<INamedTypeSymbol> GetBaseTypes(this ITypeSymbol? type)
current = current.BaseType;
}
}

public static bool IsAccessibleType(this ISymbol symbol)
Copy link
Member Author

Choose a reason for hiding this comment

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

This helps us avoid generating XML comments for non-public types and internal properties in non-public types.

Note: this does mean that DTOs have to be defined as public types if they are defined in the same assembly as the API (see where this is called in the AssemnlyTypeSymbolVisitor above). We can consider changing this behavior to allow XML comments on internal types in the application assembly.

Copy link
Member

Choose a reason for hiding this comment

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

Seems a bit overaggressive. I'm guessing we'll change this behavior a bit in the future.

Any reason to disallow the type entirely just because it has internal types?

Copy link
Member

@martincostello martincostello Mar 19, 2025

Choose a reason for hiding this comment

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

I just hit this error in preview 2:

C:\Coding\martincostello\alexa-london-travel-site\artifacts\obj\LondonTravel.Site\debug\Microsoft.AspNetCore.OpenApi.SourceGenerators\Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator\OpenApiXmlCommentSupport.generated.cs(207,125): error CS0122: 'CustomHttpHeadersMiddleware.Csp' is inaccessible due to its protection level

This relates to a private nested class in public class that has some /// comments on the internal const string members in it.

Would this change fix that scenario too?

Copy link
Member

Choose a reason for hiding this comment

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

One more scenario for you - seems like it's trying to document things to do with the Regex source generator (example):

C:\Coding\martincostello\adventofcode\artifacts\obj\AdventOfCode.Site\debug\Microsoft.AspNetCore.OpenApi.SourceGenerators\Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator\OpenApiXmlCommentSupport.generated.cs(1589,94): error CS0234: The type or namespace name 'HexColor_1' does not exist in the namespace 'System.Text.RegularExpressions.Generated' (are you missing an assembly reference?)

If not catered for by this change, I can open a new issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

It depends on whether the type in question is defined in or outside the assembly.

I believe these bits have landed in the nightly preview3 SDK if you want to take another bump to try it out.

Alternatively, I'd recommend filing an issue so we can at least capture this scenario in a test case even if it is already fixed.

Copy link
Member

Choose a reason for hiding this comment

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

I'll try out a nightly tomorrow and see if you think it should be fixed.

Copy link
Member

Choose a reason for hiding this comment

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

Confirmed these two are catered for as of 10.0.100-preview.3.25169.19, but confirmed #61019 is still an issue and found two new ones 😅. Issues shortly...

Copy link
Member

Choose a reason for hiding this comment

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

I think they're probably the same one issue: #61035

Copy link
Member

Choose a reason for hiding this comment

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

I believe these bits have landed in the nightly preview3 SDK if you want to take another bump to try it out.

Alternatively, I'd recommend filing an issue so we can at least capture this scenario in a test case even if it is already fixed.

Found a different case where a private type causes a compiler error.

Copy link
Member

Choose a reason for hiding this comment

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

@@ -27,6 +27,9 @@ internal static string GenerateXmlCommentSupportSource(string commentsFromXmlFil
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable
// Suppress warnings about obsolete types and members
// in generated code
#pragma warning disable CS0612, CS0618
Copy link
Member Author

Choose a reason for hiding this comment

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

Suppress obsoletion warnings in generated code. If someone happens to be using an obsolete type in their API, we don't want to resurface this warning in generated code where we call typeof(ObsolteType).

{
var parameter = comment.Parameters[i];
var exampleLiteral = string.IsNullOrEmpty(parameter.Example)
? "null"
: FormatStringForCode(parameter.Example!);
codeWriter.Write($"new XmlParameterComment(@\"{parameter.Name}\", @\"{parameter.Description}\", {exampleLiteral}, {(parameter.Deprecated == true ? "true" : "false")})");
codeWriter.Write("new XmlParameterComment(");
Copy link
Member Author

Choose a reason for hiding this comment

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

Here and below we format strings in the emitted code so that we can properly escape quoted strings inside XML descriptions/summaries/etc.

@@ -46,7 +46,7 @@ public static MemberKey FromMethodSymbol(IMethodSymbol method, Compilation compi

returnType = actualReturnType.TypeKind == TypeKind.TypeParameter
? "typeof(object)"
: $"typeof({actualReturnType.ToDisplayString(_typeKeyFormat)})";
: $"typeof({ReplaceGenericArguments(actualReturnType.ToDisplayString(_typeKeyFormat))})";
Copy link
Member Author

Choose a reason for hiding this comment

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

We need to make sure that all generics are emitted as open generics for method declarations. We already do this elsewhere in the MemberKey for the declaring type and the property type but not for methods.

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

public class DeeplyNestedLevel1
Copy link
Member Author

Choose a reason for hiding this comment

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

Since we require all types to be public for discovery, we moved the shared types that are used by tests and the sample app to the sample app since the tests reference the sample app.

Previously, we used compile includes with internal types which doesn't work in this new model.

@captainsafia captainsafia force-pushed the cs/openapi-xml-bugfix branch from d231073 to 196bd95 Compare March 17, 2025 20:19
@@ -118,10 +118,12 @@
"type": "object",
"properties": {
"title": {
"type": "string"
"type": "string",
Copy link
Member Author

Choose a reason for hiding this comment

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

STJ's schema generation assumes strings are nullable in an oblivious nullability context (see disable in Sample.csproj), hence the delta here.

Copy link
Contributor

@mikekistler mikekistler left a comment

Choose a reason for hiding this comment

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

I didn't really understand all this but everything that I could understand looks fine! 👍

@@ -189,4 +189,26 @@ public static IEnumerable<INamedTypeSymbol> GetBaseTypes(this ITypeSymbol? type)
current = current.BaseType;
}
}

public static bool IsAccessibleType(this ISymbol symbol)
Copy link
Member

Choose a reason for hiding this comment

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

Seems a bit overaggressive. I'm guessing we'll change this behavior a bit in the future.

Any reason to disallow the type entirely just because it has internal types?

@captainsafia
Copy link
Member Author

Any reason to disallow the type entirely just because it has internal types?

I assume you mean internal properties? This will omit public properties in internal types.

We can relax the rule a little bit and still discover XML comments on internal types on the application assembly but prohibit them on internal types in referenced files.

This would strike a balance between not discovering internal types in assemblies we don't have access to while still permitting them in the application assembly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants