Skip to content

Commit 4bbac4b

Browse files
Get rid of open generics registrations for generic notification handlers (#199)
1 parent d4ab892 commit 4bbac4b

File tree

49 files changed

+1876
-545
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1876
-545
lines changed

src/Mediator.SourceGenerator/Implementation/Analysis/CompilationAnalyzer.cs

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -506,28 +506,17 @@ private void PopulateMetadata(Queue<INamespaceOrTypeSymbol> queue)
506506

507507
foreach (var notificationMessage in _notificationMessages)
508508
{
509-
var handlerInterface = _notificationHandlerInterfaceSymbol.Construct(notificationMessage.Symbol);
510-
509+
var isHandled = false;
511510
foreach (var notificationMessageHandler in _notificationMessageHandlers)
512-
{
513-
if (notificationMessageHandler.IsOpenGeneric) // These are added as open generics
514-
continue;
511+
isHandled |= notificationMessageHandler.TryAddMessage(notificationMessage);
515512

516-
if (compilation.HasImplicitConversion(notificationMessageHandler.Symbol, handlerInterface))
517-
notificationMessage.AddHandlers(notificationMessageHandler);
513+
if (!isHandled)
514+
{
515+
ReportDiagnostic(
516+
notificationMessage.Symbol,
517+
(in CompilationAnalyzerContext c, INamedTypeSymbol s) => c.ReportMessageWithoutHandler(s)
518+
);
518519
}
519-
520-
// This diagnostic is not safe to use here.
521-
// A user can define a notification, expecting it to only
522-
// show up in an open generic handler.
523-
// We don't keep track of bindings between notification and
524-
// these open generic handlers, so we can't know what notifications
525-
// are and aren't handled just yet.
526-
// TODO - include open generic handlers in analysis as well, so that we can report this correctly.
527-
//if (notificationMessage.HandlerCount == 0)
528-
//{
529-
// ReportDiagnostic(notificationMessage.Symbol, (in GeneratorExecutionContext c, INamedTypeSymbol s) => c.ReportMessageWithoutHandler(s));
530-
//}
531520
}
532521

533522
const int NOT_RELEVANT = 0;
@@ -662,7 +651,9 @@ bool ProcessInterface(
662651
{
663652
return true;
664653
}
665-
_notificationMessageHandlers.Add(new NotificationMessageHandler(typeSymbol, this));
654+
_notificationMessageHandlers.Add(
655+
new NotificationMessageHandler(typeSymbol, _notificationHandlerInterfaceSymbol, this)
656+
);
666657
}
667658
}
668659
break;
@@ -1197,8 +1188,7 @@ CancellationToken cancellationToken
11971188
: _streamPipelineBehaviorInterfaceSymbol!;
11981189
if (
11991190
!pipelineTypeSymbol.AllInterfaces.Any(i =>
1200-
i.ConstructUnboundGenericType()
1201-
.Equals(interfaceSymbol.ConstructUnboundGenericType(), _symbolComparer)
1191+
i.IsGenericType && i.OriginalDefinition.Equals(interfaceSymbol, _symbolComparer)
12021192
)
12031193
)
12041194
{

src/Mediator.SourceGenerator/Implementation/Analysis/MessageHandler.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,5 @@ internal abstract class MessageHandler<T> : SymbolMetadata<MessageHandler<T>>
77
protected MessageHandler(INamedTypeSymbol symbol, CompilationAnalyzer analyzer)
88
: base(symbol, analyzer) { }
99

10-
public bool IsOpenGeneric => Symbol.TypeArguments.Length > 0;
11-
1210
public string FullName => Symbol.GetTypeSymbolFullName();
1311
}

src/Mediator.SourceGenerator/Implementation/Analysis/NotificationMessage.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,11 @@ namespace Mediator.SourceGenerator;
22

33
internal sealed class NotificationMessage : SymbolMetadata<NotificationMessage>
44
{
5-
private readonly HashSet<NotificationMessageHandler> _handlers;
6-
75
public NotificationMessage(INamedTypeSymbol symbol, CompilationAnalyzer analyzer)
8-
: base(symbol, analyzer)
9-
{
10-
_handlers = new();
11-
}
12-
13-
internal void AddHandlers(NotificationMessageHandler handler) => _handlers.Add(handler);
6+
: base(symbol, analyzer) { }
147

158
public NotificationMessageModel ToModel()
169
{
17-
return new NotificationMessageModel(Symbol, Analyzer, _handlers);
10+
return new NotificationMessageModel(Symbol, Analyzer);
1811
}
1912
}
Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,57 @@
1+
using Mediator.SourceGenerator.Extensions;
2+
13
namespace Mediator.SourceGenerator;
24

35
internal sealed class NotificationMessageHandler : MessageHandler<NotificationMessageHandler>
46
{
5-
public NotificationMessageHandler(INamedTypeSymbol symbol, CompilationAnalyzer analyzer)
6-
: base(symbol, analyzer) { }
7+
public readonly INamedTypeSymbol InterfaceSymbol;
8+
public readonly INamedTypeSymbol UnconstructedInterfaceSymbol;
9+
private readonly List<NotificationMessage> _messages;
10+
11+
public IReadOnlyList<NotificationMessage> Messages => _messages;
12+
13+
public NotificationMessageHandler(
14+
INamedTypeSymbol symbol,
15+
INamedTypeSymbol interfaceSymbol,
16+
CompilationAnalyzer analyzer
17+
)
18+
: base(symbol, analyzer)
19+
{
20+
UnconstructedInterfaceSymbol = interfaceSymbol;
21+
InterfaceSymbol = symbol.AllInterfaces.Single(i =>
22+
i.IsGenericType && i.OriginalDefinition.Equals(UnconstructedInterfaceSymbol, SymbolEqualityComparer.Default)
23+
);
24+
_messages = new List<NotificationMessage>();
25+
}
26+
27+
public bool TryAddMessage(NotificationMessage message)
28+
{
29+
if (Symbol.IsGenericType && Symbol.TypeParameters.Length == 1)
30+
{
31+
var messageSymbol = message.Symbol;
32+
33+
var compilation = Analyzer.Compilation;
34+
if (Symbol.SatisfiesConstraints([messageSymbol], compilation))
35+
{
36+
_messages.Add(message);
37+
return true;
38+
}
39+
}
40+
else
41+
{
42+
var serviceType = UnconstructedInterfaceSymbol.Construct([message.Symbol]);
43+
if (Analyzer.Compilation.HasImplicitConversion(Symbol, serviceType))
44+
{
45+
_messages.Add(message);
46+
return true;
47+
}
48+
}
49+
50+
return false;
51+
}
752

853
public NotificationMessageHandlerModel ToModel()
954
{
10-
return new NotificationMessageHandlerModel(Symbol, Analyzer);
55+
return new NotificationMessageHandlerModel(this, Analyzer);
1156
}
1257
}

src/Mediator.SourceGenerator/Implementation/Analysis/PipelineBehaviorType.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ public PipelineBehaviorType(INamedTypeSymbol symbol, INamedTypeSymbol interfaceS
1313
: base(symbol, analyzer)
1414
{
1515
InterfaceSymbol = symbol.AllInterfaces.Single(i =>
16-
i.ConstructUnboundGenericType()
17-
.Equals(interfaceSymbol.ConstructUnboundGenericType(), SymbolEqualityComparer.Default)
16+
i.IsGenericType && i.OriginalDefinition.Equals(interfaceSymbol, SymbolEqualityComparer.Default)
1817
);
1918
_messages = new List<RequestMessage>();
2019
}

src/Mediator.SourceGenerator/Implementation/Models/CompilationModel.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public CompilationModel(string mediatorNamespace, string generatorVersion)
2626
RequestMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
2727
NotificationMessages = ImmutableEquatableArray<NotificationMessageModel>.Empty;
2828
NotificationMessageHandlers = ImmutableEquatableArray<NotificationMessageHandlerModel>.Empty;
29-
OpenGenericNotificationMessageHandlers = ImmutableEquatableArray<NotificationMessageHandlerModel>.Empty;
3029
PipelineBehaviors = ImmutableEquatableArray<PipelineBehaviorModel>.Empty;
3130

3231
IRequestMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
@@ -73,8 +72,7 @@ bool configuredViaAttribute
7372

7473
RequestMessageHandlerWrappers = requestMessageHandlerWrappers;
7574
NotificationMessages = notificationMessages;
76-
NotificationMessageHandlers = new(notificationMessageHandlers.Where(h => !h.IsOpenGeneric));
77-
OpenGenericNotificationMessageHandlers = new(notificationMessageHandlers.Where(h => h.IsOpenGeneric));
75+
NotificationMessageHandlers = new(notificationMessageHandlers);
7876

7977
var reqMessages = new List<RequestMessageModel>();
8078

@@ -166,7 +164,6 @@ or RequestMessageKind.StreamQuery
166164

167165
public ImmutableEquatableArray<NotificationMessageHandlerModel> NotificationMessageHandlers { get; }
168166

169-
public ImmutableEquatableArray<NotificationMessageHandlerModel> OpenGenericNotificationMessageHandlers { get; }
170167
public ImmutableEquatableArray<PipelineBehaviorModel> PipelineBehaviors { get; }
171168

172169
public ImmutableEquatableArray<RequestMessageModel> IRequestMessages { get; }

src/Mediator.SourceGenerator/Implementation/Models/MessageHandlerModel.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,45 @@
1-
namespace Mediator.SourceGenerator;
1+
using Mediator.SourceGenerator.Extensions;
22

3-
internal sealed record NotificationMessageHandlerModel : MessageHandlerModel
4-
{
5-
const string OpenGenericTypeOfExpression = $"typeof(global::Mediator.INotificationHandler<>)";
3+
namespace Mediator.SourceGenerator;
64

7-
public NotificationMessageHandlerModel(INamedTypeSymbol symbol, CompilationAnalyzer analyzer)
8-
: base(symbol, analyzer)
5+
internal sealed record NotificationMessageHandlerModel : SymbolMetadataModel
6+
{
7+
public NotificationMessageHandlerModel(NotificationMessageHandler handler, CompilationAnalyzer analyzer)
8+
: base(handler.Symbol)
99
{
10-
OpenGenericServiceRegistrationBlock =
11-
$"services.Add(new SD({OpenGenericTypeOfExpression}, {TypeOfExpression(symbol, false)}, {analyzer.ServiceLifetime}));";
10+
ServiceRegistrations = ImmutableEquatableArray<string>.Empty;
11+
if (handler.Messages.Count > 0)
12+
{
13+
var interfaceSymbol = handler.InterfaceSymbol.GetTypeSymbolFullName(includeTypeParameters: false);
14+
var concreteSymbol = handler.Symbol.GetTypeSymbolFullName(includeTypeParameters: false);
15+
var builder = new List<string>(handler.Messages.Count);
16+
17+
if (!handler.Symbol.IsGenericType)
18+
{
19+
var concreteRegistration =
20+
$"services.TryAdd(new SD(typeof({concreteSymbol}), typeof({concreteSymbol}), {analyzer.ServiceLifetime}));";
21+
builder.Add(concreteRegistration);
22+
}
23+
24+
foreach (var message in handler.Messages)
25+
{
26+
var requestType = message.Symbol.GetTypeSymbolFullName();
27+
if (handler.Symbol.IsGenericType)
28+
{
29+
var concreteRegistration =
30+
$"services.TryAdd(new SD(typeof({concreteSymbol}<{requestType}>), typeof({concreteSymbol}<{requestType}>), {analyzer.ServiceLifetime}));";
31+
builder.Add(concreteRegistration);
32+
}
33+
var getExpression =
34+
$"GetRequiredService<{concreteSymbol}{(handler.Symbol.IsGenericType ? $"<{requestType}>" : "")}>()";
35+
var registration =
36+
$"services.Add(new SD(typeof({interfaceSymbol}<{requestType}>), {getExpression}, {analyzer.ServiceLifetime}));";
37+
builder.Add(registration);
38+
}
39+
40+
ServiceRegistrations = builder.ToImmutableEquatableArray();
41+
}
1242
}
1343

14-
public string OpenGenericServiceRegistrationBlock { get; }
44+
public ImmutableEquatableArray<string> ServiceRegistrations { get; }
1545
}

src/Mediator.SourceGenerator/Implementation/Models/NotificationMessageModel.cs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ namespace Mediator.SourceGenerator;
44

55
internal sealed record NotificationMessageModel : SymbolMetadataModel
66
{
7-
public NotificationMessageModel(
8-
INamedTypeSymbol symbol,
9-
CompilationAnalyzer analyzer,
10-
HashSet<NotificationMessageHandler> handlers
11-
)
7+
public NotificationMessageModel(INamedTypeSymbol symbol, CompilationAnalyzer analyzer)
128
: base(symbol)
139
{
1410
var identifierFullName = symbol
@@ -19,22 +15,9 @@ HashSet<NotificationMessageHandler> handlers
1915
HandlerWrapperTypeNameWithGenericTypeArguments =
2016
$"{handlerWrapperNamespace}.NotificationHandlerWrapper<{FullName}>";
2117
HandlerWrapperPropertyName = $"Wrapper_For_{identifierFullName}";
22-
var handlerServicesRegistrationBlock = new string[handlers.Count];
23-
var handlerTypeOfExpression = $"typeof(global::Mediator.INotificationHandler<{FullName}>)";
24-
int i = 0;
25-
foreach (var handler in handlers)
26-
{
27-
var getExpression = $"GetRequiredService<{handler.FullName}>()";
28-
handlerServicesRegistrationBlock[i] =
29-
$"services.Add(new SD({handlerTypeOfExpression}, {getExpression}, {analyzer.ServiceLifetime}));";
30-
i++;
31-
}
32-
HandlerServicesRegistrationBlock = new(handlerServicesRegistrationBlock);
3318
}
3419

3520
public string HandlerWrapperTypeNameWithGenericTypeArguments { get; }
3621

3722
public string HandlerWrapperPropertyName { get; }
38-
39-
public ImmutableEquatableArray<string> HandlerServicesRegistrationBlock { get; }
4023
}
Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
namespace Mediator.SourceGenerator;
1+
using Mediator.SourceGenerator.Extensions;
22

3-
internal sealed record RequestMessageHandlerModel : MessageHandlerModel
3+
namespace Mediator.SourceGenerator;
4+
5+
internal sealed record RequestMessageHandlerModel : SymbolMetadataModel
46
{
57
public string MessageType { get; }
68
public RequestMessageHandlerWrapperModel WrapperType { get; }
@@ -11,9 +13,15 @@ public RequestMessageHandlerModel(
1113
CompilationAnalyzer analyzer,
1214
RequestMessageHandlerWrapperModel wrapperType
1315
)
14-
: base(symbol, analyzer)
16+
: base(symbol)
1517
{
1618
MessageType = messageType;
1719
WrapperType = wrapperType;
20+
21+
var typeOfExpression = $"typeof({symbol.GetTypeSymbolFullName()})";
22+
ServiceRegistration =
23+
$"services.TryAdd(new SD({typeOfExpression}, {typeOfExpression}, {analyzer.ServiceLifetime}));";
1824
}
25+
26+
public string ServiceRegistration { get; }
1927
}

src/Mediator.SourceGenerator/Implementation/resources/Mediator.sbn-cs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,33 +66,25 @@ namespace Microsoft.Extensions.DependencyInjection
6666

6767
// Register handlers for request messages
6868
{{~ for message in RequestMessages ~}}
69-
{{ message.Handler.ServiceRegistrationBlock }}
69+
{{ message.Handler.ServiceRegistration }}
7070
services.Add(new SD(typeof(global::Mediator.I{{ message.MessageType }}Handler<{{ message.FullName }}, {{ message.ResponseFullName }}>), typeof({{ message.Handler.FullName }}), {{ ServiceLifetime }}));
7171
services.Add(new SD(typeof({{ message.HandlerWrapperTypeNameWithGenericTypeArguments }}), typeof({{ message.HandlerWrapperTypeNameWithGenericTypeArguments }}), {{ SingletonServiceLifetime }}));
7272
{{~ end ~}}
7373
{{~ end ~}}
74-
{{~ if (object.size NotificationMessageHandlers) > 0 ~}}
75-
76-
// Register concrete handlers for notification messages
77-
{{~ for handler in NotificationMessageHandlers ~}}
78-
{{ handler.ServiceRegistrationBlock }}
79-
{{~ end ~}}
80-
{{~ end ~}}
8174
{{~ if (object.size NotificationMessages) > 0 ~}}
8275

8376
// Register handlers and wrappers for notification messages
8477
{{~ for message in NotificationMessages ~}}
85-
{{~ for registration in message.HandlerServicesRegistrationBlock ~}}
86-
{{ registration }}
87-
{{~ end ~}}
8878
services.Add(new SD(typeof({{ message.HandlerWrapperTypeNameWithGenericTypeArguments }}), typeof({{ message.HandlerWrapperTypeNameWithGenericTypeArguments }}), {{ SingletonServiceLifetime }}));
8979
{{~ end ~}}
9080
{{~ end ~}}
91-
{{~ if (object.size OpenGenericNotificationMessageHandlers) > 0 ~}}
81+
{{~ if (object.size NotificationMessageHandlers) > 0 ~}}
9282

93-
// Register open generic handlers
94-
{{~ for handler in OpenGenericNotificationMessageHandlers ~}}
95-
{{ handler.OpenGenericServiceRegistrationBlock }}
83+
// Register notification handlers
84+
{{~ for handler in NotificationMessageHandlers ~}}
85+
{{~ for registration in handler.ServiceRegistrations ~}}
86+
{{ registration }}
87+
{{~ end ~}}
9688
{{~ end ~}}
9789
{{~ end ~}}
9890
{{~ if (object.size PipelineBehaviors) > 0 ~}}

0 commit comments

Comments
 (0)