Created
September 23, 2025 10:10
-
-
Save ophura/1e0918361e776933b8770ee423144648 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using Microsoft.CodeAnalysis; | |
| using Microsoft.CodeAnalysis.CSharp; | |
| using Microsoft.CodeAnalysis.CSharp.Syntax; | |
| using Microsoft.CodeAnalysis.Diagnostics; | |
| using System.Collections.Immutable; | |
| using System.Linq; | |
| namespace ApiUsageAnalyzer | |
| { | |
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | |
| public sealed class ApiUsageAnalyzer : DiagnosticAnalyzer | |
| { | |
| private static readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor( | |
| id: "ID0002", | |
| title: "Mismatched ApiUsage target", | |
| messageFormat: "Event '{1}' is marked with ApiUsage({0}.{2}) but is called with {0}.{3}", | |
| category: "Usage", | |
| defaultSeverity: DiagnosticSeverity.Error, | |
| isEnabledByDefault: true | |
| ); | |
| public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(_descriptor); | |
| public override void Initialize(AnalysisContext context) | |
| { | |
| context.EnableConcurrentExecution(); | |
| context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); | |
| context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression); | |
| } | |
| private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context) | |
| { | |
| if (!(context.Node is InvocationExpressionSyntax invocation)) return; | |
| var model = context.SemanticModel; | |
| var arguments = invocation.ArgumentList.Arguments; | |
| var enumInfo = model.GetTypeInfo(arguments.First().Expression); | |
| var fields = enumInfo.Type.GetMembers().OfType<IFieldSymbol>().ToDictionary(field => field.ConstantValue, field => field.Name); | |
| var target = model.GetConstantValue(arguments.First().Expression).Value; | |
| var method = (string)model.GetConstantValue(arguments.Last().Expression).Value; | |
| var type = context.ContainingSymbol.ContainingType; | |
| var symbol = type.GetMembers(method).OfType<IMethodSymbol>().First(); | |
| var attribute = symbol.GetAttributes().First(data => data.AttributeClass.Name is "ApiUsageAttribute"); | |
| var usage = attribute.ConstructorArguments.First().Value; | |
| if (target.Equals(usage)) return; | |
| context.ReportDiagnostic(Diagnostic.Create(_descriptor, invocation.GetLocation(), enumInfo.Type.Name, method, fields[usage], fields[target])); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment