From b85cfff79dc23a5a8e4350a2edd1d2f61cd193a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Wed, 25 Dec 2024 21:09:17 +0100 Subject: [PATCH] Handle override, virtual and abstract property modifiers --- ...artialReactive_DerivedExtends.verified.txt | 2 +- ...ts.ClassWithVirtualProperties.verified.txt | 2 +- ...sWithInheritanceAndInterfaces.verified.txt | 4 +- ReactiveGenerator/ReactiveGenerator.cs | 79 ++++++++++++++----- 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/ReactiveGenerator.Tests/Snapshots/CrossAssemblyTests.BaseHasPartialReactive_DerivedExtends.verified.txt b/ReactiveGenerator.Tests/Snapshots/CrossAssemblyTests.BaseHasPartialReactive_DerivedExtends.verified.txt index 48eae67..aa81125 100644 --- a/ReactiveGenerator.Tests/Snapshots/CrossAssemblyTests.BaseHasPartialReactive_DerivedExtends.verified.txt +++ b/ReactiveGenerator.Tests/Snapshots/CrossAssemblyTests.BaseHasPartialReactive_DerivedExtends.verified.txt @@ -30,7 +30,7 @@ namespace MainLib private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp)); private static readonly PropertyChangedEventArgs _derivedPropChangedEventArgs = new PropertyChangedEventArgs(nameof(DerivedProp)); - public partial string VirtualProp + public override partial string VirtualProp { get => field; set diff --git a/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.ClassWithVirtualProperties.verified.txt b/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.ClassWithVirtualProperties.verified.txt index 96ddd99..2e4101f 100644 --- a/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.ClassWithVirtualProperties.verified.txt +++ b/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.ClassWithVirtualProperties.verified.txt @@ -64,7 +64,7 @@ public partial class TestClass private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp)); private static readonly PropertyChangedEventArgs _nonVirtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(NonVirtualProp)); - public partial string VirtualProp + public virtual partial string VirtualProp { get => field; set diff --git a/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.NestedClassesWithInheritanceAndInterfaces.verified.txt b/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.NestedClassesWithInheritanceAndInterfaces.verified.txt index d18d1b2..1ebe581 100644 --- a/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.NestedClassesWithInheritanceAndInterfaces.verified.txt +++ b/ReactiveGenerator.Tests/Snapshots/ReactiveGeneratorTests.NestedClassesWithInheritanceAndInterfaces.verified.txt @@ -46,7 +46,7 @@ public partial class Container { private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp)); - public partial string VirtualProp + public virtual partial string VirtualProp { get => field; set @@ -81,7 +81,7 @@ public partial class Container private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp)); private static readonly PropertyChangedEventArgs _regularPropChangedEventArgs = new PropertyChangedEventArgs(nameof(RegularProp)); - public partial string VirtualProp + public override partial string VirtualProp { get => field; set diff --git a/ReactiveGenerator/ReactiveGenerator.cs b/ReactiveGenerator/ReactiveGenerator.cs index 55d41ea..7043746 100644 --- a/ReactiveGenerator/ReactiveGenerator.cs +++ b/ReactiveGenerator/ReactiveGenerator.cs @@ -11,11 +11,26 @@ namespace ReactiveGenerator; [Generator] public class ReactiveGenerator : IIncrementalGenerator { - private record PropertyInfo( + private record PropertyInfo( IPropertySymbol Property, bool HasReactiveAttribute, bool HasIgnoreAttribute, - bool HasImplementation); + bool HasImplementation) + { + public string GetPropertyModifiers() + { + var modifiers = new List(); + + if (Property.IsOverride) + modifiers.Add("override"); + else if (Property.IsVirtual) + modifiers.Add("virtual"); + else if (Property.IsAbstract) + modifiers.Add("abstract"); + + return string.Join(" ", modifiers); + } + } public void Initialize(IncrementalGeneratorInitializationContext context) { @@ -342,33 +357,37 @@ private static void Execute( processedTypes.Add(type); } } - - private static bool HasINPCImplementation(Compilation compilation, INamedTypeSymbol typeSymbol, - HashSet processedTypes) + + private static bool HasINPCImplementation(Compilation compilation, INamedTypeSymbol typeSymbol, HashSet processedTypes) { var inpcType = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged"); if (inpcType is null) return false; - // First check if current type implements INPC directly - if (typeSymbol.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default)) - return true; - - // Check if current type is in processedTypes (will have INPC implemented) - if (processedTypes.Contains(typeSymbol)) + // First check if current type implements INPC directly or is in processedTypes + if (typeSymbol.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default) || + processedTypes.Contains(typeSymbol)) return true; // Check base types recursively var current = typeSymbol.BaseType; while (current is not null) { - // Check if base type implements INPC directly - if (current.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default)) - return true; - - // Check if base type is in processedTypes - if (processedTypes.Contains(current)) - return true; + // If base type is in same assembly + if (current.ContainingAssembly == typeSymbol.ContainingAssembly) + { + // Check for INPC implementation or presence in processedTypes + if (current.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default) || + processedTypes.Contains(current)) + return true; + } + // If base type is in different assembly + else + { + // For external types, only check for actual INPC implementation + if (current.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default)) + return true; + } current = current.BaseType; } @@ -767,7 +786,15 @@ private static void GenerateLegacyProperty( var getterAccessibility = GetAccessorAccessibility(property.GetMethod); var setterAccessibility = GetAccessorAccessibility(property.SetMethod); - sb.AppendLine($"{indent}{propertyAccessibility} partial {propertyType} {propertyName}"); + var propInfo = new PropertyInfo(property, false, false, false); + var modifiers = propInfo.GetPropertyModifiers(); + + var declarationModifiers = new List { propertyAccessibility }; + if (!string.IsNullOrEmpty(modifiers)) + declarationModifiers.Add(modifiers); + declarationModifiers.Add("partial"); + + sb.AppendLine($"{indent}{string.Join(" ", declarationModifiers)} {propertyType} {propertyName}"); sb.AppendLine($"{indent}{{"); if (isReactiveObject) @@ -817,8 +844,18 @@ private static void GenerateFieldKeywordProperty( var propertyType = GetPropertyTypeWithNullability(property); var getterAccessibility = GetAccessorAccessibility(property.GetMethod); var setterAccessibility = GetAccessorAccessibility(property.SetMethod); - - sb.AppendLine($"{indent}{propertyAccessibility} partial {propertyType} {propertyName}"); + + // Create PropertyInfo to get modifiers + var propInfo = new PropertyInfo(property, false, false, false); + var modifiers = propInfo.GetPropertyModifiers(); + + // Combine modifiers with accessibility and partial + var declarationModifiers = new List { propertyAccessibility }; + if (!string.IsNullOrEmpty(modifiers)) + declarationModifiers.Add(modifiers); + declarationModifiers.Add("partial"); + + sb.AppendLine($"{indent}{string.Join(" ", declarationModifiers)} {propertyType} {propertyName}"); sb.AppendLine($"{indent}{{"); if (isReactiveObject)