diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UnsupportedCSharpLanguageVersionAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UnsupportedCSharpLanguageVersionAnalyzer.cs index 42312904..cbfa6ecd 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UnsupportedCSharpLanguageVersionAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UnsupportedCSharpLanguageVersionAnalyzer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -76,7 +75,7 @@ public sealed class UnsupportedCSharpLanguageVersionAnalyzer : DiagnosticAnalyze typeSymbols.TryGetValue(attributeName, out INamedTypeSymbol? attributeSymbol) && SymbolEqualityComparer.Default.Equals(attributeClass, attributeSymbol)) { - context.ReportDiagnostic(Diagnostic.Create(UnsupportedCSharpLanguageVersionError, context.Symbol.Locations.FirstOrDefault())); + context.ReportDiagnostic(Diagnostic.Create(UnsupportedCSharpLanguageVersionError, context.Symbol.GetLocationFromAttributeDataOrDefault(attribute))); // If we created a diagnostic for this symbol, we can stop. Even if there's multiple attributes, no need for repeated errors return; diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs index b96c5844..2b0621dc 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -79,7 +78,7 @@ public sealed class WinRTClassUsingNotifyPropertyChangedAttributesAnalyzer : Dia { context.ReportDiagnostic(Diagnostic.Create( GeneratorAttributeNamesToDiagnosticsMap[attributeClass.Name], - context.Symbol.Locations.FirstOrDefault(), + context.Symbol.GetLocationFromAttributeDataOrDefault(attribute), context.Symbol)); } } diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs index 891e8455..f76cb4bc 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -56,7 +55,7 @@ public sealed class WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer } // We only care about it if it's using [GeneratedBindableCustomProperty] - if (!typeSymbol.HasAttributeWithType(generatedBindableCustomPropertySymbol)) + if (!typeSymbol.TryGetAttributeWithType(generatedBindableCustomPropertySymbol, out AttributeData? generatedBindableCustomPropertyAttribute)) { return; } @@ -66,7 +65,7 @@ public sealed class WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer { context.ReportDiagnostic(Diagnostic.Create( WinRTGeneratedBindableCustomPropertyWithBaseObservablePropertyOnField, - typeSymbol.Locations.FirstOrDefault(), + typeSymbol.GetLocationFromAttributeDataOrDefault(generatedBindableCustomPropertyAttribute), typeSymbol, fieldSymbol.ContainingType, fieldSymbol.Name)); @@ -77,7 +76,7 @@ public sealed class WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer { context.ReportDiagnostic(Diagnostic.Create( WinRTGeneratedBindableCustomPropertyWithBaseRelayCommand, - typeSymbol.Locations.FirstOrDefault(), + typeSymbol.GetLocationFromAttributeDataOrDefault(generatedBindableCustomPropertyAttribute), typeSymbol, methodSymbol)); } diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs index 3c92eb9e..7cbcf136 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Linq; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -50,7 +49,7 @@ public sealed class WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompati } // If the method is not using [RelayCommand], we can skip it - if (!methodSymbol.HasAttributeWithType(relayCommandSymbol)) + if (!methodSymbol.TryGetAttributeWithType(relayCommandSymbol, out AttributeData? relayCommandAttribute)) { return; } @@ -60,7 +59,7 @@ public sealed class WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompati { context.ReportDiagnostic(Diagnostic.Create( WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatible, - methodSymbol.Locations.FirstOrDefault(), + methodSymbol.GetLocationFromAttributeDataOrDefault(relayCommandAttribute), methodSymbol)); } }, SymbolKind.Method); diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs index a825454f..964e4daf 100644 --- a/src/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs +++ b/src/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs @@ -175,4 +175,31 @@ internal static class ISymbolExtensions accessibility == Accessibility.Public || accessibility == Accessibility.Internal && symbol.ContainingAssembly.GivesAccessTo(assembly); } + + /// + /// Gets the location of a given symbol that is in the same syntax tree of a specified attribute, or the first one. + /// + /// The input instance to check. + /// The target instance. + /// The best match. + public static Location? GetLocationFromAttributeDataOrDefault(this ISymbol symbol, AttributeData attributeData) + { + Location? firstLocation = null; + + // Get the syntax tree where the attribute application is located. We use + // it to try to find the symbol location that belongs to the same file. + SyntaxTree? attributeTree = attributeData.ApplicationSyntaxReference?.SyntaxTree; + + foreach (Location location in symbol.Locations) + { + if (location.SourceTree == attributeTree) + { + return location; + } + + firstLocation ??= location; + } + + return firstLocation; + } }