| File: MakeExeTypesInternalAnalyzer.cs | Web Access |
| Project: src\src\Analyzers\Microsoft.Analyzers.Extra\Microsoft.Analyzers.Extra.csproj (Microsoft.Analyzers.Extra) |
// 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 Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.Extensions.ExtraAnalyzers.Utilities; namespace Microsoft.Extensions.ExtraAnalyzers; /// <summary> /// C# analyzer that recommends making an executable's types internal. /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class MakeExeTypesInternalAnalyzer : DiagnosticAnalyzer { // if any member of the discovered public types are annotated with these attributes, then the type is known // to need to be public, so we don't report the type private static readonly string[] _disqualifyingMemberAttributes = new[] { "Xunit.FactAttribute", "Xunit.TheoryAttribute", "BenchmarkDotNet.Attributes.BenchmarkAttribute", "Microsoft.AspNetCore.Mvc.HttpGetAttribute", "System.Text.Json.Serialization.JsonConstructorAttribute", "System.Text.Json.Serialization.JsonExtensionDataAttribute", "System.Text.Json.Serialization.JsonIgnoreAttribute", "System.Text.Json.Serialization.JsonIncludeAttribute", "System.Text.Json.Serialization.JsonNumberHandlingAttribute", "System.Text.Json.Serialization.JsonPropertyNameAttribute", "System.Text.Json.Serialization.JsonPropertyOrderAttribute", }; private static readonly string[] _disqualifyingTypeAttributes = new[] { "MessagePack.MessagePackObjectAttribute", "Microsoft.AspNetCore.Mvc.ApiControllerAttribute", }; // if any of the discovered public types derive from the given base classes, we know the use requires the types to // be public, so we don't report the type private static readonly string[] _disqualifyingBaseClasses = new[] { "Microsoft.AspNetCore.Mvc.ControllerBase", "System.Web.Http.ApiController", "System.Web.Mvc.Controller", }; public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DiagDescriptors.MakeExeTypesInternal); public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(compilationStartContext => { var disqualifyingMemberAttributes = new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default); foreach (var name in _disqualifyingMemberAttributes) { var type = compilationStartContext.Compilation.GetTypeByMetadataName(name); if (type != null) { _ = disqualifyingMemberAttributes.Add(type); } } var disqualifyingTypeAttributes = new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default); foreach (var name in _disqualifyingTypeAttributes) { var type = compilationStartContext.Compilation.GetTypeByMetadataName(name); if (type != null) { _ = disqualifyingTypeAttributes.Add(type); } } var disqualifyingBaseClasses = new List<ITypeSymbol>(); foreach (var name in _disqualifyingBaseClasses) { var type = compilationStartContext.Compilation.GetTypeByMetadataName(name); if (type != null) { disqualifyingBaseClasses.Add(type); } } if (compilationStartContext.Compilation.Options.OutputKind == OutputKind.ConsoleApplication) { compilationStartContext.RegisterSymbolAction(symbolActionContext => { var type = (ITypeSymbol)symbolActionContext.Symbol; if (type.DeclaredAccessibility == Accessibility.Public && type.ContainingType == null) { // see if the type is annotated with one of the disqualifying attributes foreach (var attr in type.GetAttributes()) { if (attr.AttributeClass != null) { if (disqualifyingTypeAttributes.Contains(attr.AttributeClass)) { return; } } } // see if the type derives from one of the disqualifying base types foreach (var c in disqualifyingBaseClasses) { if (c.IsAncestorOf(type)) { return; } } // see if any members are annotated with disqualifying attributes var members = type.GetMembers(); foreach (var member in members) { var attrs = member.GetAttributes(); foreach (var attr in attrs) { if (attr.AttributeClass != null) { if (disqualifyingMemberAttributes.Contains(attr.AttributeClass)) { return; } } } } var diagnostic = Diagnostic.Create(DiagDescriptors.MakeExeTypesInternal, type.Locations[0], type.Name); symbolActionContext.ReportDiagnostic(diagnostic); } }, SymbolKind.NamedType); } }); } }