Improve logic to find target symbol locations

This commit is contained in:
Sergio Pedri
2024-11-12 22:51:37 -08:00
parent 50d8ff5b8e
commit 4e6cf2bc21
5 changed files with 34 additions and 11 deletions
@@ -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;
@@ -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));
}
}
@@ -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));
}
@@ -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);
@@ -175,4 +175,31 @@ internal static class ISymbolExtensions
accessibility == Accessibility.Public ||
accessibility == Accessibility.Internal && symbol.ContainingAssembly.GivesAccessTo(assembly);
}
/// <summary>
/// Gets the location of a given symbol that is in the same syntax tree of a specified attribute, or the first one.
/// </summary>
/// <param name="symbol">The input <see cref="ISymbol"/> instance to check.</param>
/// <param name="attributeData">The target <see cref="AttributeData"/> instance.</param>
/// <returns>The best <see cref="Location"/> match.</returns>
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;
}
}