diff --git a/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs b/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs index 269472de..8aa4a57f 100644 --- a/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs +++ b/CommunityToolkit.Mvvm.SourceGenerators/Extensions/ISymbolExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; namespace CommunityToolkit.Mvvm.SourceGenerators.Extensions; @@ -64,6 +65,32 @@ internal static class ISymbolExtensions return false; } + /// + /// Tries to get an attribute with the specified full name. + /// + /// The input instance to check. + /// The attribute name to look for. + /// The resulting attribute, if it was found. + /// Whether or not has an attribute with the specified name. + public static bool TryGetAttributeWithFullyQualifiedName(this ISymbol symbol, string name, [NotNullWhen(true)] out AttributeData? attributeData) + { + ImmutableArray attributes = symbol.GetAttributes(); + + foreach (AttributeData attribute in attributes) + { + if (attribute.AttributeClass?.HasFullyQualifiedName(name) == true) + { + attributeData = attribute; + + return true; + } + } + + attributeData = null; + + return false; + } + /// /// Calculates the effective accessibility for a given symbol. /// diff --git a/CommunityToolkit.Mvvm.SourceGenerators/Input/RelayCommandGenerator.cs b/CommunityToolkit.Mvvm.SourceGenerators/Input/RelayCommandGenerator.cs index 06e2c7bc..8e0660f7 100644 --- a/CommunityToolkit.Mvvm.SourceGenerators/Input/RelayCommandGenerator.cs +++ b/CommunityToolkit.Mvvm.SourceGenerators/Input/RelayCommandGenerator.cs @@ -23,40 +23,33 @@ public sealed partial class RelayCommandGenerator : IIncrementalGenerator /// public void Initialize(IncrementalGeneratorInitializationContext context) { - // Get all method declarations with at least one attribute - IncrementalValuesProvider methodSymbols = + // Gather info for all annotated command methods (starting from method declarations with at least one attribute) + IncrementalValuesProvider<(HierarchyInfo Hierarchy, Result Info)> commandInfoWithErrors = context.SyntaxProvider .CreateSyntaxProvider( static (node, _) => node is MethodDeclarationSyntax { Parent: ClassDeclarationSyntax, AttributeLists.Count: > 0 }, - static (context, _) => + static (context, token) => { if (!context.SemanticModel.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp8)) { return default; } - return (IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!; + IMethodSymbol methodSymbol = (IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node, token)!; + + // Filter the methods using [RelayCommand] + if (!methodSymbol.TryGetAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.Input.RelayCommandAttribute", out AttributeData? attribute)) + { + return default; + } + + // Produce the incremental models + HierarchyInfo hierarchy = HierarchyInfo.From(methodSymbol.ContainingType); + CommandInfo? commandInfo = Execute.GetInfo(methodSymbol, attribute, out ImmutableArray diagnostics); + + return (Hierarchy: hierarchy, new Result(commandInfo, diagnostics)); }) - .Where(static item => item is not null)!; - - // Filter the methods using [RelayCommand] - IncrementalValuesProvider<(IMethodSymbol Symbol, AttributeData Attribute)> methodSymbolsWithAttributeData = - methodSymbols - .Select(static (item, _) => ( - item, - Attribute: item.GetAttributes().FirstOrDefault(a => a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Mvvm.Input.RelayCommandAttribute") == true))) - .Where(static item => item.Attribute is not null)!; - - // Gather info for all annotated command methods - IncrementalValuesProvider<(HierarchyInfo Hierarchy, Result Info)> commandInfoWithErrors = - methodSymbolsWithAttributeData - .Select(static (item, _) => - { - HierarchyInfo hierarchy = HierarchyInfo.From(item.Symbol.ContainingType); - CommandInfo? commandInfo = Execute.GetInfo(item.Symbol, item.Attribute, out ImmutableArray diagnostics); - - return (hierarchy, new Result(commandInfo, diagnostics)); - }); + .Where(static item => item.Hierarchy is not null); // Output the diagnostics context.ReportDiagnostics(commandInfoWithErrors.Select(static (item, _) => item.Info.Errors)); @@ -66,7 +59,7 @@ public sealed partial class RelayCommandGenerator : IIncrementalGenerator commandInfoWithErrors .Where(static item => item.Info.Value is not null) .Select(static (item, _) => (item.Hierarchy, item.Info.Value!)) - .WithComparers(HierarchyInfo.Comparer.Default, CommandInfo.Comparer.Default); + .WithComparers(HierarchyInfo.Comparer.Default, CommandInfo.Comparer.Default); // Generate the commands context.RegisterSourceOutput(commandInfo, static (context, item) =>