Commit:
08f5c7dParent:
0ff7bb5Follow corss-project and corss-type "message" publishers/handlers
Assisted-by: GitHub Copilot:gpt-6.1-sol
core/src/MessageLinks.cs
+10
-7
diff --git a/core/src/MessageLinks.cs b/core/src/MessageLinks.cs
index 85f22bb..76375cc 100644
@@ -103,7 +103,7 @@ internal sealed class MessageLinks
token.ThrowIfCancellationRequested();
if (
!(member is IMethodSymbol { MethodKind: MethodKind.Constructor } && syntax is TypeDeclarationSyntax)
&& !SymbolEqualityComparer.Default.Equals(ContainingMember(model, invocation, token), member)
&& !SameMember(ContainingMember(model, invocation, token), member)
|| model.GetOperation(invocation, token) is not IInvocationOperation operation
)
{
@@ -153,12 +153,7 @@ internal sealed class MessageLinks
foreach (
var registration in candidates
.SelectMany(Registrations)
.Where(registration =>
SymbolEqualityComparer.Default.Equals(
registration.Handler.OriginalDefinition,
member.OriginalDefinition
)
)
.Where(registration => SameMember(registration.Handler, member))
)
{
result.UnionWith(await Resolve(registration.Route, registration.Message, publishers: true, token));
@@ -357,6 +352,14 @@ internal sealed class MessageLinks
&& DocumentationCommentId.CreateReferenceId(left) is { } id
&& id == DocumentationCommentId.CreateReferenceId(right);
private static bool SameMember(ISymbol? left, ISymbol right) =>
SymbolEqualityComparer.Default.Equals(left?.OriginalDefinition, right.OriginalDefinition)
// Retargeted symbols from another project still represent the same source declaration.
|| left is not null
&& left.ContainingAssembly.Identity.Equals(right.ContainingAssembly.Identity)
&& left.OriginalDefinition.GetDocumentationCommentId() is { } id
&& id == right.OriginalDefinition.GetDocumentationCommentId();
private static IOperation? Unwrap(IOperation? operation) =>
operation switch
{
core/tests/CallGraphBuilderTests/FindCalleesShould.cs
+48
-0
diff --git a/core/tests/CallGraphBuilderTests/FindCalleesShould.cs b/core/tests/CallGraphBuilderTests/FindCalleesShould.cs
index 133c09b..18319e7 100644
@@ -9,6 +9,54 @@ namespace Cfa.Tests.CallGraphBuilderTests;
public sealed class FindCalleesShould
{
[Theory]
[InlineData("")]
[InlineData("<int>")]
public async Task FollowInheritedPublishersFromConstructedGenericAggregatesToOverloadedHandlers(
string typeArguments
)
{
// Arrange
var source =
MessageSource.Contracts
+ $$"""
abstract class Aggregate<TAggregate, TId, TState>
{
protected void ApplyEvent(Sample.Domain.Message<TId> message) { }
}
class Device{{(typeArguments is "" ? "" : "<T>")}} : Aggregate<object, int, object>
{
public void Request() { ApplyEvent(new Updated()); }
}
class Receiver : Sample.Grains.EventHandlers.EventReceiver<Sample.Domain.Message<int>>
{
public Receiver() { RegisterHandlers(); }
void RegisterHandlers() { On<Updated>(Publish); On<Other>(Publish); }
void Publish(Updated message) { Save(); }
void Publish(Other message) { }
void Save() { }
}
class Example { void Start(Device{{typeArguments}} device) { device.Request(); } }
""";
var options = MessageSource.Options;
options.MessageRoutes[0].Publisher.Method = new()
{
Type = "Aggregate`3",
Name = "ApplyEvent",
GenericArity = 0,
ParameterCount = 1,
};
// Act
var (start, calls) = await source.FindCallees(options);
// Assert
var request = calls[start].ShouldHaveSingleItem().Target;
var handler = calls[request].Where(call => call.LinkKind == LinkKind.Event).ShouldHaveSingleItem();
handler.Target.Name.ShouldBe("Publish");
calls[handler.Target].ShouldHaveSingleItem().Target.Name.ShouldBe("Save");
}
[Fact]
public async Task NotTreatPrimaryConstructorMemberBodiesAsConstructorCalls()
{