Skip to content

Commit 2689986

Browse files
authored
Merge pull request #120 from CommunityToolkit/dev/fix-messenger-generator-duplicates
Fix IRecipient generator for partial recipient declarations
2 parents df3b529 + 1a24e0d commit 2689986

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

CommunityToolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,21 @@ public sealed partial class IMessengerRegisterAllGenerator : IIncrementalGenerat
2323
/// <inheritdoc/>
2424
public void Initialize(IncrementalGeneratorInitializationContext context)
2525
{
26-
// Get all class declarations
26+
// Get all class declarations. This pipeline step also needs to filter out duplicate recipient
27+
// definitions (it might happen if a recipient has partial declarations). To do this, all pairs
28+
// of class declarations and associated symbols are gathered, and then only the pair where the
29+
// class declaration is the first syntax reference for the associated symbol is kept.
2730
IncrementalValuesProvider<INamedTypeSymbol> typeSymbols =
2831
context.SyntaxProvider
2932
.CreateSyntaxProvider(
3033
static (node, _) => node is ClassDeclarationSyntax,
31-
static (context, _) => (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!);
34+
static (context, _) => (context.Node, Symbol: (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!))
35+
.Where(static item =>
36+
item.Symbol.DeclaringSyntaxReferences.Length > 0 &&
37+
item.Symbol.DeclaringSyntaxReferences[0] is SyntaxReference syntaxReference &&
38+
syntaxReference.SyntaxTree == item.Node.SyntaxTree &&
39+
syntaxReference.Span == item.Node.Span)
40+
.Select(static (item, _) => item.Symbol);
3241

3342
// Get the target IRecipient<TMessage> interfaces and filter out other types
3443
IncrementalValuesProvider<(INamedTypeSymbol Type, ImmutableArray<INamedTypeSymbol> Interfaces)> typeAndInterfaceSymbols =

tests/CommunityToolkit.Mvvm.UnitTests/Test_IRecipientGenerator.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ public partial class Test_IRecipientGenerator
1616
[TestMethod]
1717
public void Test_IRecipientGenerator_GeneratedRegistration()
1818
{
19-
StrongReferenceMessenger? messenger = new();
20-
RecipientWithSomeMessages? recipient = new();
19+
StrongReferenceMessenger messenger = new();
20+
RecipientWithSomeMessages recipient = new();
2121

22-
MessageA? messageA = new();
23-
MessageB? messageB = new();
22+
MessageA messageA = new();
23+
MessageB messageB = new();
2424

2525
Action<IMessenger, object, int> registrator = Messaging.__Internals.__IMessengerExtensions.CreateAllMessagesRegistratorWithToken<int>(recipient);
2626

@@ -43,6 +43,15 @@ public void Test_IRecipientGenerator_GeneratedRegistration()
4343
Assert.AreSame(recipient.B, messageB);
4444
}
4545

46+
[TestMethod]
47+
public void Test_IRecipientGenerator_TypeWithMultipleClassDeclarations()
48+
{
49+
RecipientWithMultipleClassDeclarations recipient = new();
50+
51+
// This test really just needs to verify this compiles and executes normally
52+
_ = Messaging.__Internals.__IMessengerExtensions.CreateAllMessagesRegistratorWithToken<int>(recipient);
53+
}
54+
4655
public sealed class RecipientWithSomeMessages :
4756
IRecipient<MessageA>,
4857
IRecipient<MessageB>
@@ -69,4 +78,17 @@ public sealed class MessageA
6978
public sealed class MessageB
7079
{
7180
}
81+
82+
public sealed partial class RecipientWithMultipleClassDeclarations : IRecipient<MessageA>
83+
{
84+
public void Receive(MessageA message)
85+
{
86+
}
87+
}
88+
89+
// This empty partial type declarations needs to be present to ensure the generator
90+
// correctly handles cases where the source type has multiple class declarations.
91+
partial class RecipientWithMultipleClassDeclarations
92+
{
93+
}
7294
}

0 commit comments

Comments
 (0)