Skip to content

Commit

Permalink
FIX: Actor source generator generates invalid code for generic interf…
Browse files Browse the repository at this point in the history
…aces (dapr#1419)

* Handled generic actor interface

Signed-off-by: Manuel Menegazzo <[email protected]>

* Added more actor examples

Signed-off-by: Manuel Menegazzo <[email protected]>

* Updated actor namespace in example project

Signed-off-by: Manuel Menegazzo <[email protected]>

---------

Signed-off-by: Manuel Menegazzo <[email protected]>
Co-authored-by: Whit Waldo <[email protected]>
Signed-off-by: Siri Varma Vegiraju <[email protected]>
  • Loading branch information
2 people authored and svegiraju-microsoft committed Dec 20, 2024
1 parent b343ecb commit 4ed55b3
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 6 deletions.
27 changes: 27 additions & 0 deletions examples/GeneratedActor/ActorClient/IGenericClientActor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// ------------------------------------------------------------------------
// Copyright 2023 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

using Dapr.Actors.Generators;

namespace GeneratedActor
{
[GenerateActorClient]
internal interface IGenericClientActor<TGenericType1, TGenericType2>
{
[ActorMethod(Name = "GetState")]
Task<TGenericType1> GetStateAsync(CancellationToken cancellationToken = default);

[ActorMethod(Name = "SetState")]
Task SetStateAsync(TGenericType2 state, CancellationToken cancellationToken = default);
}
}
23 changes: 17 additions & 6 deletions src/Dapr.Actors.Generators/ActorClientGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,23 @@ private static void GenerateActorClientCode(SourceProductionContext context, Act
.Append(SyntaxKind.SealedKeyword)
.Select(sk => SyntaxFactory.Token(sk));

var actorClientClassDeclaration = SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName)
.WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers))
.WithMembers(SyntaxFactory.List(actorMembers))
.WithBaseList(SyntaxFactory.BaseList(
SyntaxFactory.Token(SyntaxKind.ColonToken),
SyntaxFactory.SeparatedList<BaseTypeSyntax>(new[] { actorClientBaseInterface })));
var actorClientClassTypeParameters = descriptor.InterfaceType.TypeParameters
.Select(x => SyntaxFactory.TypeParameter(x.ToString()));

var actorClientClassDeclaration = (actorClientClassTypeParameters.Count() == 0)
? SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName)
.WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers))
.WithMembers(SyntaxFactory.List(actorMembers))
.WithBaseList(SyntaxFactory.BaseList(
SyntaxFactory.Token(SyntaxKind.ColonToken),
SyntaxFactory.SeparatedList<BaseTypeSyntax>(new[] { actorClientBaseInterface })))
: SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName)
.WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers))
.WithTypeParameterList(SyntaxFactory.TypeParameterList(SyntaxFactory.SeparatedList(actorClientClassTypeParameters)))
.WithMembers(SyntaxFactory.List(actorMembers))
.WithBaseList(SyntaxFactory.BaseList(
SyntaxFactory.Token(SyntaxKind.ColonToken),
SyntaxFactory.SeparatedList<BaseTypeSyntax>(new[] { actorClientBaseInterface })));

var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(descriptor.NamespaceName))
.WithMembers(SyntaxFactory.List<MemberDeclarationSyntax>(new[] { actorClientClassDeclaration }))
Expand Down
172 changes: 172 additions & 0 deletions test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,92 @@ public System.Threading.Tasks.Task TestMethod()
await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync();
}

[Fact]
public async Task TestSingleGenericInternalInterface()
{
var originalSource = @"
using Dapr.Actors.Generators;
using System.Threading.Tasks;
namespace Test
{
[GenerateActorClient]
internal interface ITestActor<TGenericType>
{
Task TestMethod();
}
}";

var generatedSource = @"// <auto-generated/>
#nullable enable
namespace Test
{
internal sealed class TestActorClient<TGenericType> : Test.ITestActor<TGenericType>
{
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
{
if (actorProxy is null)
{
throw new System.ArgumentNullException(nameof(actorProxy));
}
this.actorProxy = actorProxy;
}
public System.Threading.Tasks.Task TestMethod()
{
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
}
}
}";

await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync();
}

[Fact]
public async Task TestMultipleGenericsInternalInterface()
{
var originalSource = @"
using Dapr.Actors.Generators;
using System.Threading.Tasks;
namespace Test
{
[GenerateActorClient]
internal interface ITestActor<TGenericType1, TGenericType2>
{
Task TestMethod();
}
}";

var generatedSource = @"// <auto-generated/>
#nullable enable
namespace Test
{
internal sealed class TestActorClient<TGenericType1, TGenericType2> : Test.ITestActor<TGenericType1, TGenericType2>
{
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
{
if (actorProxy is null)
{
throw new System.ArgumentNullException(nameof(actorProxy));
}
this.actorProxy = actorProxy;
}
public System.Threading.Tasks.Task TestMethod()
{
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
}
}
}";

await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync();
}

[Fact]
public async Task TestRenamedClient()
{
Expand Down Expand Up @@ -211,6 +297,92 @@ public System.Threading.Tasks.Task TestMethod()
await CreateTest(originalSource, "Test.MyTestActorClient.g.cs", generatedSource).RunAsync();
}

[Fact]
public async Task TestSingleGenericRenamedClient()
{
var originalSource = @"
using Dapr.Actors.Generators;
using System.Threading.Tasks;
namespace Test
{
[GenerateActorClient(Name = ""MyTestActorClient"")]
internal interface ITestActor<TGenericType>
{
Task TestMethod();
}
}";

var generatedSource = @"// <auto-generated/>
#nullable enable
namespace Test
{
internal sealed class MyTestActorClient<TGenericType> : Test.ITestActor<TGenericType>
{
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
public MyTestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
{
if (actorProxy is null)
{
throw new System.ArgumentNullException(nameof(actorProxy));
}
this.actorProxy = actorProxy;
}
public System.Threading.Tasks.Task TestMethod()
{
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
}
}
}";

await CreateTest(originalSource, "Test.MyTestActorClient.g.cs", generatedSource).RunAsync();
}

[Fact]
public async Task TestMultipleGenericsRenamedClient()
{
var originalSource = @"
using Dapr.Actors.Generators;
using System.Threading.Tasks;
namespace Test
{
[GenerateActorClient(Name = ""MyTestActorClient"")]
internal interface ITestActor<TGenericType1, TGenericType2>
{
Task TestMethod();
}
}";

var generatedSource = @"// <auto-generated/>
#nullable enable
namespace Test
{
internal sealed class MyTestActorClient<TGenericType1, TGenericType2> : Test.ITestActor<TGenericType1, TGenericType2>
{
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
public MyTestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
{
if (actorProxy is null)
{
throw new System.ArgumentNullException(nameof(actorProxy));
}
this.actorProxy = actorProxy;
}
public System.Threading.Tasks.Task TestMethod()
{
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
}
}
}";

await CreateTest(originalSource, "Test.MyTestActorClient.g.cs", generatedSource).RunAsync();
}

[Fact]
public async Task TestCustomNamespace()
{
Expand Down

0 comments on commit 4ed55b3

Please sign in to comment.