From 0e99323c27325b8950056ffa5a61578ee332fa4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Fri, 1 Aug 2025 01:15:09 -0400 Subject: [PATCH] Use additional locations to report diagnostic on symbols with multiple locations --- src/DocumentationGenerator/Program.cs | 2 +- .../Internals/ContextExtensions.cs | 52 +++++++++++++++---- .../Internals/ContextExtensions.g.cs | 25 +++++++++ .../Internals/ContextExtensions.tt | 4 ++ 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/DocumentationGenerator/Program.cs b/src/DocumentationGenerator/Program.cs index bb68718fe..a17664828 100644 --- a/src/DocumentationGenerator/Program.cs +++ b/src/DocumentationGenerator/Program.cs @@ -1,4 +1,4 @@ -#pragma warning disable RS1035 +#pragma warning disable RS1035 #pragma warning disable CA1849 #pragma warning disable MA0004 #pragma warning disable MA0009 diff --git a/src/Meziantou.Analyzer/Internals/ContextExtensions.cs b/src/Meziantou.Analyzer/Internals/ContextExtensions.cs index 40858b22d..63a6353fc 100755 --- a/src/Meziantou.Analyzer/Internals/ContextExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/ContextExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Immutable; +using System.Data; using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -11,11 +12,31 @@ namespace Meziantou.Analyzer.Internals; internal static partial class ContextExtensions { - private static Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, Location location, ImmutableDictionary? properties, string?[]? messageArgs) + private static Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, Location? location, ImmutableDictionary? properties, string?[]? messageArgs) { return Diagnostic.Create(descriptor, location, properties, messageArgs); } + public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, IEnumerable locations, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary.Empty, locations, messageArgs); + public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IEnumerable locations, params string?[]? messageArgs) + { + var inSource = locations.Where(l => l.IsInSource); + if (!inSource.Any()) + { + context.ReportDiagnostic(CreateDiagnostic(descriptor, location: null, properties, messageArgs)); + return; + } + + var diagnostic = Diagnostic.Create( + descriptor, + location: inSource.First(), + additionalLocations: inSource.Skip(1), + properties: properties, + messageArgs: messageArgs); + + context.ReportDiagnostic(diagnostic); + } + public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, SyntaxReference syntaxReference, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary.Empty, syntaxReference, messageArgs); @@ -40,15 +61,13 @@ public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticD public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ISymbol symbol, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary.Empty, symbol, messageArgs); public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, ISymbol symbol, string?[]? messageArgs = null) { - foreach (var location in symbol.Locations) - { - ReportDiagnostic(context, descriptor, properties, location, messageArgs); - } + ReportDiagnostic(context, descriptor, properties, symbol.Locations, messageArgs); } public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, IFieldSymbol symbol, DiagnosticFieldReportOptions reportOptions, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary.Empty, symbol, reportOptions, messageArgs); public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IFieldSymbol symbol, DiagnosticFieldReportOptions reportOptions, string?[]? messageArgs = null) { + List? locations = null; foreach (var location in symbol.Locations) { if (reportOptions.HasFlag(DiagnosticFieldReportOptions.ReportOnReturnType)) @@ -61,13 +80,17 @@ public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticD } } - ReportDiagnostic(context, descriptor, properties, location, messageArgs); + locations ??= []; + locations.Add(location); } + + ReportDiagnostic(context, descriptor, properties, locations ?? [], messageArgs); } public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, IMethodSymbol symbol, DiagnosticMethodReportOptions reportOptions, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary.Empty, symbol, reportOptions, messageArgs); public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IMethodSymbol symbol, DiagnosticMethodReportOptions reportOptions, string?[]? messageArgs = null) { + List? locations = null; foreach (var location in symbol.Locations) { if (reportOptions.HasFlag(DiagnosticMethodReportOptions.ReportOnReturnType)) @@ -86,13 +109,17 @@ public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticD } } - ReportDiagnostic(context, descriptor, properties, location, messageArgs); + locations ??= []; + locations.Add(location); } + + ReportDiagnostic(context, descriptor, properties, locations ?? [], messageArgs); } public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, IParameterSymbol symbol, DiagnosticParameterReportOptions reportOptions, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary.Empty, symbol, reportOptions, messageArgs); public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IParameterSymbol symbol, DiagnosticParameterReportOptions reportOptions, string?[]? messageArgs = null) { + List? locations = null; foreach (var location in symbol.Locations) { if (reportOptions.HasFlag(DiagnosticParameterReportOptions.ReportOnType)) @@ -105,13 +132,17 @@ public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticD } } - ReportDiagnostic(context, descriptor, properties, location, messageArgs); + locations ??= []; + locations.Add(location); } + + ReportDiagnostic(context, descriptor, properties, locations ?? [], messageArgs); } public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, IPropertySymbol symbol, DiagnosticPropertyReportOptions reportOptions, string?[]? messageArgs = null) => ReportDiagnostic(context, descriptor, ImmutableDictionary.Empty, symbol, reportOptions, messageArgs); public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IPropertySymbol symbol, DiagnosticPropertyReportOptions reportOptions, string?[]? messageArgs = null) { + List? locations = null; foreach (var location in symbol.Locations) { if (reportOptions.HasFlag(DiagnosticPropertyReportOptions.ReportOnReturnType)) @@ -130,8 +161,11 @@ public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticD } } - ReportDiagnostic(context, descriptor, properties, location, messageArgs); + locations ??= []; + locations.Add(location); } + + ReportDiagnostic(context, descriptor, properties, locations ?? [], messageArgs); } public static void ReportDiagnostic(this DiagnosticReporter context, DiagnosticDescriptor descriptor, IOperation operation, string?[]? messageArgs = null) diff --git a/src/Meziantou.Analyzer/Internals/ContextExtensions.g.cs b/src/Meziantou.Analyzer/Internals/ContextExtensions.g.cs index 6ad31456a..a7a45d2d9 100644 --- a/src/Meziantou.Analyzer/Internals/ContextExtensions.g.cs +++ b/src/Meziantou.Analyzer/Internals/ContextExtensions.g.cs @@ -9,6 +9,11 @@ namespace Meziantou.Analyzer.Internals; internal static partial class ContextExtensions { + + public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, IEnumerable locations, string?[]? messageArgs = null) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, locations, messageArgs); + + public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IEnumerable locations, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, locations, messageArgs); + public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken syntaxToken, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, syntaxToken, messageArgs); @@ -83,6 +88,11 @@ public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, Diag public static void ReportDiagnostic(this SyntaxNodeAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, AttributeData attribute, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, attribute, messageArgs); + + public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, IEnumerable locations, string?[]? messageArgs = null) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, locations, messageArgs); + + public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IEnumerable locations, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, locations, messageArgs); + public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken syntaxToken, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, syntaxToken, messageArgs); @@ -157,6 +167,11 @@ public static void ReportDiagnostic(this SymbolAnalysisContext context, Diagnost public static void ReportDiagnostic(this SymbolAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, AttributeData attribute, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, attribute, messageArgs); + + public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, IEnumerable locations, string?[]? messageArgs = null) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, locations, messageArgs); + + public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IEnumerable locations, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, locations, messageArgs); + public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken syntaxToken, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, syntaxToken, messageArgs); @@ -231,6 +246,11 @@ public static void ReportDiagnostic(this OperationAnalysisContext context, Diagn public static void ReportDiagnostic(this OperationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, AttributeData attribute, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, attribute, messageArgs); + + public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, IEnumerable locations, string?[]? messageArgs = null) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, locations, messageArgs); + + public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IEnumerable locations, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, locations, messageArgs); + public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken syntaxToken, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, syntaxToken, messageArgs); @@ -305,6 +325,11 @@ public static void ReportDiagnostic(this OperationBlockAnalysisContext context, public static void ReportDiagnostic(this OperationBlockAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, AttributeData attribute, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, attribute, messageArgs); + + public static void ReportDiagnostic(this CompilationAnalysisContext context, DiagnosticDescriptor descriptor, IEnumerable locations, string?[]? messageArgs = null) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, locations, messageArgs); + + public static void ReportDiagnostic(this CompilationAnalysisContext context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IEnumerable locations, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, locations, messageArgs); + public static void ReportDiagnostic(this CompilationAnalysisContext context, DiagnosticDescriptor descriptor, SyntaxToken syntaxToken, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, syntaxToken, messageArgs); diff --git a/src/Meziantou.Analyzer/Internals/ContextExtensions.tt b/src/Meziantou.Analyzer/Internals/ContextExtensions.tt index 5b8c0ce9c..0597906a0 100644 --- a/src/Meziantou.Analyzer/Internals/ContextExtensions.tt +++ b/src/Meziantou.Analyzer/Internals/ContextExtensions.tt @@ -14,6 +14,10 @@ namespace Meziantou.Analyzer.Internals; internal static partial class ContextExtensions { <# foreach (string type in new string[] { "SyntaxNodeAnalysisContext", "SymbolAnalysisContext", "OperationAnalysisContext", "OperationBlockAnalysisContext", "CompilationAnalysisContext" }) { #> + public static void ReportDiagnostic(this <#= type #> context, DiagnosticDescriptor descriptor, IEnumerable locations, string?[]? messageArgs = null) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, locations, messageArgs); + + public static void ReportDiagnostic(this <#= type #> context, DiagnosticDescriptor descriptor, ImmutableDictionary? properties, IEnumerable locations, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, properties, locations, messageArgs); + public static void ReportDiagnostic(this <#= type #> context, DiagnosticDescriptor descriptor, SyntaxToken syntaxToken, params string?[]? messageArgs) => ReportDiagnostic(new DiagnosticReporter(context), descriptor, syntaxToken, messageArgs);