From 4cdce0038e27d785c2e5f587e27d5951e7ba524d Mon Sep 17 00:00:00 2001 From: Thomas Claudius Huber Date: Sun, 10 Dec 2023 15:23:29 +0100 Subject: [PATCH] Generate correct constructor when inheriting from another ViewModel with injections --- .../Model/ViewModelToGenerateTests.cs | 51 ++++++++++++++++- .../InjectAttributeTests.cs | 56 ++++++++++++++++++- .../Generators/ConstructorGenerator.cs | 48 ++++++++++++++-- .../ViewModelInjectAttributeInspector.cs | 7 ++- .../Model/ViewModelToGenerate.cs | 5 +- .../ViewModelGenerator.cs | 1 + 6 files changed, 157 insertions(+), 11 deletions(-) diff --git a/src/MvvmGen.SourceGenerators.Tests/Model/ViewModelToGenerateTests.cs b/src/MvvmGen.SourceGenerators.Tests/Model/ViewModelToGenerateTests.cs index 4517d35..acc0a77 100644 --- a/src/MvvmGen.SourceGenerators.Tests/Model/ViewModelToGenerateTests.cs +++ b/src/MvvmGen.SourceGenerators.Tests/Model/ViewModelToGenerateTests.cs @@ -282,6 +282,49 @@ public void ShouldBeEqual5() Assert.Equal(_viewModelToGenerate1, _viewModelToGenerate2); } + [Fact] + public void ShouldNotBeEqualDifferentBaseClassInjectionsToGenerate1() + { + _viewModelToGenerate2.BaseClassInjectionsToGenerate = new List(); + + Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2); + } + + [Fact] + public void ShouldNotBeEqualDifferentBaseClassInjectionsToGenerate2() + { + var list = (List)_viewModelToGenerate2.BaseClassInjectionsToGenerate!; + + list.Add(new InjectionToGenerate("IDataProvider", "DataProvider") { PropertyAccessModifier = "private" }); + + Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2); + } + + [Fact] + public void ShouldNotBeEqualDifferentBaseClassInjectionsToGenerate3() + { + var list = (List)_viewModelToGenerate2.BaseClassInjectionsToGenerate!; + + var originalInjectionToGenerate = list[0]; + + list.Clear(); + list.Add(new InjectionToGenerate("IChangedDataProvider", originalInjectionToGenerate.PropertyName) + { + PropertyAccessModifier = originalInjectionToGenerate.PropertyAccessModifier + }); + + Assert.NotEqual(_viewModelToGenerate1, _viewModelToGenerate2); + } + + [Fact] + public void ShouldBeEqual6() + { + _viewModelToGenerate1.BaseClassInjectionsToGenerate = null; + _viewModelToGenerate2.BaseClassInjectionsToGenerate = null; + + Assert.Equal(_viewModelToGenerate1, _viewModelToGenerate2); + } + [Fact] public void ShouldNotBeEqualDifferentViewModelFactoryToGenerate1() { @@ -299,7 +342,7 @@ public void ShouldNotBeEqualDifferentViewModelFactoryToGenerate2() } [Fact] - public void ShouldBeEqual6() + public void ShouldBeEqual7() { _viewModelToGenerate1.ViewModelFactoryToGenerate = null; _viewModelToGenerate2.ViewModelFactoryToGenerate = null; @@ -324,7 +367,7 @@ public void ShouldNotBeEqualDifferentViewModelInterfaceToGenerate2() } [Fact] - public void ShouldBeEqual7() + public void ShouldBeEqual8() { _viewModelToGenerate1.ViewModelInterfaceToGenerate = null; _viewModelToGenerate2.ViewModelInterfaceToGenerate = null; @@ -360,6 +403,10 @@ private static void FillAllProperties(ViewModelToGenerate viewModelToGenerate) new InjectionToGenerate("IEventAggregator","EventAggregator"){ PropertyAccessModifier ="public"} }; + viewModelToGenerate.BaseClassInjectionsToGenerate = new List { + new InjectionToGenerate("IEventAggregator","EventAggregator"){ PropertyAccessModifier ="public"} + }; + viewModelToGenerate.ViewModelFactoryToGenerate = new FactoryToGenerate("FactoryClassName", "FactoryInterfaceName", "CustomReturnType"); viewModelToGenerate.ViewModelInterfaceToGenerate = new InterfaceToGenerate("InterfaceName") { diff --git a/src/MvvmGen.SourceGenerators.Tests/ViewModelGeneratorTests/InjectAttributeTests.cs b/src/MvvmGen.SourceGenerators.Tests/ViewModelGeneratorTests/InjectAttributeTests.cs index da4b314..05e7a9f 100644 --- a/src/MvvmGen.SourceGenerators.Tests/ViewModelGeneratorTests/InjectAttributeTests.cs +++ b/src/MvvmGen.SourceGenerators.Tests/ViewModelGeneratorTests/InjectAttributeTests.cs @@ -107,7 +107,7 @@ public void GeneratePropertyOfInjectedType2() namespace MyCode {{ - [Inject(typeof(MvvmGen.Events.IEventAggregator)))] + [Inject(typeof(MvvmGen.Events.IEventAggregator))] [ViewModel] public partial class EmployeeViewModel {{ @@ -258,5 +258,59 @@ public EmployeeViewModel(MyCode.IDataProvider dataProvider1, My }} "); } + + [Fact] + public void GenerateConstructorForBaseClassCall() + { + ShouldGenerateExpectedCode( + $@"using MvvmGen; + +namespace MyCode +{{ + [Inject(typeof(MvvmGen.Events.IEventAggregator))] + [ViewModel] + public partial class MyBaseViewModel + {{ + }} + + [ViewModel + public partial class EmployeeViewModel : MyBaseViewModel + {{ + }} +}}", + $@"{AutoGeneratedTopContent} + +namespace MyCode +{{ + partial class MyBaseViewModel : global::MvvmGen.ViewModels.ViewModelBase + {{ + public MyBaseViewModel(MvvmGen.Events.IEventAggregator eventAggregator) + {{ + this.EventAggregator = eventAggregator; + this.OnInitialize(); + }} + + partial void OnInitialize(); + + protected MvvmGen.Events.IEventAggregator EventAggregator {{ get; private set; }} + }} +}} +", + $@"{AutoGeneratedTopContent} + +namespace MyCode +{{ + partial class EmployeeViewModel + {{ + public EmployeeViewModel(MvvmGen.Events.IEventAggregator eventAggregator) : base(eventAggregator) + {{ + this.OnInitialize(); + }} + + partial void OnInitialize(); + }} +}} +" ); + } } } diff --git a/src/MvvmGen.SourceGenerators/Generators/ConstructorGenerator.cs b/src/MvvmGen.SourceGenerators/Generators/ConstructorGenerator.cs index 5664de6..75d4b9a 100644 --- a/src/MvvmGen.SourceGenerators/Generators/ConstructorGenerator.cs +++ b/src/MvvmGen.SourceGenerators/Generators/ConstructorGenerator.cs @@ -19,22 +19,26 @@ internal static void GenerateConstructor(this ViewModelBuilder vmBuilder, ViewMo { Generate(vmBuilder, viewModelToGenerate.ClassName, viewModelToGenerate.InjectionsToGenerate, + viewModelToGenerate.BaseClassInjectionsToGenerate, viewModelToGenerate.IsEventSubscriber); } } private static void Generate(ViewModelBuilder vmBuilder, string viewModelClassName, - IEnumerable? injectionsToGenerate, bool isEventSubscriber) + IEnumerable? directInjectionsToGenerate, + IEnumerable? baseClassInjectionsToGenerate, + bool isEventSubscriber) { vmBuilder.AppendLineBeforeMember(); vmBuilder.Append($"public {viewModelClassName}("); - injectionsToGenerate ??= Enumerable.Empty(); + directInjectionsToGenerate ??= Enumerable.Empty(); + baseClassInjectionsToGenerate ??= Enumerable.Empty(); var first = true; string? eventAggregatorAccessForSubscription = null; if (isEventSubscriber) { - var eventAggregatorInjection = injectionsToGenerate.FirstOrDefault(x => x.Type == "MvvmGen.Events.IEventAggregator"); + var eventAggregatorInjection = directInjectionsToGenerate.FirstOrDefault(x => x.Type == "MvvmGen.Events.IEventAggregator"); if (eventAggregatorInjection is not null) { eventAggregatorAccessForSubscription = $"this.{eventAggregatorInjection.PropertyName}"; @@ -47,7 +51,7 @@ private static void Generate(ViewModelBuilder vmBuilder, string viewModelClassNa } } - foreach (var injectionToGenerate in injectionsToGenerate) + foreach (var injectionToGenerate in directInjectionsToGenerate) { if (!first) { @@ -57,10 +61,42 @@ private static void Generate(ViewModelBuilder vmBuilder, string viewModelClassNa vmBuilder.Append($"{injectionToGenerate.Type} {injectionToGenerate.PropertyName.ToCamelCase()}"); } - vmBuilder.AppendLine(")"); + var hasBaseClassInjections = false; + foreach (var injectionToGenerate in baseClassInjectionsToGenerate) + { + hasBaseClassInjections = true; + if (!first) + { + vmBuilder.Append(", "); + } + first = false; + vmBuilder.Append($"{injectionToGenerate.Type} {injectionToGenerate.PropertyName.ToCamelCase()}"); + } + + vmBuilder.Append(")"); + + if (hasBaseClassInjections) + { + vmBuilder.Append(" : base("); + + first = true; + foreach (var injectionToGenerate in baseClassInjectionsToGenerate) + { + if (!first) + { + vmBuilder.Append(", "); + } + first = false; + vmBuilder.Append(injectionToGenerate.PropertyName.ToCamelCase()); + } + + vmBuilder.Append(")"); + } + + vmBuilder.AppendLine(); vmBuilder.AppendLine("{"); vmBuilder.IncreaseIndent(); - foreach (var injectionToGenerate in injectionsToGenerate) + foreach (var injectionToGenerate in directInjectionsToGenerate) { vmBuilder.AppendLine($"this.{injectionToGenerate.PropertyName} = {injectionToGenerate.PropertyName.ToCamelCase()};"); } diff --git a/src/MvvmGen.SourceGenerators/Inspectors/ViewModelInjectAttributeInspector.cs b/src/MvvmGen.SourceGenerators/Inspectors/ViewModelInjectAttributeInspector.cs index 61db574..bc2c39e 100644 --- a/src/MvvmGen.SourceGenerators/Inspectors/ViewModelInjectAttributeInspector.cs +++ b/src/MvvmGen.SourceGenerators/Inspectors/ViewModelInjectAttributeInspector.cs @@ -13,8 +13,13 @@ namespace MvvmGen.Inspectors { internal static class ViewModelInjectAttributeInspector { - internal static IEnumerable Inspect(INamedTypeSymbol viewModelClassSymbol) + internal static IEnumerable Inspect(INamedTypeSymbol? viewModelClassSymbol) { + if(viewModelClassSymbol is null) + { + return Enumerable.Empty(); + } + List injectionsToGenerate = new(); var injectAttributeDatas = viewModelClassSymbol.GetAttributes() .Where(x => x.AttributeClass?.ToDisplayString() == "MvvmGen.InjectAttribute") diff --git a/src/MvvmGen.SourceGenerators/Model/ViewModelToGenerate.cs b/src/MvvmGen.SourceGenerators/Model/ViewModelToGenerate.cs index 25eba33..ba4fca5 100644 --- a/src/MvvmGen.SourceGenerators/Model/ViewModelToGenerate.cs +++ b/src/MvvmGen.SourceGenerators/Model/ViewModelToGenerate.cs @@ -45,6 +45,8 @@ public ViewModelToGenerate(string className, string namespaceName) public IEnumerable? InjectionsToGenerate { get; set; } + public IEnumerable? BaseClassInjectionsToGenerate { get; set; } + public FactoryToGenerate? ViewModelFactoryToGenerate { get; set; } public InterfaceToGenerate? ViewModelInterfaceToGenerate { get; set; } @@ -70,7 +72,8 @@ public bool Equals(ViewModelToGenerate? other) CommandsToGenerate.SequenceEqualWithNullCheck(other.CommandsToGenerate) && CommandInvalidationsToGenerate.SequenceEqualWithNullCheck(other.CommandInvalidationsToGenerate) && PropertiesToGenerate.SequenceEqualWithNullCheck(other.PropertiesToGenerate) && - InjectionsToGenerate.SequenceEqualWithNullCheck(other.InjectionsToGenerate); + InjectionsToGenerate.SequenceEqualWithNullCheck(other.InjectionsToGenerate) && + BaseClassInjectionsToGenerate.SequenceEqualWithNullCheck(other.BaseClassInjectionsToGenerate); } public override int GetHashCode() diff --git a/src/MvvmGen.SourceGenerators/ViewModelGenerator.cs b/src/MvvmGen.SourceGenerators/ViewModelGenerator.cs index 64520af..ea9cd3e 100644 --- a/src/MvvmGen.SourceGenerators/ViewModelGenerator.cs +++ b/src/MvvmGen.SourceGenerators/ViewModelGenerator.cs @@ -81,6 +81,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { ClassAccessModifier = accessModifier, InjectionsToGenerate = ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol), + BaseClassInjectionsToGenerate=ViewModelInjectAttributeInspector.Inspect(viewModelClassSymbol.BaseType), GenerateConstructor = ViewModelAttributeInspector.InspectGenerateConstructor(viewModelAttributeData), ViewModelFactoryToGenerate = ViewModelGenerateFactoryAttributeInspector.Inspect(viewModelClassSymbol), InheritFromViewModelBase = ViewModelBaseClassInspector.Inspect(viewModelClassSymbol, viewModelBaseClassSymbol),