Skip to content

Commit 83d4ee2

Browse files
authored
Merge pull request #260 from CommunityToolkit/dev/disable-generation-for-abstracts
Disable source generators for abstract types
2 parents b8db4b0 + d61796f commit 83d4ee2

File tree

4 files changed

+52
-6
lines changed

4 files changed

+52
-6
lines changed

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ public sealed partial class ObservableValidatorValidateAllPropertiesGenerator :
2020
/// <inheritdoc/>
2121
public void Initialize(IncrementalGeneratorInitializationContext context)
2222
{
23-
// Get all class declarations
23+
// Get all class declarations. We intentionally skip generating code for abstract types, as that would never be used.
24+
// The methods that are generated by this generator are retrieved through reflection using the type of the invoking
25+
// instance as discriminator, which means a type that is abstract could never be used (since it couldn't be instantiated).
2426
IncrementalValuesProvider<INamedTypeSymbol> typeSymbols =
2527
context.SyntaxProvider
2628
.CreateSyntaxProvider(
2729
static (node, _) => node is ClassDeclarationSyntax,
2830
static (context, _) => (context.Node, Symbol: (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!))
29-
.Where(static item => item.Node.IsFirstSyntaxDeclarationForSymbol(item.Symbol))
31+
.Where(static item => !item.Symbol.IsAbstract && item.Node.IsFirstSyntaxDeclarationForSymbol(item.Symbol))
3032
.Select(static (item, _) => item.Symbol);
3133

3234
// Get the types that inherit from ObservableValidator and gather their info

CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
2525
// definitions (it might happen if a recipient has partial declarations). To do this, all pairs
2626
// of class declarations and associated symbols are gathered, and then only the pair where the
2727
// class declaration is the first syntax reference for the associated symbol is kept.
28+
// Just like with the ObservableValidator generator, we also intentionally skip abstract types.
2829
IncrementalValuesProvider<INamedTypeSymbol> typeSymbols =
2930
context.SyntaxProvider
3031
.CreateSyntaxProvider(
3132
static (node, _) => node is ClassDeclarationSyntax,
3233
static (context, _) => (context.Node, Symbol: (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!))
33-
.Where(static item => item.Node.IsFirstSyntaxDeclarationForSymbol(item.Symbol))
34+
.Where(static item => !item.Symbol.IsAbstract && item.Node.IsFirstSyntaxDeclarationForSymbol(item.Symbol))
3435
.Select(static (item, _) => item.Symbol);
3536

3637
// Get the target IRecipient<TMessage> interfaces and filter out other types

tests/CommunityToolkit.Mvvm.UnitTests/Test_IRecipientGenerator.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Linq;
7+
using System.Reflection;
68
using CommunityToolkit.Mvvm.Messaging;
79
using Microsoft.VisualStudio.TestTools.UnitTesting;
810

@@ -52,9 +54,20 @@ public void Test_IRecipientGenerator_TypeWithMultipleClassDeclarations()
5254
_ = Messaging.__Internals.__IMessengerExtensions.CreateAllMessagesRegistratorWithToken<int>(recipient);
5355
}
5456

55-
public sealed class RecipientWithSomeMessages :
56-
IRecipient<MessageA>,
57-
IRecipient<MessageB>
57+
[TestMethod]
58+
public void Test_IRecipientGenerator_AbstractTypesDoNotTriggerCodeGeneration()
59+
{
60+
MethodInfo? createAllPropertiesValidatorMethod = typeof(Messaging.__Internals.__IMessengerExtensions)
61+
.GetMethods(BindingFlags.Static | BindingFlags.Public)
62+
.Where(static m => m.Name == "CreateAllMessagesRegistratorWithToken")
63+
.Where(static m => m.GetParameters() is { Length: 1 } parameters && parameters[0].ParameterType == typeof(AbstractModelWithValidatablePropertyIRecipientInterfaces))
64+
.FirstOrDefault();
65+
66+
// We need to validate that no methods are generated for abstract types, so we just check this method doesn't exist
67+
Assert.IsNull(createAllPropertiesValidatorMethod);
68+
}
69+
70+
public sealed class RecipientWithSomeMessages : IRecipient<MessageA>, IRecipient<MessageB>
5871
{
5972
public MessageA? A { get; private set; }
6073

@@ -91,4 +104,14 @@ public void Receive(MessageA message)
91104
partial class RecipientWithMultipleClassDeclarations
92105
{
93106
}
107+
108+
public abstract class AbstractModelWithValidatablePropertyIRecipientInterfaces : IRecipient<MessageA>, IRecipient<MessageB>
109+
{
110+
public abstract void Receive(MessageA message);
111+
112+
public void Receive(MessageB message)
113+
{
114+
115+
}
116+
}
94117
}

tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservableValidator.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,19 @@ public void Test_ObservableValidator_VerifyTrimmingAnnotation()
526526
#endif
527527
}
528528

529+
[TestMethod]
530+
public void Test_ObservableRecipient_AbstractTypesDoNotTriggerCodeGeneration()
531+
{
532+
MethodInfo? createAllPropertiesValidatorMethod = typeof(ComponentModel.__Internals.__ObservableValidatorExtensions)
533+
.GetMethods(BindingFlags.Static | BindingFlags.Public)
534+
.Where(static m => m.Name == "CreateAllPropertiesValidator")
535+
.Where(static m => m.GetParameters() is { Length: 1 } parameters && parameters[0].ParameterType == typeof(AbstractModelWithValidatableProperty))
536+
.FirstOrDefault();
537+
538+
// We need to validate that no methods are generated for abstract types, so we just check this method doesn't exist
539+
Assert.IsNull(createAllPropertiesValidatorMethod);
540+
}
541+
529542
public class Person : ObservableValidator
530543
{
531544
private string? name;
@@ -777,4 +790,11 @@ public partial class PersonWithPartialDeclaration
777790
[Range(10, 1000)]
778791
public int Number { get; set; }
779792
}
793+
794+
public abstract class AbstractModelWithValidatableProperty : ObservableValidator
795+
{
796+
[Required]
797+
[MinLength(2)]
798+
public string? Name { get; set; }
799+
}
780800
}

0 commit comments

Comments
 (0)