// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Immutable; using System.Linq; using CommunityToolkit.Mvvm.SourceGenerators.Diagnostics; using CommunityToolkit.Mvvm.SourceGenerators.Extensions; using CommunityToolkit.Mvvm.SourceGenerators.Input.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; namespace CommunityToolkit.Mvvm.SourceGenerators; /// /// A source generator for the INotifyPropertyChangedAttribute type. /// [Generator(LanguageNames.CSharp)] public sealed class INotifyPropertyChangedGenerator : TransitiveMembersGenerator { /// /// Initializes a new instance of the class. /// public INotifyPropertyChangedGenerator() : base("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute") { } /// 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) { ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); // 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; } // Check if the type uses [INotifyPropertyChanged] or [ObservableObject] already (in the type hierarchy too) if (typeSymbol.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableObjectAttribute") || typeSymbol.InheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute")) { builder.Add(InvalidAttributeCombinationForINotifyPropertyChangedAttributeError, typeSymbol, typeSymbol); diagnostics = builder.ToImmutable(); return false; } diagnostics = builder.ToImmutable(); return true; } /// protected override ImmutableArray FilterDeclaredMembers(INotifyPropertyChangedInfo info, ImmutableArray memberDeclarations) { // If requested, only include the event and the basic methods to raise it, but not the additional helpers if (!info.IncludeAdditionalHelperMethods) { return memberDeclarations.Where(static member => member is EventFieldDeclarationSyntax or MethodDeclarationSyntax { Identifier.ValueText: "OnPropertyChanged" }).ToImmutableArray(); } return memberDeclarations; } }