| File: Helpers\AssemblyTypeSymbolsVisitor.cs | Web Access |
| Project: src\aspnetcore\src\OpenApi\gen\Microsoft.AspNetCore.OpenApi.SourceGenerators.csproj (Microsoft.AspNetCore.OpenApi.SourceGenerators) |
// 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; 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 (type.IsGenericType) { // Because type comments are based on the Unbound generic type, i.e. List`1, we want to use the unbound generic type type = type.OriginalDefinition; } if (!IsAccessibleType(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 (IsAccessibleType(property) && _exportedProperties.Add(property)) { property.Type.Accept(this); } } var methods = type.GetMembers().OfType<IMethodSymbol>(); foreach (var method in methods) { _cancellationToken.ThrowIfCancellationRequested(); if (IsAccessibleType(method) && _exportedMethods.Add(method)) { method.Accept(this); } } } private bool IsAccessibleType(ISymbol symbol) => SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assemblySymbol) && (symbol.DeclaredAccessibility == Accessibility.Public || symbol.DeclaredAccessibility == Accessibility.Internal || symbol.DeclaredAccessibility == Accessibility.ProtectedOrInternal); }