Skip to content

Commit 196bd95

Browse files
committed
Fix OpenAPI XML code gen
1 parent 825daf6 commit 196bd95

File tree

31 files changed

+537
-304
lines changed

31 files changed

+537
-304
lines changed

src/OpenApi/gen/Helpers/AssemblyTypeSymbolsVisitor.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Collections.Immutable;
66
using System.Threading;
7+
using Microsoft.AspNetCore.OpenApi.SourceGenerators.Xml;
78
using Microsoft.CodeAnalysis;
89

910
namespace Microsoft.AspNetCore.OpenApi.SourceGenerators;
@@ -44,7 +45,7 @@ public override void VisitNamedType(INamedTypeSymbol type)
4445
{
4546
_cancellationToken.ThrowIfCancellationRequested();
4647

47-
if (!IsDeclaredInAssembly(type) || !_exportedTypes.Add(type))
48+
if (!IsDeclaredInAssembly(type) || !type.IsAccessibleType() || !_exportedTypes.Add(type))
4849
{
4950
return;
5051
}
@@ -61,7 +62,7 @@ public override void VisitNamedType(INamedTypeSymbol type)
6162
foreach (var property in properties)
6263
{
6364
_cancellationToken.ThrowIfCancellationRequested();
64-
if (IsDeclaredInAssembly(property) && _exportedProperties.Add(property))
65+
if (IsDeclaredInAssembly(property) && property.IsAccessibleType() && _exportedProperties.Add(property))
6566
{
6667
property.Type.Accept(this);
6768
}
@@ -70,7 +71,7 @@ public override void VisitNamedType(INamedTypeSymbol type)
7071
foreach (var method in methods)
7172
{
7273
_cancellationToken.ThrowIfCancellationRequested();
73-
if (IsDeclaredInAssembly(method) && _exportedMethods.Add(method))
74+
if (IsDeclaredInAssembly(method) && method.IsAccessibleType() && _exportedMethods.Add(method))
7475
{
7576
method.Accept(this);
7677
}

src/OpenApi/gen/Helpers/ISymbolExtensions.cs

+22
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,26 @@ public static IEnumerable<INamedTypeSymbol> GetBaseTypes(this ITypeSymbol? type)
189189
current = current.BaseType;
190190
}
191191
}
192+
193+
public static bool IsAccessibleType(this ISymbol symbol)
194+
{
195+
// Check if the symbol itself is public
196+
if (symbol.DeclaredAccessibility != Accessibility.Public)
197+
{
198+
return false;
199+
}
200+
201+
// Check if all containing types are public as well
202+
var containingType = symbol.ContainingType;
203+
while (containingType != null)
204+
{
205+
if (containingType.DeclaredAccessibility != Accessibility.Public)
206+
{
207+
return false;
208+
}
209+
containingType = containingType.ContainingType;
210+
}
211+
212+
return true;
213+
}
192214
}

src/OpenApi/gen/XmlCommentGenerator.Emitter.cs

+16-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ internal static string GenerateXmlCommentSupportSource(string commentsFromXmlFil
2727
// </auto-generated>
2828
//------------------------------------------------------------------------------
2929
#nullable enable
30+
// Suppress warnings about obsolete types and members
31+
// in generated code
32+
#pragma warning disable CS0612, CS0618
3033
3134
namespace System.Runtime.CompilerServices
3235
{
@@ -487,7 +490,7 @@ internal static string EmitSourceGeneratedXmlComment(XmlComment comment)
487490
else
488491
{
489492
codeWriter.Write("[");
490-
for (int i = 0; i < comment.Examples.Count; i++)
493+
for (var i = 0; i < comment.Examples.Count; i++)
491494
{
492495
var example = comment.Examples[i];
493496
codeWriter.Write(FormatStringForCode(example));
@@ -506,13 +509,18 @@ internal static string EmitSourceGeneratedXmlComment(XmlComment comment)
506509
else
507510
{
508511
codeWriter.Write("[");
509-
for (int i = 0; i < comment.Parameters.Count; i++)
512+
for (var i = 0; i < comment.Parameters.Count; i++)
510513
{
511514
var parameter = comment.Parameters[i];
512515
var exampleLiteral = string.IsNullOrEmpty(parameter.Example)
513516
? "null"
514517
: FormatStringForCode(parameter.Example!);
515-
codeWriter.Write($"new XmlParameterComment(@\"{parameter.Name}\", @\"{parameter.Description}\", {exampleLiteral}, {(parameter.Deprecated == true ? "true" : "false")})");
518+
codeWriter.Write("new XmlParameterComment(");
519+
codeWriter.Write(FormatStringForCode(parameter.Name) + ", ");
520+
codeWriter.Write(FormatStringForCode(parameter.Description) + ", ");
521+
codeWriter.Write(exampleLiteral + ", ");
522+
codeWriter.Write(parameter.Deprecated == true ? "true" : "false");
523+
codeWriter.Write(")");
516524
if (i < comment.Parameters.Count - 1)
517525
{
518526
codeWriter.Write(", ");
@@ -528,10 +536,13 @@ internal static string EmitSourceGeneratedXmlComment(XmlComment comment)
528536
else
529537
{
530538
codeWriter.Write("[");
531-
for (int i = 0; i < comment.Responses.Count; i++)
539+
for (var i = 0; i < comment.Responses.Count; i++)
532540
{
533541
var response = comment.Responses[i];
534-
codeWriter.Write($"new XmlResponseComment(@\"{response.Code}\", @\"{response.Description}\", {(response.Example is null ? "null" : FormatStringForCode(response.Example))})");
542+
codeWriter.Write("new XmlResponseComment(");
543+
codeWriter.Write(FormatStringForCode(response.Code) + ", ");
544+
codeWriter.Write(FormatStringForCode(response.Description) + ", ");
545+
codeWriter.Write(response.Example is null ? "null)" : FormatStringForCode(response.Example) + ")");
535546
if (i < comment.Responses.Count - 1)
536547
{
537548
codeWriter.Write(", ");

src/OpenApi/gen/XmlCommentGenerator.Parser.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,11 @@ public sealed partial class XmlCommentGenerator
9191
var comments = new List<(MemberKey, XmlComment?)>();
9292
foreach (var (name, value) in input.RawComments)
9393
{
94-
if (DocumentationCommentId.GetFirstSymbolForDeclarationId(name, compilation) is ISymbol symbol)
94+
if (DocumentationCommentId.GetFirstSymbolForDeclarationId(name, compilation) is ISymbol symbol &&
95+
symbol.IsAccessibleType() &&
96+
// Skip static classes that are just containers for members with annotations
97+
// since they cannot be instantiated.
98+
symbol is not INamedTypeSymbol { TypeKind: TypeKind.Class, IsStatic: true })
9599
{
96100
var parsedComment = XmlComment.Parse(symbol, compilation, value, cancellationToken);
97101
if (parsedComment is not null)

src/OpenApi/gen/XmlComments/MemberKey.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public static MemberKey FromMethodSymbol(IMethodSymbol method, Compilation compi
4646

4747
returnType = actualReturnType.TypeKind == TypeKind.TypeParameter
4848
? "typeof(object)"
49-
: $"typeof({actualReturnType.ToDisplayString(_typeKeyFormat)})";
49+
: $"typeof({ReplaceGenericArguments(actualReturnType.ToDisplayString(_typeKeyFormat))})";
5050
}
5151

5252
// Handle extension methods by skipping the 'this' parameter
@@ -62,10 +62,10 @@ public static MemberKey FromMethodSymbol(IMethodSymbol method, Compilation compi
6262
// For params arrays, use the array type
6363
if (p.IsParams && p.Type is IArrayTypeSymbol arrayType)
6464
{
65-
return $"typeof({arrayType.ToDisplayString(_typeKeyFormat)})";
65+
return $"typeof({ReplaceGenericArguments(arrayType.ToDisplayString(_typeKeyFormat))})";
6666
}
6767

68-
return $"typeof({p.Type.ToDisplayString(_typeKeyFormat)})";
68+
return $"typeof({ReplaceGenericArguments(p.Type.ToDisplayString(_typeKeyFormat))})";
6969
})
7070
.ToArray();
7171

src/OpenApi/sample/Controllers/TestController.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class RouteParamsContainer
4040
[FromRoute]
4141
[MinLength(5)]
4242
[UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", Justification = "MinLengthAttribute works without reflection on string properties.")]
43-
public string? Name { get; set; }
43+
public string Name { get; set; }
4444
}
4545

4646
public record MvcTodo(string Title, string Description, bool IsCompleted);

src/OpenApi/sample/Sample.csproj

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
<PropertyGroup>
44
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
5-
<Nullable>enable</Nullable>
5+
<!-- Disable to match nullability annotations in test environment. -->
6+
<Nullable>disable</Nullable>
67
<ImplicitUsings>enable</ImplicitUsings>
78
<!-- Required to generated trimmable Map-invocations. -->
89
<EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator>
@@ -24,11 +25,6 @@
2425
<Reference Include="Microsoft.AspNetCore.Mvc" />
2526
</ItemGroup>
2627

27-
<ItemGroup>
28-
<Compile Include="../test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.cs" />
29-
<Compile Include="../test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.Polymorphism.cs" />
30-
</ItemGroup>
31-
3228
<!-- Required to generated trimmable Map-invocations. -->
3329
<ItemGroup>
3430
<ProjectReference Include="$(RepoRoot)/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/Microsoft.AspNetCore.Http.RequestDelegateGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
public class DeeplyNestedLevel1
5+
{
6+
public required DeeplyNestedLevel2 Item2 { get; set; }
7+
}
8+
9+
public class DeeplyNestedLevel2
10+
{
11+
public required DeeplyNestedLevel3 Item3 { get; set; }
12+
}
13+
14+
public class DeeplyNestedLevel3
15+
{
16+
public required DeeplyNestedLevel4 Item4 { get; set; }
17+
}
18+
19+
public class DeeplyNestedLevel4
20+
{
21+
public required DeeplyNestedLevel5 Item5 { get; set; }
22+
}
23+
24+
public class DeeplyNestedLevel5
25+
{
26+
public required DeeplyNestedLevel6 Item6 { get; set; }
27+
}
28+
29+
public class DeeplyNestedLevel6
30+
{
31+
public required DeeplyNestedLevel7 Item7 { get; set; }
32+
}
33+
34+
public class DeeplyNestedLevel7
35+
{
36+
public required DeeplyNestedLevel8 Item8 { get; set; }
37+
}
38+
39+
public class DeeplyNestedLevel8
40+
{
41+
public required DeeplyNestedLevel9 Item9 { get; set; }
42+
}
43+
44+
public class DeeplyNestedLevel9
45+
{
46+
public required DeeplyNestedLevel10 Item10 { get; set; }
47+
}
48+
49+
public class DeeplyNestedLevel10
50+
{
51+
public required DeeplyNestedLevel11 Item11 { get; set; }
52+
}
53+
54+
public class DeeplyNestedLevel11
55+
{
56+
public required DeeplyNestedLevel12 Item12 { get; set; }
57+
}
58+
59+
public class DeeplyNestedLevel12
60+
{
61+
public required DeeplyNestedLevel13 Item13 { get; set; }
62+
}
63+
64+
public class DeeplyNestedLevel13
65+
{
66+
public required DeeplyNestedLevel14 Item14 { get; set; }
67+
}
68+
69+
public class DeeplyNestedLevel14
70+
{
71+
public required DeeplyNestedLevel15 Item15 { get; set; }
72+
}
73+
74+
public class DeeplyNestedLevel15
75+
{
76+
public required DeeplyNestedLevel16 Item16 { get; set; }
77+
}
78+
79+
public class DeeplyNestedLevel16
80+
{
81+
public required DeeplyNestedLevel17 Item17 { get; set; }
82+
}
83+
84+
public class DeeplyNestedLevel17
85+
{
86+
public required DeeplyNestedLevel18 Item18 { get; set; }
87+
}
88+
89+
public class DeeplyNestedLevel18
90+
{
91+
public required DeeplyNestedLevel19 Item19 { get; set; }
92+
}
93+
94+
public class DeeplyNestedLevel19
95+
{
96+
public required DeeplyNestedLevel20 Item20 { get; set; }
97+
}
98+
99+
public class DeeplyNestedLevel20
100+
{
101+
public required DeeplyNestedLevel21 Item21 { get; set; }
102+
}
103+
104+
public class DeeplyNestedLevel21
105+
{
106+
public required DeeplyNestedLevel22 Item22 { get; set; }
107+
}
108+
109+
public class DeeplyNestedLevel22
110+
{
111+
public required DeeplyNestedLevel23 Item23 { get; set; }
112+
}
113+
114+
public class DeeplyNestedLevel23
115+
{
116+
public required DeeplyNestedLevel24 Item24 { get; set; }
117+
}
118+
119+
public class DeeplyNestedLevel24
120+
{
121+
public required DeeplyNestedLevel25 Item25 { get; set; }
122+
}
123+
124+
public class DeeplyNestedLevel25
125+
{
126+
public required DeeplyNestedLevel26 Item26 { get; set; }
127+
}
128+
129+
public class DeeplyNestedLevel26
130+
{
131+
public required DeeplyNestedLevel27 Item27 { get; set; }
132+
}
133+
134+
public class DeeplyNestedLevel27
135+
{
136+
public required DeeplyNestedLevel28 Item28 { get; set; }
137+
}
138+
139+
public class DeeplyNestedLevel28
140+
{
141+
public required DeeplyNestedLevel29 Item29 { get; set; }
142+
}
143+
144+
public class DeeplyNestedLevel29
145+
{
146+
public required DeeplyNestedLevel30 Item30 { get; set; }
147+
}
148+
149+
public class DeeplyNestedLevel30
150+
{
151+
public required DeeplyNestedLevel31 Item31 { get; set; }
152+
}
153+
154+
public class DeeplyNestedLevel31
155+
{
156+
public required DeeplyNestedLevel32 Item32 { get; set; }
157+
}
158+
159+
public class DeeplyNestedLevel32
160+
{
161+
public required DeeplyNestedLevel33 Item33 { get; set; }
162+
}
163+
164+
public class DeeplyNestedLevel33
165+
{
166+
public required DeeplyNestedLevel34 Item34 { get; set; }
167+
}
168+
169+
public class DeeplyNestedLevel34
170+
{
171+
public required DeeplyNestedLevel35 Item35 { get; set; }
172+
}
173+
174+
public class DeeplyNestedLevel35
175+
{
176+
public required DeeplyNestedLevel36 Item36 { get; set; }
177+
}
178+
179+
public class DeeplyNestedLevel36
180+
{
181+
}

0 commit comments

Comments
 (0)