-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Populate XML doc comments into OpenAPI document #60326
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
Changes from 11 commits
f1fcf50
6cabfc8
cb35db3
6c667cb
4bff0d2
28dccf5
d3eaad1
78bb55c
320c49f
2e9d107
5ae71ff
6633e28
779c96a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?xml version="1.0" encoding="utf-8" standalone="no"?> | ||
| <Project> | ||
| <PropertyGroup> | ||
| <InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces> | ||
| <InterceptorsPreviewNamespaces> | ||
| $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsPreviewNamespaces> | ||
| </PropertyGroup> | ||
| <Target Name="GenerateAdditionalXmlFilesForOpenApi" | ||
| AfterTargets="ResolveReferences"> | ||
| <ItemGroup> | ||
| <AdditionalFiles | ||
| Include="@(ReferencePath->'%(RootDir)%(Directory)%(Filename).xml')" | ||
| Condition="'%(ReferencePath.Extension)' == '.dll' AND | ||
| Exists('%(ReferencePath.RootDir)%(ReferencePath.Directory)%(ReferencePath.Filename).xml') AND | ||
| ('%(ReferencePath.ReferenceSourceTarget)' == 'ProjectReference' OR | ||
| '%(ReferencePath.NuGetSourceType)' == 'Package')" | ||
| KeepMetadata="Identity" /> | ||
| </ItemGroup> | ||
| </Target> | ||
| </Project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.CodeAnalysis.CSharp; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
|
||
| namespace Microsoft.AspNetCore.OpenApi.SourceGenerators; | ||
|
|
||
| /// <summary> | ||
| /// Represents an invocation of the AddOpenApi method. | ||
| /// </summary> | ||
| /// <param name="Variant">The variant of the AddOpenApi method.</param> | ||
| /// <param name="InvocationExpression">The invocation expression.</param> | ||
| /// <param name="Location">The location of the invocation that can be intercepted.</param> | ||
| internal sealed record AddOpenApiInvocation( | ||
| AddOpenApiOverloadVariant Variant, | ||
| InvocationExpressionSyntax InvocationExpression, | ||
| InterceptableLocation? Location); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Collections.Generic; | ||
|
|
||
| namespace Microsoft.AspNetCore.OpenApi.SourceGenerators; | ||
|
|
||
| internal class AddOpenApiInvocationComparer : IEqualityComparer<AddOpenApiInvocation> | ||
| { | ||
| public static AddOpenApiInvocationComparer Instance { get; } = new(); | ||
| public bool Equals(AddOpenApiInvocation? x, AddOpenApiInvocation? y) | ||
| { | ||
| if (ReferenceEquals(x, y)) | ||
| { | ||
| return true; | ||
| } | ||
| if (x is null || y is null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return x.Variant.Equals(y.Variant); | ||
| } | ||
|
|
||
| public int GetHashCode(AddOpenApiInvocation obj) => | ||
| HashCode.Combine(obj.Variant); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Microsoft.AspNetCore.OpenApi.SourceGenerators; | ||
|
|
||
| internal enum AddOpenApiOverloadVariant | ||
| { | ||
| AddOpenApi, | ||
| AddOpenApiDocumentName, | ||
| AddOpenApiDocumentNameConfigureOptions, | ||
| AddOpenApiConfigureOptions, | ||
| Unknown | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Collections.Generic; | ||
| using System.Collections.Immutable; | ||
| using System.Threading; | ||
| using Microsoft.CodeAnalysis; | ||
|
|
||
| namespace Microsoft.AspNetCore.OpenApi.SourceGenerators; | ||
|
|
||
| /// <summary> | ||
| /// Visits the assembly symbols to collect public types, properties, and methods that might | ||
| /// contain XML documentation comments. | ||
| /// </summary> | ||
| internal sealed class AssemblyTypeSymbolsVisitor(IAssemblySymbol assemblySymbol, CancellationToken cancellation) : SymbolVisitor | ||
| { | ||
| private readonly CancellationToken _cancellationToken = cancellation; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: when do you decide to drop a primary constructor arg into an explicit field on a class?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, I didn't commit a fix to this. I think this might have been caught in a refactor. I wasn't previously passing in the CancellationToken via the primary constructor. |
||
| private readonly HashSet<INamedTypeSymbol> _exportedTypes = new(SymbolEqualityComparer.Default); | ||
| private readonly HashSet<IPropertySymbol> _exportedProperties = new(SymbolEqualityComparer.Default); | ||
| private readonly HashSet<IMethodSymbol> _exportedMethods = new(SymbolEqualityComparer.Default); | ||
|
|
||
| public ImmutableArray<INamedTypeSymbol> GetPublicTypes() => [.. _exportedTypes]; | ||
| public ImmutableArray<IPropertySymbol> GetPublicProperties() => [.. _exportedProperties]; | ||
| public ImmutableArray<IMethodSymbol> GetPublicMethods() => [.. _exportedMethods]; | ||
|
|
||
| public void VisitAssembly() => VisitAssembly(assemblySymbol); | ||
|
|
||
| public override void VisitAssembly(IAssemblySymbol symbol) | ||
| { | ||
| _cancellationToken.ThrowIfCancellationRequested(); | ||
| symbol.GlobalNamespace.Accept(this); | ||
| } | ||
|
|
||
| public override void VisitNamespace(INamespaceSymbol symbol) | ||
| { | ||
| foreach (var namespaceOrType in symbol.GetMembers()) | ||
| { | ||
| _cancellationToken.ThrowIfCancellationRequested(); | ||
| namespaceOrType.Accept(this); | ||
| } | ||
| } | ||
|
|
||
| public override void VisitNamedType(INamedTypeSymbol type) | ||
| { | ||
| _cancellationToken.ThrowIfCancellationRequested(); | ||
|
|
||
| if (!IsDeclaredInAssembly(type) || !_exportedTypes.Add(type)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var nestedTypes = type.GetTypeMembers(); | ||
|
|
||
| foreach (var nestedType in nestedTypes) | ||
| { | ||
| _cancellationToken.ThrowIfCancellationRequested(); | ||
| nestedType.Accept(this); | ||
| } | ||
|
|
||
| var properties = type.GetMembers().OfType<IPropertySymbol>(); | ||
| foreach (var property in properties) | ||
| { | ||
| _cancellationToken.ThrowIfCancellationRequested(); | ||
| if (IsDeclaredInAssembly(property) && _exportedProperties.Add(property)) | ||
| { | ||
| property.Type.Accept(this); | ||
| } | ||
| } | ||
| var methods = type.GetMembers().OfType<IMethodSymbol>(); | ||
| foreach (var method in methods) | ||
| { | ||
| _cancellationToken.ThrowIfCancellationRequested(); | ||
| if (IsDeclaredInAssembly(method) && _exportedMethods.Add(method)) | ||
| { | ||
| method.Accept(this); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private bool IsDeclaredInAssembly(ISymbol symbol) => | ||
| SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assemblySymbol); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
|
|
||
| namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Xml; | ||
|
|
||
| internal static class DocumentationCommentXmlNames | ||
| { | ||
| public const string CElementName = "c"; | ||
| public const string CodeElementName = "code"; | ||
| public const string CompletionListElementName = "completionlist"; | ||
| public const string DescriptionElementName = "description"; | ||
| public const string ExampleElementName = "example"; | ||
| public const string ExceptionElementName = "exception"; | ||
| public const string IncludeElementName = "include"; | ||
| public const string InheritdocElementName = "inheritdoc"; | ||
| public const string ItemElementName = "item"; | ||
| public const string ListElementName = "list"; | ||
| public const string ListHeaderElementName = "listheader"; | ||
| public const string ParaElementName = "para"; | ||
| public const string ParameterElementName = "param"; | ||
| public const string ParameterReferenceElementName = "paramref"; | ||
| public const string PermissionElementName = "permission"; | ||
| public const string PlaceholderElementName = "placeholder"; | ||
| public const string PreliminaryElementName = "preliminary"; | ||
| public const string RemarksElementName = "remarks"; | ||
| public const string ReturnsElementName = "returns"; | ||
| public const string SeeElementName = "see"; | ||
| public const string SeeAlsoElementName = "seealso"; | ||
| public const string SummaryElementName = "summary"; | ||
| public const string TermElementName = "term"; | ||
| public const string ThreadSafetyElementName = "threadsafety"; | ||
| public const string TypeParameterElementName = "typeparam"; | ||
| public const string TypeParameterReferenceElementName = "typeparamref"; | ||
| public const string ValueElementName = "value"; | ||
| public const string CrefAttributeName = "cref"; | ||
| public const string HrefAttributeName = "href"; | ||
| public const string FileAttributeName = "file"; | ||
| public const string InstanceAttributeName = "instance"; | ||
| public const string LangwordAttributeName = "langword"; | ||
| public const string NameAttributeName = "name"; | ||
| public const string PathAttributeName = "path"; | ||
| public const string StaticAttributeName = "static"; | ||
| public const string TypeAttributeName = "type"; | ||
|
|
||
| public static bool ElementEquals(string name1, string name2, bool fromVb = false) | ||
| { | ||
| return string.Equals(name1, name2, fromVb ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); | ||
| } | ||
|
|
||
| public static bool AttributeEquals(string name1, string name2) | ||
| { | ||
| return string.Equals(name1, name2, StringComparison.Ordinal); | ||
| } | ||
|
|
||
| public static new bool Equals(object left, object right) | ||
| { | ||
| return object.Equals(left, right); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.