Skip to content

Commit 1b7e65c

Browse files
m3naxWhitWaldo
authored andcommitted
FIX: Actor source generator generates invalid code for generic interfaces (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: Divya Perumal <[email protected]>
1 parent 7ff85d3 commit 1b7e65c

File tree

3 files changed

+216
-6
lines changed

3 files changed

+216
-6
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// ------------------------------------------------------------------------
2+
// Copyright 2023 The Dapr Authors
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
// ------------------------------------------------------------------------
13+
14+
using Dapr.Actors.Generators;
15+
16+
namespace GeneratedActor
17+
{
18+
[GenerateActorClient]
19+
internal interface IGenericClientActor<TGenericType1, TGenericType2>
20+
{
21+
[ActorMethod(Name = "GetState")]
22+
Task<TGenericType1> GetStateAsync(CancellationToken cancellationToken = default);
23+
24+
[ActorMethod(Name = "SetState")]
25+
Task SetStateAsync(TGenericType2 state, CancellationToken cancellationToken = default);
26+
}
27+
}

src/Dapr.Actors.Generators/ActorClientGenerator.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,23 @@ private static void GenerateActorClientCode(SourceProductionContext context, Act
161161
.Append(SyntaxKind.SealedKeyword)
162162
.Select(sk => SyntaxFactory.Token(sk));
163163

164-
var actorClientClassDeclaration = SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName)
165-
.WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers))
166-
.WithMembers(SyntaxFactory.List(actorMembers))
167-
.WithBaseList(SyntaxFactory.BaseList(
168-
SyntaxFactory.Token(SyntaxKind.ColonToken),
169-
SyntaxFactory.SeparatedList<BaseTypeSyntax>(new[] { actorClientBaseInterface })));
164+
var actorClientClassTypeParameters = descriptor.InterfaceType.TypeParameters
165+
.Select(x => SyntaxFactory.TypeParameter(x.ToString()));
166+
167+
var actorClientClassDeclaration = (actorClientClassTypeParameters.Count() == 0)
168+
? SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName)
169+
.WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers))
170+
.WithMembers(SyntaxFactory.List(actorMembers))
171+
.WithBaseList(SyntaxFactory.BaseList(
172+
SyntaxFactory.Token(SyntaxKind.ColonToken),
173+
SyntaxFactory.SeparatedList<BaseTypeSyntax>(new[] { actorClientBaseInterface })))
174+
: SyntaxFactory.ClassDeclaration(descriptor.ClientTypeName)
175+
.WithModifiers(SyntaxFactory.TokenList(actorClientClassModifiers))
176+
.WithTypeParameterList(SyntaxFactory.TypeParameterList(SyntaxFactory.SeparatedList(actorClientClassTypeParameters)))
177+
.WithMembers(SyntaxFactory.List(actorMembers))
178+
.WithBaseList(SyntaxFactory.BaseList(
179+
SyntaxFactory.Token(SyntaxKind.ColonToken),
180+
SyntaxFactory.SeparatedList<BaseTypeSyntax>(new[] { actorClientBaseInterface })));
170181

171182
var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(descriptor.NamespaceName))
172183
.WithMembers(SyntaxFactory.List<MemberDeclarationSyntax>(new[] { actorClientClassDeclaration }))

test/Dapr.Actors.Generators.Test/ActorClientGeneratorTests.cs

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,92 @@ public System.Threading.Tasks.Task TestMethod()
168168
await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync();
169169
}
170170

171+
[Fact]
172+
public async Task TestSingleGenericInternalInterface()
173+
{
174+
var originalSource = @"
175+
using Dapr.Actors.Generators;
176+
using System.Threading.Tasks;
177+
178+
namespace Test
179+
{
180+
[GenerateActorClient]
181+
internal interface ITestActor<TGenericType>
182+
{
183+
Task TestMethod();
184+
}
185+
}";
186+
187+
var generatedSource = @"// <auto-generated/>
188+
#nullable enable
189+
namespace Test
190+
{
191+
internal sealed class TestActorClient<TGenericType> : Test.ITestActor<TGenericType>
192+
{
193+
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
194+
public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
195+
{
196+
if (actorProxy is null)
197+
{
198+
throw new System.ArgumentNullException(nameof(actorProxy));
199+
}
200+
201+
this.actorProxy = actorProxy;
202+
}
203+
204+
public System.Threading.Tasks.Task TestMethod()
205+
{
206+
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
207+
}
208+
}
209+
}";
210+
211+
await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync();
212+
}
213+
214+
[Fact]
215+
public async Task TestMultipleGenericsInternalInterface()
216+
{
217+
var originalSource = @"
218+
using Dapr.Actors.Generators;
219+
using System.Threading.Tasks;
220+
221+
namespace Test
222+
{
223+
[GenerateActorClient]
224+
internal interface ITestActor<TGenericType1, TGenericType2>
225+
{
226+
Task TestMethod();
227+
}
228+
}";
229+
230+
var generatedSource = @"// <auto-generated/>
231+
#nullable enable
232+
namespace Test
233+
{
234+
internal sealed class TestActorClient<TGenericType1, TGenericType2> : Test.ITestActor<TGenericType1, TGenericType2>
235+
{
236+
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
237+
public TestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
238+
{
239+
if (actorProxy is null)
240+
{
241+
throw new System.ArgumentNullException(nameof(actorProxy));
242+
}
243+
244+
this.actorProxy = actorProxy;
245+
}
246+
247+
public System.Threading.Tasks.Task TestMethod()
248+
{
249+
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
250+
}
251+
}
252+
}";
253+
254+
await CreateTest(originalSource, "Test.TestActorClient.g.cs", generatedSource).RunAsync();
255+
}
256+
171257
[Fact]
172258
public async Task TestRenamedClient()
173259
{
@@ -211,6 +297,92 @@ public System.Threading.Tasks.Task TestMethod()
211297
await CreateTest(originalSource, "Test.MyTestActorClient.g.cs", generatedSource).RunAsync();
212298
}
213299

300+
[Fact]
301+
public async Task TestSingleGenericRenamedClient()
302+
{
303+
var originalSource = @"
304+
using Dapr.Actors.Generators;
305+
using System.Threading.Tasks;
306+
307+
namespace Test
308+
{
309+
[GenerateActorClient(Name = ""MyTestActorClient"")]
310+
internal interface ITestActor<TGenericType>
311+
{
312+
Task TestMethod();
313+
}
314+
}";
315+
316+
var generatedSource = @"// <auto-generated/>
317+
#nullable enable
318+
namespace Test
319+
{
320+
internal sealed class MyTestActorClient<TGenericType> : Test.ITestActor<TGenericType>
321+
{
322+
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
323+
public MyTestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
324+
{
325+
if (actorProxy is null)
326+
{
327+
throw new System.ArgumentNullException(nameof(actorProxy));
328+
}
329+
330+
this.actorProxy = actorProxy;
331+
}
332+
333+
public System.Threading.Tasks.Task TestMethod()
334+
{
335+
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
336+
}
337+
}
338+
}";
339+
340+
await CreateTest(originalSource, "Test.MyTestActorClient.g.cs", generatedSource).RunAsync();
341+
}
342+
343+
[Fact]
344+
public async Task TestMultipleGenericsRenamedClient()
345+
{
346+
var originalSource = @"
347+
using Dapr.Actors.Generators;
348+
using System.Threading.Tasks;
349+
350+
namespace Test
351+
{
352+
[GenerateActorClient(Name = ""MyTestActorClient"")]
353+
internal interface ITestActor<TGenericType1, TGenericType2>
354+
{
355+
Task TestMethod();
356+
}
357+
}";
358+
359+
var generatedSource = @"// <auto-generated/>
360+
#nullable enable
361+
namespace Test
362+
{
363+
internal sealed class MyTestActorClient<TGenericType1, TGenericType2> : Test.ITestActor<TGenericType1, TGenericType2>
364+
{
365+
private readonly Dapr.Actors.Client.ActorProxy actorProxy;
366+
public MyTestActorClient(Dapr.Actors.Client.ActorProxy actorProxy)
367+
{
368+
if (actorProxy is null)
369+
{
370+
throw new System.ArgumentNullException(nameof(actorProxy));
371+
}
372+
373+
this.actorProxy = actorProxy;
374+
}
375+
376+
public System.Threading.Tasks.Task TestMethod()
377+
{
378+
return this.actorProxy.InvokeMethodAsync(""TestMethod"");
379+
}
380+
}
381+
}";
382+
383+
await CreateTest(originalSource, "Test.MyTestActorClient.g.cs", generatedSource).RunAsync();
384+
}
385+
214386
[Fact]
215387
public async Task TestCustomNamespace()
216388
{

0 commit comments

Comments
 (0)