diff --git a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs index 2831b6a1..37882bc0 100644 --- a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs +++ b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs @@ -28,33 +28,18 @@ public INotifyPropertyChangedGenerator() } /// - protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, INotifyPropertyChangedInfo Info)> GetInfo( - IncrementalGeneratorInitializationContext context, - IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source) - { - static INotifyPropertyChangedInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData) - { - bool includeAdditionalHelperMethods = attributeData.GetNamedArgument("IncludeAdditionalHelperMethods", true); - - return new(includeAdditionalHelperMethods); - } - - return source.Select(static (item, _) => (item.Symbol, GetInfo(item.Symbol, item.AttributeData))); - } - - /// - protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, INotifyPropertyChangedInfo info, out ImmutableArray diagnostics) + protected override INotifyPropertyChangedInfo? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray diagnostics) { ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + INotifyPropertyChangedInfo? info = null; + // Check if the type already implements INotifyPropertyChanged if (typeSymbol.AllInterfaces.Any(i => i.HasFullyQualifiedName("global::System.ComponentModel.INotifyPropertyChanged"))) { builder.Add(DuplicateINotifyPropertyChangedInterfaceForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } // Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too) @@ -63,14 +48,17 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, INotifyP { builder.Add(InvalidAttributeCombinationForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } + bool includeAdditionalHelperMethods = attributeData.GetNamedArgument("IncludeAdditionalHelperMethods", true); + + info = new INotifyPropertyChangedInfo(includeAdditionalHelperMethods); + + End: diagnostics = builder.ToImmutable(); - return true; + return info; } /// diff --git a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs index 114cb937..c101ad09 100644 --- a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs +++ b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs @@ -27,15 +27,7 @@ public ObservableObjectGenerator() } /// - protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, object? Info)> GetInfo( - IncrementalGeneratorInitializationContext context, - IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source) - { - return source.Select(static (item, _) => (item.Symbol, (object?)null)); - } - - /// - protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object? info, out ImmutableArray diagnostics) + protected override object? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray diagnostics) { ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); @@ -44,9 +36,7 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object? { builder.Add(DuplicateINotifyPropertyChangedInterfaceForObservableObjectAttributeError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } // ...or INotifyPropertyChanging @@ -54,9 +44,7 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object? { builder.Add(DuplicateINotifyPropertyChangingInterfaceForObservableObjectAttributeError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } // Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too) @@ -65,14 +53,13 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, object? { builder.Add(InvalidAttributeCombinationForObservableObjectAttributeError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } + End: diagnostics = builder.ToImmutable(); - return true; + return null; } /// diff --git a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs index e16a161d..af217683 100644 --- a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs +++ b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs @@ -30,53 +30,18 @@ public ObservableRecipientGenerator() } /// - protected override IncrementalValuesProvider<(INamedTypeSymbol Symbol, ObservableRecipientInfo Info)> GetInfo( - IncrementalGeneratorInitializationContext context, - IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source) - { - static ObservableRecipientInfo GetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, bool isRequiresUnreferencedCodeAttributeAvailable) - { - string typeName = typeSymbol.Name; - bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true }); - bool isAbstract = typeSymbol.IsAbstract; - bool isObservableValidator = typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator"); - bool hasOnActivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnActivated" }); - bool hasOnDeactivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnDeactivated" }); - - return new( - typeName, - hasExplicitConstructors, - isAbstract, - isObservableValidator, - isRequiresUnreferencedCodeAttributeAvailable, - hasOnActivatedMethod, - hasOnDeactivatedMethod); - } - - // Check whether [RequiresUnreferencedCode] is available - IncrementalValueProvider isRequiresUnreferencedCodeAttributeAvailable = - context.CompilationProvider - .Select(static (item, _) => item.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute") is { DeclaredAccessibility: Accessibility.Public }); - - return - source - .Combine(isRequiresUnreferencedCodeAttributeAvailable) - .Select(static (item, _) => (item.Left.Symbol, GetInfo(item.Left.Symbol, item.Left.AttributeData, item.Right))); - } - - /// - protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, ObservableRecipientInfo info, out ImmutableArray diagnostics) + protected override ObservableRecipientInfo? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray diagnostics) { ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + ObservableRecipientInfo? info = null; + // Check if the type already inherits from ObservableRecipient if (typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient")) { builder.Add(DuplicateObservableRecipientError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } // Check if the type already inherits [ObservableRecipient] @@ -84,9 +49,7 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, Observab { builder.Add(InvalidAttributeCombinationForObservableRecipientAttributeError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } // In order to use [ObservableRecipient], the target type needs to inherit from ObservableObject, @@ -99,14 +62,31 @@ protected override bool ValidateTargetType(INamedTypeSymbol typeSymbol, Observab { builder.Add(MissingBaseObservableObjectFunctionalityError, typeSymbol, typeSymbol); - diagnostics = builder.ToImmutable(); - - return false; + goto End; } + // Gather all necessary info to propagate down the pipeline + string typeName = typeSymbol.Name; + bool hasExplicitConstructors = !(typeSymbol.InstanceConstructors.Length == 1 && typeSymbol.InstanceConstructors[0] is { Parameters.IsEmpty: true, IsImplicitlyDeclared: true }); + bool isAbstract = typeSymbol.IsAbstract; + bool isObservableValidator = typeSymbol.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableValidator"); + bool isRequiresUnreferencedCodeAttributeAvailable = compilation.HasAccessibleTypeWithMetadataName("System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute"); + bool hasOnActivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnActivated" }); + bool hasOnDeactivatedMethod = typeSymbol.GetMembers().Any(m => m is IMethodSymbol { Parameters.IsEmpty: true, Name: "OnDeactivated" }); + + info = new ObservableRecipientInfo( + typeName, + hasExplicitConstructors, + isAbstract, + isObservableValidator, + isRequiresUnreferencedCodeAttributeAvailable, + hasOnActivatedMethod, + hasOnDeactivatedMethod); + + End: diagnostics = builder.ToImmutable(); - return true; + return info; } /// diff --git a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs index 8b0a01a0..95fc8967 100644 --- a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs +++ b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; namespace CommunityToolkit.Mvvm.SourceGenerators; @@ -70,46 +69,40 @@ private protected TransitiveMembersGenerator(string attributeType, IEqualityComp /// public void Initialize(IncrementalGeneratorInitializationContext context) { - // Get all class declarations - IncrementalValuesProvider typeSymbols = + // Gather all generation info, and any diagnostics + IncrementalValuesProvider> generationInfoWithErrors = context.SyntaxProvider .CreateSyntaxProvider( static (node, _) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }, - static (context, _) => + (context, token) => { if (!context.SemanticModel.Compilation.HasLanguageVersionAtLeastEqualTo(LanguageVersion.CSharp8)) { return default; } - return (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!; - }) - .Where(static item => item is not null)!; + INamedTypeSymbol typeSymbol = (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node, token)!; - // Filter the types with the target attribute - IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> typeSymbolsWithAttributeData = - typeSymbols - .Select((item, _) => ( - Symbol: item, - Attribute: item.GetAttributes().FirstOrDefault(a => a.AttributeClass?.HasFullyQualifiedName(this.attributeType) == true))) - .Where(static item => item.Attribute is not null)!; + // Filter the types with the target attribute + if (!typeSymbol.TryGetAttributeWithFullyQualifiedName(this.attributeType, out AttributeData? attributeData)) + { + return default; + } - // Transform the input data - IncrementalValuesProvider<(INamedTypeSymbol Symbol, TInfo Info)> typeSymbolsWithInfo = GetInfo(context, typeSymbolsWithAttributeData); + // Gather all generation info, and any diagnostics + TInfo? info = ValidateTargetTypeAndGetInfo(typeSymbol, attributeData, context.SemanticModel.Compilation, out ImmutableArray diagnostics); - // Gather all generation info, and any diagnostics - IncrementalValuesProvider> generationInfoWithErrors = - typeSymbolsWithInfo.Select((item, _) => - { - if (ValidateTargetType(item.Symbol, item.Info, out ImmutableArray diagnostics)) - { - return new Result<(HierarchyInfo, bool, TInfo)>( - (HierarchyInfo.From(item.Symbol), item.Symbol.IsSealed, item.Info), - ImmutableArray.Empty); - } + // If there are any diagnostics, there's no need to compute the hierarchy info at all, just return them + if (diagnostics.Length > 0) + { + return new Result<(HierarchyInfo, bool, TInfo?)>(default, diagnostics); + } + + HierarchyInfo hierarchy = HierarchyInfo.From(typeSymbol); - return new Result<(HierarchyInfo, bool, TInfo)>(default, diagnostics); - }); + return new Result<(HierarchyInfo, bool, TInfo?)>((hierarchy, typeSymbol.IsSealed, info), diagnostics); + }) + .Where(static item => item is not null)!; // Emit the diagnostic, if needed context.ReportDiagnostics(generationInfoWithErrors.Select(static (item, _) => item.Errors)); @@ -118,7 +111,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) IncrementalValuesProvider<(HierarchyInfo Hierarchy, bool IsSealed, TInfo Info)> generationInfo = generationInfoWithErrors .Where(static item => item.Errors.IsEmpty) - .Select(static (item, _) => item.Value) + .Select(static (item, _) => item.Value)! .WithComparers(HierarchyInfo.Comparer.Default, EqualityComparer.Default, this.comparer); // Generate the required members @@ -133,23 +126,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } /// - /// Gathers info from a source input. + /// Validates the target type being processes, gets the info if possible and produces all necessary diagnostics. /// - /// The instance in use. - /// The source input. - /// A transformed instance with the gathered data. - protected abstract IncrementalValuesProvider<(INamedTypeSymbol Symbol, TInfo Info)> GetInfo( - IncrementalGeneratorInitializationContext context, - IncrementalValuesProvider<(INamedTypeSymbol Symbol, AttributeData AttributeData)> source); - - /// - /// Validates a target type being processed. - /// - /// The instance for the target type. - /// The instance with the current processing info. - /// The resulting diagnostics from the processing operation. - /// Whether or not the target type is valid and can be processed normally. - protected abstract bool ValidateTargetType(INamedTypeSymbol typeSymbol, TInfo info, out ImmutableArray diagnostics); + /// The instance currently being processed. + /// The instance for the attribute used over . + /// The compilation that belongs to. + /// The resulting diagnostics, if any. + /// The extracted info for the current type, if possible. + /// If is empty, the returned info will always be ignored and no sources will be produced. + protected abstract TInfo? ValidateTargetTypeAndGetInfo(INamedTypeSymbol typeSymbol, AttributeData attributeData, Compilation compilation, out ImmutableArray diagnostics); /// /// Filters the nodes to generate from the input parsed tree.