Skip to content

Commit dcd61e4

Browse files
committed
Added additional checking to C# Codegen.Tests to validate return types
1 parent 445ca79 commit dcd61e4

5 files changed

Lines changed: 289 additions & 21 deletions

File tree

crates/bindings-csharp/Codegen.Tests/Tests.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace SpacetimeDB.Codegen.Tests;
44
using System.Runtime.CompilerServices;
55
using Microsoft.CodeAnalysis;
66
using Microsoft.CodeAnalysis.CSharp;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
78
using Microsoft.CodeAnalysis.MSBuild;
89
using Microsoft.CodeAnalysis.Text;
910

@@ -15,8 +16,19 @@ public static class GeneratorSnapshotTests
1516

1617
record struct StepOutput(string Key, IncrementalStepRunReason Reason, object Value);
1718

18-
class Fixture(string projectDir, CSharpCompilation sampleCompilation)
19+
class Fixture
1920
{
21+
private readonly string projectDir;
22+
private readonly CSharpCompilation sampleCompilation;
23+
24+
public Fixture(string projectDir, CSharpCompilation sampleCompilation)
25+
{
26+
this.projectDir = projectDir;
27+
this.sampleCompilation = sampleCompilation;
28+
}
29+
30+
public CSharpCompilation SampleCompilation => sampleCompilation;
31+
2032
public static async Task<Fixture> Compile(string name)
2133
{
2234
var projectDir = Path.Combine(GetProjectDir(), "fixtures", name);
@@ -63,6 +75,14 @@ IIncrementalGenerator generator
6375
return genResult.GeneratedTrees;
6476
}
6577

78+
public GeneratorDriverRunResult RunGeneratorAndGetResult(
79+
IIncrementalGenerator generator
80+
)
81+
{
82+
var driver = CreateDriver(generator, sampleCompilation.LanguageVersion);
83+
return driver.RunGenerators(sampleCompilation).GetRunResult();
84+
}
85+
6686
public async Task<CSharpCompilation> RunAndCheckGenerators(
6787
params IIncrementalGenerator[] generators
6888
) =>
@@ -257,4 +277,38 @@ public static async Task TestDiagnostics()
257277
AssertRuntimeDoesNotDefineLocal(compilationAfterGen);
258278
AssertGeneratedCodeDoesNotUseInternalBound(compilationAfterGen);
259279
}
280+
281+
[Fact]
282+
public static async Task ViewInvalidReturnHighlightsReturnType()
283+
{
284+
var fixture = await Fixture.Compile("diag");
285+
286+
var runResult = fixture.RunGeneratorAndGetResult(new SpacetimeDB.Codegen.Module());
287+
288+
var method = fixture.SampleCompilation.SyntaxTrees
289+
.Select(tree => new { Tree = tree, Root = tree.GetRoot() })
290+
.SelectMany(entry => entry.Root.DescendantNodes()
291+
.OfType<MethodDeclarationSyntax>()
292+
.Select(method => new { entry.Tree, entry.Root, Method = method }))
293+
.Single(entry => entry.Method.Identifier.Text == "ViewDefIEnumerableReturnFromIter");
294+
295+
var returnTypeSpan = method.Method.ReturnType.Span;
296+
var diagnostics = runResult.Results.SelectMany(result => result.Diagnostics)
297+
.Where(d => d.Id == "STDB0024")
298+
.ToList();
299+
var diagnostic = diagnostics.FirstOrDefault(d =>
300+
d.GetMessage().Contains("ViewDefIEnumerableReturnFromIter")
301+
&& d.Location.SourceTree == method.Tree
302+
);
303+
304+
Assert.NotNull(diagnostic);
305+
306+
Assert.Equal(returnTypeSpan, diagnostic!.Location.SourceSpan);
307+
308+
var returnTypeText = method.Root.ToFullString().Substring(
309+
returnTypeSpan.Start,
310+
returnTypeSpan.Length
311+
);
312+
Assert.Contains("IEnumerable", returnTypeText);
313+
}
260314
}

crates/bindings-csharp/Codegen.Tests/fixtures/diag/Lib.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,20 +554,34 @@ public static List<Player> ViewDefWrongContext(ReducerContext ctx)
554554
}
555555

556556
// TODO: Investigate why void return breaks the FFI generation
557-
// // Invalid: Void return type is not Vec<T> or Option<T>
557+
// // Invalid: Void return type is not List<T> or T?
558558
// [SpacetimeDB.View(Accessor = "view_def_no_return", Public = true)]
559559
// public static void ViewDefNoReturn(ViewContext ctx)
560560
// {
561561
// return;
562562
// }
563563

564-
// Invalid: Wrong return type is not Vec<T> or Option<T>
564+
// Invalid: Wrong return type is not List<T> or T?
565565
[SpacetimeDB.View(Accessor = "view_def_wrong_return", Public = true)]
566566
public static Player ViewDefWrongReturn(ViewContext ctx)
567567
{
568568
return new Player { Identity = new() };
569569
}
570570

571+
// Invalid: IEnumerable<T> return type (from Iter()) is not List<T> or T?
572+
[SpacetimeDB.View(Accessor = "view_def_ienumerable_return_from_iter", Public = true)]
573+
public static IEnumerable<Player> ViewDefIEnumerableReturnFromIter(ViewContext ctx)
574+
{
575+
return ctx.Db.Player.Iter();
576+
}
577+
578+
// Invalid: IEnumerable<T> return type (from Filter()) is not List<T> or T?
579+
[SpacetimeDB.View(Accessor = "view_def_ienumerable_return_from_filter", Public = true)]
580+
public static IEnumerable<Player> ViewDefIEnumerableReturnFromFilter(ViewContext ctx)
581+
{
582+
return ctx.Db.Player.Identity.Filter(ctx.Sender);
583+
}
584+
571585
// Invalid: Returns type that is not a SpacetimeType
572586
[SpacetimeDB.View(Accessor = "view_def_returns_not_a_spacetime_type", Public = true)]
573587
public static NotSpacetimeType? ViewDefReturnsNotASpacetimeType(AnonymousViewContext ctx)

crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,29 @@ SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_FOU
114114
]
115115
}
116116
},
117+
{/*
118+
{
119+
return ctx.Db.Player.Iter();
120+
^^^^
121+
}
122+
*/
123+
Message: 'PlayerReadOnly' does not contain a definition for 'Iter' and no accessible extension method 'Iter' accepting a first argument of type 'PlayerReadOnly' could be found (are you missing a using directive or an assembly reference?),
124+
Severity: Error,
125+
Descriptor: {
126+
Id: CS1061,
127+
Title: ,
128+
HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061),
129+
MessageFormat: '{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?),
130+
Category: Compiler,
131+
DefaultSeverity: Error,
132+
IsEnabledByDefault: true,
133+
CustomTags: [
134+
Compiler,
135+
Telemetry,
136+
NotConfigurable
137+
]
138+
}
139+
},
117140
{/*
118141
{
119142
ctx.Db.Player.Iter();
@@ -137,6 +160,29 @@ SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_FOU
137160
]
138161
}
139162
},
163+
{/*
164+
{
165+
return ctx.Db.Player.Identity.Filter(ctx.Sender);
166+
^^^^^^
167+
}
168+
*/
169+
Message: 'ReadOnlyUniqueIndex<PlayerReadOnly, Player, Identity, Identity.BSATN>.Filter(Identity)' is inaccessible due to its protection level,
170+
Severity: Error,
171+
Descriptor: {
172+
Id: CS0122,
173+
Title: ,
174+
HelpLink: https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0122),
175+
MessageFormat: '{0}' is inaccessible due to its protection level,
176+
Category: Compiler,
177+
DefaultSeverity: Error,
178+
IsEnabledByDefault: true,
179+
CustomTags: [
180+
Compiler,
181+
Telemetry,
182+
NotConfigurable
183+
]
184+
}
185+
},
140186
{/*
141187
// Valid Filter, but [ClientVisibilityFilter] is disabled
142188
[SpacetimeDB.ClientVisibilityFilter]

crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs

Lines changed: 100 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)