diff --git a/crates/bindings-csharp/BSATN.Codegen/Diag.cs b/crates/bindings-csharp/BSATN.Codegen/Diag.cs index 7da994bd64..2ae3b2513b 100644 --- a/crates/bindings-csharp/BSATN.Codegen/Diag.cs +++ b/crates/bindings-csharp/BSATN.Codegen/Diag.cs @@ -91,6 +91,22 @@ Func toLocation ) : this(group, title, interpolate, ctx => toLocation(ctx).Locations.FirstOrDefault()) { } + public ErrorDescriptor( + ErrorDescriptorGroup group, + string title, + Expression> interpolate, + Func toLocation + ) + : this( + group, + title, + interpolate, + ctx => + toLocation(ctx).ApplicationSyntaxReference is { } r + ? r.SyntaxTree.GetLocation(r.Span) + : null + ) { } + public Diagnostic ToDiag(TContext ctx) => Diagnostic.Create(descriptor, toLocation(ctx), makeFormatArgs(ctx)); } diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/Lib.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/Lib.cs index 61423e76db..8b485188a7 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/Lib.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/Lib.cs @@ -361,20 +361,6 @@ public static partial class InAnotherNamespace public partial struct TestDuplicateTableName { } } -[SpacetimeDB.Table( - Name = "TestIncompatibleSchedule1", - Scheduled = nameof(TestIncompatibleScheduleReducer) -)] -[SpacetimeDB.Table(Name = "TestIncompatibleSchedule2")] -public partial struct TestIncompatibleSchedule -{ - [SpacetimeDB.Reducer] - public static void TestIncompatibleScheduleReducer( - ReducerContext ctx, - TestIncompatibleSchedule table - ) { } -} - [SpacetimeDB.Table] [SpacetimeDB.Index] public partial struct TestIndexWithoutColumns { } @@ -382,3 +368,35 @@ public partial struct TestIndexWithoutColumns { } [SpacetimeDB.Table] [SpacetimeDB.Index(BTree = [])] public partial struct TestIndexWithEmptyColumns { } + +[SpacetimeDB.Table( + Name = "TestScheduleWithoutPrimaryKey", + Scheduled = "DummyScheduledReducer", + ScheduledAt = nameof(ScheduleAtCorrectType) +)] +[SpacetimeDB.Table( + Name = "TestScheduleWithWrongPrimaryKeyType", + Scheduled = "DummyScheduledReducer", + ScheduledAt = nameof(ScheduleAtCorrectType) +)] +[SpacetimeDB.Table(Name = "TestScheduleWithoutScheduleAt", Scheduled = "DummyScheduledReducer")] +[SpacetimeDB.Table( + Name = "TestScheduleWithWrongScheduleAtType", + Scheduled = "DummyScheduledReducer", + ScheduledAt = nameof(ScheduleAtWrongType) +)] +public partial struct TestScheduleIssues +{ + [SpacetimeDB.PrimaryKey(Table = "TestScheduleWithWrongPrimaryKeyType")] + public string IdWrongType; + + [SpacetimeDB.PrimaryKey(Table = "TestScheduleWithoutScheduleAt")] + [SpacetimeDB.PrimaryKey(Table = "TestScheduleWithWrongScheduleAtType")] + public int IdCorrectType; + + public int ScheduleAtWrongType; + public ScheduleAt ScheduleAtCorrectType; + + [SpacetimeDB.Reducer] + public static void DummyScheduledReducer(ReducerContext ctx, TestScheduleIssues table) { } +} diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt index a61c60ca17..465227b1cc 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt @@ -805,7 +805,7 @@ public TestUniqueNotEquatableUniqueIndex PrimaryKeyField => new(this); } }, {/* -SpacetimeDB.Internal.Module.RegisterTable(); +SpacetimeDB.Internal.Module.RegisterTable(); SpacetimeDB.Internal.Module.RegisterTable(); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SpacetimeDB.Internal.Module.RegisterTable(); @@ -828,7 +828,7 @@ SpacetimeDB.Internal.Module.RegisterTable(); +SpacetimeDB.Internal.Module.RegisterTable(); SpacetimeDB.Internal.Module.RegisterTable(); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SpacetimeDB.Internal.Module.RegisterTable(); diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs index 46b372df9c..a3c89ede44 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs @@ -198,219 +198,352 @@ public bool Delete(global::TestDuplicateTableName row) => >.DoDelete(row); } - public readonly struct TestIncompatibleSchedule1 + public readonly struct TestScheduleWithoutPrimaryKey : SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues > { - static global::TestIncompatibleSchedule SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule - >.ReadGenFields(System.IO.BinaryReader reader, global::TestIncompatibleSchedule row) + static global::TestScheduleIssues SpacetimeDB.Internal.ITableView< + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues + >.ReadGenFields(System.IO.BinaryReader reader, global::TestScheduleIssues row) { - if (row.ScheduledId == default) - { - row.ScheduledId = global::TestIncompatibleSchedule.BSATN.ScheduledId.Read( - reader - ); - } return row; } static SpacetimeDB.Internal.RawTableDefV9 SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues >.MakeTableDesc(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new( - Name: nameof(TestIncompatibleSchedule1), + Name: nameof(TestScheduleWithoutPrimaryKey), ProductTypeRef: (uint) - new global::TestIncompatibleSchedule.BSATN() - .GetAlgebraicType(registrar) - .Ref_, - PrimaryKey: [0], + new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_, + PrimaryKey: [], + Indexes: [], + Constraints: [], + Sequences: [], + Schedule: SpacetimeDB.Internal.ITableView< + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues + >.MakeSchedule("DummyScheduledReducer", 3), + TableType: SpacetimeDB.Internal.TableType.User, + TableAccess: SpacetimeDB.Internal.TableAccess.Private + ); + + public ulong Count => + SpacetimeDB.Internal.ITableView< + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues + >.DoCount(); + + public IEnumerable Iter() => + SpacetimeDB.Internal.ITableView< + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues + >.DoIter(); + + public global::TestScheduleIssues Insert(global::TestScheduleIssues row) => + SpacetimeDB.Internal.ITableView< + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues + >.DoInsert(row); + + public bool Delete(global::TestScheduleIssues row) => + SpacetimeDB.Internal.ITableView< + TestScheduleWithoutPrimaryKey, + global::TestScheduleIssues + >.DoDelete(row); + } + + public readonly struct TestScheduleWithoutScheduleAt + : SpacetimeDB.Internal.ITableView< + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues + > + { + static global::TestScheduleIssues SpacetimeDB.Internal.ITableView< + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues + >.ReadGenFields(System.IO.BinaryReader reader, global::TestScheduleIssues row) + { + return row; + } + + static SpacetimeDB.Internal.RawTableDefV9 SpacetimeDB.Internal.ITableView< + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues + >.MakeTableDesc(SpacetimeDB.BSATN.ITypeRegistrar registrar) => + new( + Name: nameof(TestScheduleWithoutScheduleAt), + ProductTypeRef: (uint) + new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_, + PrimaryKey: [1], Indexes: [ new( Name: null, - AccessorName: "ScheduledId", - Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0]) + AccessorName: "IdCorrectType", + Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1]) ) ], Constraints: [ SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule - >.MakeUniqueConstraint(0) - ], - Sequences: - [ - SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule - >.MakeSequence(0) + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues + >.MakeUniqueConstraint(1) ], - Schedule: SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule - >.MakeSchedule("TestIncompatibleScheduleReducer", 1), + Sequences: [], + Schedule: null, TableType: SpacetimeDB.Internal.TableType.User, TableAccess: SpacetimeDB.Internal.TableAccess.Private ); public ulong Count => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues >.DoCount(); - public IEnumerable Iter() => + public IEnumerable Iter() => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues >.DoIter(); - public global::TestIncompatibleSchedule Insert(global::TestIncompatibleSchedule row) => + public global::TestScheduleIssues Insert(global::TestScheduleIssues row) => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues >.DoInsert(row); - public bool Delete(global::TestIncompatibleSchedule row) => + public bool Delete(global::TestScheduleIssues row) => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues >.DoDelete(row); - public sealed class TestIncompatibleSchedule1UniqueIndex + public sealed class TestScheduleWithoutScheduleAtUniqueIndex : UniqueIndex< - TestIncompatibleSchedule1, - global::TestIncompatibleSchedule, - ulong, - SpacetimeDB.BSATN.U64 + TestScheduleWithoutScheduleAt, + global::TestScheduleIssues, + int, + SpacetimeDB.BSATN.I32 > { - internal TestIncompatibleSchedule1UniqueIndex(TestIncompatibleSchedule1 handle) - : base(handle, "TestIncompatibleSchedule1_ScheduledId_idx_btree") { } + internal TestScheduleWithoutScheduleAtUniqueIndex( + TestScheduleWithoutScheduleAt handle + ) + : base(handle, "TestScheduleWithoutScheduleAt_IdCorrectType_idx_btree") { } // Important: don't move this to the base class. // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another. - public global::TestIncompatibleSchedule? Find(ulong key) => - DoFilter(key).Cast().SingleOrDefault(); + public global::TestScheduleIssues? Find(int key) => + DoFilter(key).Cast().SingleOrDefault(); - public bool Update(global::TestIncompatibleSchedule row) => - DoUpdate(row.ScheduledId, row); + public bool Update(global::TestScheduleIssues row) => + DoUpdate(row.IdCorrectType, row); } - public TestIncompatibleSchedule1UniqueIndex ScheduledId => new(this); + public TestScheduleWithoutScheduleAtUniqueIndex IdCorrectType => new(this); } - public readonly struct TestIncompatibleSchedule2 + public readonly struct TestScheduleWithWrongPrimaryKeyType : SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues > { - static global::TestIncompatibleSchedule SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule - >.ReadGenFields(System.IO.BinaryReader reader, global::TestIncompatibleSchedule row) + static global::TestScheduleIssues SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues + >.ReadGenFields(System.IO.BinaryReader reader, global::TestScheduleIssues row) { - if (row.ScheduledId == default) - { - row.ScheduledId = global::TestIncompatibleSchedule.BSATN.ScheduledId.Read( - reader - ); - } return row; } static SpacetimeDB.Internal.RawTableDefV9 SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues >.MakeTableDesc(SpacetimeDB.BSATN.ITypeRegistrar registrar) => new( - Name: nameof(TestIncompatibleSchedule2), + Name: nameof(TestScheduleWithWrongPrimaryKeyType), ProductTypeRef: (uint) - new global::TestIncompatibleSchedule.BSATN() - .GetAlgebraicType(registrar) - .Ref_, + new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_, PrimaryKey: [0], Indexes: [ new( Name: null, - AccessorName: "ScheduledId", + AccessorName: "IdWrongType", Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0]) ) ], Constraints: [ SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues >.MakeUniqueConstraint(0) ], - Sequences: + Sequences: [], + Schedule: SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues + >.MakeSchedule("DummyScheduledReducer", 3), + TableType: SpacetimeDB.Internal.TableType.User, + TableAccess: SpacetimeDB.Internal.TableAccess.Private + ); + + public ulong Count => + SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues + >.DoCount(); + + public IEnumerable Iter() => + SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues + >.DoIter(); + + public global::TestScheduleIssues Insert(global::TestScheduleIssues row) => + SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues + >.DoInsert(row); + + public bool Delete(global::TestScheduleIssues row) => + SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues + >.DoDelete(row); + + public sealed class TestScheduleWithWrongPrimaryKeyTypeUniqueIndex + : UniqueIndex< + TestScheduleWithWrongPrimaryKeyType, + global::TestScheduleIssues, + string, + SpacetimeDB.BSATN.String + > + { + internal TestScheduleWithWrongPrimaryKeyTypeUniqueIndex( + TestScheduleWithWrongPrimaryKeyType handle + ) + : base(handle, "TestScheduleWithWrongPrimaryKeyType_IdWrongType_idx_btree") { } + + // Important: don't move this to the base class. + // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based + // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another. + public global::TestScheduleIssues? Find(string key) => + DoFilter(key).Cast().SingleOrDefault(); + + public bool Update(global::TestScheduleIssues row) => + DoUpdate(row.IdWrongType, row); + } + + public TestScheduleWithWrongPrimaryKeyTypeUniqueIndex IdWrongType => new(this); + } + + public readonly struct TestScheduleWithWrongScheduleAtType + : SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues + > + { + static global::TestScheduleIssues SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues + >.ReadGenFields(System.IO.BinaryReader reader, global::TestScheduleIssues row) + { + return row; + } + + static SpacetimeDB.Internal.RawTableDefV9 SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues + >.MakeTableDesc(SpacetimeDB.BSATN.ITypeRegistrar registrar) => + new( + Name: nameof(TestScheduleWithWrongScheduleAtType), + ProductTypeRef: (uint) + new global::TestScheduleIssues.BSATN().GetAlgebraicType(registrar).Ref_, + PrimaryKey: [1], + Indexes: + [ + new( + Name: null, + AccessorName: "IdCorrectType", + Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1]) + ) + ], + Constraints: [ SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule - >.MakeSequence(0) + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues + >.MakeUniqueConstraint(1) ], - Schedule: null, + Sequences: [], + Schedule: SpacetimeDB.Internal.ITableView< + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues + >.MakeSchedule("DummyScheduledReducer", 2), TableType: SpacetimeDB.Internal.TableType.User, TableAccess: SpacetimeDB.Internal.TableAccess.Private ); public ulong Count => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues >.DoCount(); - public IEnumerable Iter() => + public IEnumerable Iter() => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues >.DoIter(); - public global::TestIncompatibleSchedule Insert(global::TestIncompatibleSchedule row) => + public global::TestScheduleIssues Insert(global::TestScheduleIssues row) => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues >.DoInsert(row); - public bool Delete(global::TestIncompatibleSchedule row) => + public bool Delete(global::TestScheduleIssues row) => SpacetimeDB.Internal.ITableView< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues >.DoDelete(row); - public sealed class TestIncompatibleSchedule2UniqueIndex + public sealed class TestScheduleWithWrongScheduleAtTypeUniqueIndex : UniqueIndex< - TestIncompatibleSchedule2, - global::TestIncompatibleSchedule, - ulong, - SpacetimeDB.BSATN.U64 + TestScheduleWithWrongScheduleAtType, + global::TestScheduleIssues, + int, + SpacetimeDB.BSATN.I32 > { - internal TestIncompatibleSchedule2UniqueIndex(TestIncompatibleSchedule2 handle) - : base(handle, "TestIncompatibleSchedule2_ScheduledId_idx_btree") { } + internal TestScheduleWithWrongScheduleAtTypeUniqueIndex( + TestScheduleWithWrongScheduleAtType handle + ) + : base(handle, "TestScheduleWithWrongScheduleAtType_IdCorrectType_idx_btree") + { } // Important: don't move this to the base class. // C# generics don't play well with nullable types and can't accept both struct-type-based and class-type-based // `globalName` in one generic definition, leading to buggy `Row?` expansion for either one or another. - public global::TestIncompatibleSchedule? Find(ulong key) => - DoFilter(key).Cast().SingleOrDefault(); + public global::TestScheduleIssues? Find(int key) => + DoFilter(key).Cast().SingleOrDefault(); - public bool Update(global::TestIncompatibleSchedule row) => - DoUpdate(row.ScheduledId, row); + public bool Update(global::TestScheduleIssues row) => + DoUpdate(row.IdCorrectType, row); } - public TestIncompatibleSchedule2UniqueIndex ScheduledId => new(this); + public TestScheduleWithWrongScheduleAtTypeUniqueIndex IdCorrectType => new(this); } public readonly struct TestTableTaggedEnum @@ -595,8 +728,14 @@ public sealed class Local { public Internal.TableHandles.TestAutoIncNotInteger TestAutoIncNotInteger => new(); public Internal.TableHandles.TestDuplicateTableName TestDuplicateTableName => new(); - public Internal.TableHandles.TestIncompatibleSchedule1 TestIncompatibleSchedule1 => new(); - public Internal.TableHandles.TestIncompatibleSchedule2 TestIncompatibleSchedule2 => new(); + public Internal.TableHandles.TestScheduleWithoutPrimaryKey TestScheduleWithoutPrimaryKey => + new(); + public Internal.TableHandles.TestScheduleWithoutScheduleAt TestScheduleWithoutScheduleAt => + new(); + public Internal.TableHandles.TestScheduleWithWrongPrimaryKeyType TestScheduleWithWrongPrimaryKeyType => + new(); + public Internal.TableHandles.TestScheduleWithWrongScheduleAtType TestScheduleWithWrongScheduleAtType => + new(); public Internal.TableHandles.TestTableTaggedEnum TestTableTaggedEnum => new(); public Internal.TableHandles.TestUniqueNotEquatable TestUniqueNotEquatable => new(); } @@ -616,6 +755,28 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } + class DummyScheduledReducer : SpacetimeDB.Internal.IReducer + { + private static readonly TestScheduleIssues.BSATN table = new(); + + public SpacetimeDB.Internal.RawReducerDefV9 MakeReducerDef( + SpacetimeDB.BSATN.ITypeRegistrar registrar + ) => + new( + nameof(DummyScheduledReducer), + [new(nameof(table), table.GetAlgebraicType(registrar))], + null + ); + + public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) + { + TestScheduleIssues.DummyScheduledReducer( + (SpacetimeDB.ReducerContext)ctx, + table.Read(reader) + ); + } + } + class OnReducerWithReservedPrefix : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV9 MakeReducerDef( @@ -664,28 +825,6 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } - class TestIncompatibleScheduleReducer : SpacetimeDB.Internal.IReducer - { - private static readonly TestIncompatibleSchedule.BSATN table = new(); - - public SpacetimeDB.Internal.RawReducerDefV9 MakeReducerDef( - SpacetimeDB.BSATN.ITypeRegistrar registrar - ) => - new( - nameof(TestIncompatibleScheduleReducer), - [new(nameof(table), table.GetAlgebraicType(registrar))], - null - ); - - public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx) - { - TestIncompatibleSchedule.TestIncompatibleScheduleReducer( - (SpacetimeDB.ReducerContext)ctx, - table.Read(reader) - ); - } - } - class TestReducerReturnType : SpacetimeDB.Internal.IReducer { public SpacetimeDB.Internal.RawReducerDefV9 MakeReducerDef( @@ -729,11 +868,11 @@ public static void Main() ); SpacetimeDB.Internal.Module.RegisterReducer<__ReducerWithReservedPrefix>(); + SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); - SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterTable< @@ -745,12 +884,20 @@ public static void Main() SpacetimeDB.Internal.TableHandles.TestDuplicateTableName >(); SpacetimeDB.Internal.Module.RegisterTable< - global::TestIncompatibleSchedule, - SpacetimeDB.Internal.TableHandles.TestIncompatibleSchedule1 + global::TestScheduleIssues, + SpacetimeDB.Internal.TableHandles.TestScheduleWithoutPrimaryKey + >(); + SpacetimeDB.Internal.Module.RegisterTable< + global::TestScheduleIssues, + SpacetimeDB.Internal.TableHandles.TestScheduleWithoutScheduleAt + >(); + SpacetimeDB.Internal.Module.RegisterTable< + global::TestScheduleIssues, + SpacetimeDB.Internal.TableHandles.TestScheduleWithWrongPrimaryKeyType >(); SpacetimeDB.Internal.Module.RegisterTable< - global::TestIncompatibleSchedule, - SpacetimeDB.Internal.TableHandles.TestIncompatibleSchedule2 + global::TestScheduleIssues, + SpacetimeDB.Internal.TableHandles.TestScheduleWithWrongScheduleAtType >(); SpacetimeDB.Internal.Module.RegisterTable< global::TestTableTaggedEnum, diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestIncompatibleSchedule.TestIncompatibleScheduleReducer.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestIncompatibleSchedule.TestIncompatibleScheduleReducer.verified.cs deleted file mode 100644 index 29b0e639c8..0000000000 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestIncompatibleSchedule.TestIncompatibleScheduleReducer.verified.cs +++ /dev/null @@ -1,20 +0,0 @@ -//HintName: TestIncompatibleSchedule.TestIncompatibleScheduleReducer.cs -// -#nullable enable - -partial struct TestIncompatibleSchedule -{ - [System.Diagnostics.CodeAnalysis.Experimental("STDB_UNSTABLE")] - public static void VolatileNonatomicScheduleImmediateTestIncompatibleScheduleReducer( - TestIncompatibleSchedule table - ) - { - using var stream = new MemoryStream(); - using var writer = new BinaryWriter(stream); - new TestIncompatibleSchedule.BSATN().Write(writer, table); - SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate( - nameof(TestIncompatibleScheduleReducer), - stream - ); - } -} // TestIncompatibleSchedule diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestIncompatibleSchedule.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestIncompatibleSchedule.verified.cs deleted file mode 100644 index 9266a73b19..0000000000 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#TestIncompatibleSchedule.verified.cs +++ /dev/null @@ -1,53 +0,0 @@ -//HintName: TestIncompatibleSchedule.cs -// -#nullable enable - -[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Auto)] -partial struct TestIncompatibleSchedule : SpacetimeDB.BSATN.IStructuralReadWrite -{ - public void ReadFields(System.IO.BinaryReader reader) - { - ScheduledId = BSATN.ScheduledId.Read(reader); - ScheduledAt = BSATN.ScheduledAt.Read(reader); - } - - public void WriteFields(System.IO.BinaryWriter writer) - { - BSATN.ScheduledId.Write(writer, ScheduledId); - BSATN.ScheduledAt.Write(writer, ScheduledAt); - } - - public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite - { - internal static readonly SpacetimeDB.BSATN.U64 ScheduledId = new(); - internal static readonly SpacetimeDB.ScheduleAt.BSATN ScheduledAt = new(); - - public TestIncompatibleSchedule Read(System.IO.BinaryReader reader) => - SpacetimeDB.BSATN.IStructuralReadWrite.Read(reader); - - public void Write(System.IO.BinaryWriter writer, TestIncompatibleSchedule value) - { - value.WriteFields(writer); - } - - public SpacetimeDB.BSATN.AlgebraicType.Ref GetAlgebraicType( - SpacetimeDB.BSATN.ITypeRegistrar registrar - ) => - registrar.RegisterType( - _ => new SpacetimeDB.BSATN.AlgebraicType.Product( - new SpacetimeDB.BSATN.AggregateElement[] - { - new(nameof(ScheduledId), ScheduledId.GetAlgebraicType(registrar)), - new(nameof(ScheduledAt), ScheduledAt.GetAlgebraicType(registrar)) - } - ) - ); - - SpacetimeDB.BSATN.AlgebraicType SpacetimeDB.BSATN.IReadWrite.GetAlgebraicType( - SpacetimeDB.BSATN.ITypeRegistrar registrar - ) => GetAlgebraicType(registrar); - } - - public ulong ScheduledId; - public SpacetimeDB.ScheduleAt ScheduledAt; -} // TestIncompatibleSchedule diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt index d5844b566b..f68a850679 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module.verified.txt @@ -87,31 +87,6 @@ public partial record TestTableTaggedEnum : SpacetimeDB.TaggedEnum<(int X, int Y }, {/* -[SpacetimeDB.Table( -^^^^^^^^^^^^^^^^^^^ - Name = "TestIncompatibleSchedule1", -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Scheduled = nameof(TestIncompatibleScheduleReducer) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -)] -^^ -[SpacetimeDB.Table(Name = "TestIncompatibleSchedule2")] -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -public partial struct TestIncompatibleSchedule -*/ - Message: Schedule adds extra fields to the row type. Either all `[Table]` attributes should have a `Schedule`, or none of them., - Severity: Error, - Descriptor: { - Id: STDB0010, - Title: Incompatible `[Table(Schedule)]` attributes, - MessageFormat: Schedule adds extra fields to the row type. Either all `[Table]` attributes should have a `Schedule`, or none of them., - Category: SpacetimeDB, - DefaultSeverity: Error, - IsEnabledByDefault: true - } - }, - {/* - [SpacetimeDB.Table] ^^^^^^^^^^^^^^^^^^^ [SpacetimeDB.Index] @@ -153,6 +128,98 @@ public partial struct TestIndexWithEmptyColumns { } } }, {/* + +[SpacetimeDB.Table( + ^^^^^^^^^^^^^^^^^^ + Name = "TestScheduleWithoutPrimaryKey", +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scheduled = "DummyScheduledReducer", +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ScheduledAt = nameof(ScheduleAtCorrectType) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +)] +^ +[SpacetimeDB.Table( +*/ + Message: TestScheduleWithoutPrimaryKey is a scheduled table but doesn't have a primary key of type `ulong`., + Severity: Error, + Descriptor: { + Id: STDB0012, + Title: Invalid scheduled table declaration, + MessageFormat: {0}, + Category: SpacetimeDB, + DefaultSeverity: Error, + IsEnabledByDefault: true + } + }, + {/* +)] +[SpacetimeDB.Table( + ^^^^^^^^^^^^^^^^^^ + Name = "TestScheduleWithWrongPrimaryKeyType", +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scheduled = "DummyScheduledReducer", +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ScheduledAt = nameof(ScheduleAtCorrectType) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +)] +^ +[SpacetimeDB.Table(Name = "TestScheduleWithoutScheduleAt", Scheduled = "DummyScheduledReducer")] +*/ + Message: TestScheduleWithWrongPrimaryKeyType is a scheduled table but doesn't have a primary key of type `ulong`., + Severity: Error, + Descriptor: { + Id: STDB0012, + Title: Invalid scheduled table declaration, + MessageFormat: {0}, + Category: SpacetimeDB, + DefaultSeverity: Error, + IsEnabledByDefault: true + } + }, + {/* +)] +[SpacetimeDB.Table(Name = "TestScheduleWithoutScheduleAt", Scheduled = "DummyScheduledReducer")] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[SpacetimeDB.Table( +*/ + Message: Item ScheduledAt not found., + Severity: Error, + Descriptor: { + Id: STDB0012, + Title: Invalid scheduled table declaration, + MessageFormat: {0}, + Category: SpacetimeDB, + DefaultSeverity: Error, + IsEnabledByDefault: true + } + }, + {/* +[SpacetimeDB.Table(Name = "TestScheduleWithoutScheduleAt", Scheduled = "DummyScheduledReducer")] +[SpacetimeDB.Table( + ^^^^^^^^^^^^^^^^^^ + Name = "TestScheduleWithWrongScheduleAtType", +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Scheduled = "DummyScheduledReducer", +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ScheduledAt = nameof(ScheduleAtWrongType) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +)] +^ +public partial struct TestScheduleIssues +*/ + Message: TestScheduleWithWrongScheduleAtType is a scheduled table but doesn't have a primary key of type `ulong`., + Severity: Error, + Descriptor: { + Id: STDB0012, + Title: Invalid scheduled table declaration, + MessageFormat: {0}, + Category: SpacetimeDB, + DefaultSeverity: Error, + IsEnabledByDefault: true + } + }, + {/* [SpacetimeDB.Reducer] public static int TestReducerReturnType(ReducerContext ctx) => 0; ^^^ diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/Lib.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/Lib.cs index 84b699424a..5938e29383 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/Lib.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/Lib.cs @@ -118,6 +118,10 @@ public static partial class Timers [SpacetimeDB.Table(Scheduled = nameof(SendScheduledMessage))] public partial struct SendMessageTimer { + [PrimaryKey] + [AutoInc] + public ulong ScheduledId; + public ScheduleAt ScheduledAt; public string Text; } diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs index 06305513a2..97b9b5cfd1 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs @@ -747,13 +747,13 @@ static SpacetimeDB.Internal.RawTableDefV9 SpacetimeDB.Internal.ITableView< new global::Timers.SendMessageTimer.BSATN() .GetAlgebraicType(registrar) .Ref_, - PrimaryKey: [1], + PrimaryKey: [0], Indexes: [ new( Name: null, AccessorName: "ScheduledId", - Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([1]) + Algorithm: new SpacetimeDB.Internal.RawIndexAlgorithm.BTree([0]) ) ], Constraints: @@ -761,19 +761,19 @@ static SpacetimeDB.Internal.RawTableDefV9 SpacetimeDB.Internal.ITableView< SpacetimeDB.Internal.ITableView< SendMessageTimer, global::Timers.SendMessageTimer - >.MakeUniqueConstraint(1) + >.MakeUniqueConstraint(0) ], Sequences: [ SpacetimeDB.Internal.ITableView< SendMessageTimer, global::Timers.SendMessageTimer - >.MakeSequence(1) + >.MakeSequence(0) ], Schedule: SpacetimeDB.Internal.ITableView< SendMessageTimer, global::Timers.SendMessageTimer - >.MakeSchedule("SendScheduledMessage", 2), + >.MakeSchedule("SendScheduledMessage", 1), TableType: SpacetimeDB.Internal.TableType.User, TableAccess: SpacetimeDB.Internal.TableAccess.Private ); diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.SendMessageTimer.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.SendMessageTimer.verified.cs index 5cc70ec992..4ada75d442 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.SendMessageTimer.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.SendMessageTimer.verified.cs @@ -4,28 +4,27 @@ partial class Timers { - [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Auto)] partial struct SendMessageTimer : SpacetimeDB.BSATN.IStructuralReadWrite { public void ReadFields(System.IO.BinaryReader reader) { - Text = BSATN.Text.Read(reader); ScheduledId = BSATN.ScheduledId.Read(reader); ScheduledAt = BSATN.ScheduledAt.Read(reader); + Text = BSATN.Text.Read(reader); } public void WriteFields(System.IO.BinaryWriter writer) { - BSATN.Text.Write(writer, Text); BSATN.ScheduledId.Write(writer, ScheduledId); BSATN.ScheduledAt.Write(writer, ScheduledAt); + BSATN.Text.Write(writer, Text); } public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite { - internal static readonly SpacetimeDB.BSATN.String Text = new(); internal static readonly SpacetimeDB.BSATN.U64 ScheduledId = new(); internal static readonly SpacetimeDB.ScheduleAt.BSATN ScheduledAt = new(); + internal static readonly SpacetimeDB.BSATN.String Text = new(); public Timers.SendMessageTimer Read(System.IO.BinaryReader reader) => SpacetimeDB.BSATN.IStructuralReadWrite.Read(reader); @@ -42,9 +41,9 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar _ => new SpacetimeDB.BSATN.AlgebraicType.Product( new SpacetimeDB.BSATN.AggregateElement[] { - new(nameof(Text), Text.GetAlgebraicType(registrar)), new(nameof(ScheduledId), ScheduledId.GetAlgebraicType(registrar)), - new(nameof(ScheduledAt), ScheduledAt.GetAlgebraicType(registrar)) + new(nameof(ScheduledAt), ScheduledAt.GetAlgebraicType(registrar)), + new(nameof(Text), Text.GetAlgebraicType(registrar)) } ) ); @@ -53,8 +52,5 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar SpacetimeDB.BSATN.ITypeRegistrar registrar ) => GetAlgebraicType(registrar); } - - public ulong ScheduledId; - public SpacetimeDB.ScheduleAt ScheduledAt; } // SendMessageTimer } // Timers diff --git a/crates/bindings-csharp/Codegen/Diag.cs b/crates/bindings-csharp/Codegen/Diag.cs index 27c1eaff0a..1635bc0b16 100644 --- a/crates/bindings-csharp/Codegen/Diag.cs +++ b/crates/bindings-csharp/Codegen/Diag.cs @@ -105,4 +105,10 @@ IEnumerable fullNames $"Several reducers are assigned to the same lifecycle kind {ctx.kind}: {string.Join(", ", ctx.fullNames)}", ctx => Location.None ); + + public static readonly ErrorDescriptor<( + AttributeData attr, + string message + )> InvalidScheduledDeclaration = + new(group, "Invalid scheduled table declaration", ctx => $"{ctx.message}", ctx => ctx.attr); } diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index a3c44dd1c3..018094c7d0 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -154,7 +154,7 @@ record TableView public readonly bool IsPublic; public readonly Scheduled? Scheduled; - public TableView(TableDeclaration table, AttributeData data) + public TableView(TableDeclaration table, AttributeData data, DiagReporter diag) { var attr = data.ParseAs(); @@ -162,18 +162,28 @@ public TableView(TableDeclaration table, AttributeData data) IsPublic = attr.Public; if (attr.Scheduled is { } reducer) { - Scheduled = new(reducer, table.Members.Select(m => m.Name).IndexOf(attr.ScheduledAt)); - if (table.GetPrimaryKey(this) is not { } pk || table.Members[pk].Type != "ulong") + try { - throw new InvalidOperationException( - $"{Name} is a scheduled table but doesn't have a primary key of type `ulong`." + Scheduled = new( + reducer, + table.Members.Select(m => m.Name).IndexOf(attr.ScheduledAt) ); + if (table.GetPrimaryKey(this) is not { } pk || table.Members[pk].Type != "ulong") + { + throw new InvalidOperationException( + $"{Name} is a scheduled table but doesn't have a primary key of type `ulong`." + ); + } + if (table.Members[Scheduled.ScheduledAtColumn].Type != "SpacetimeDB.ScheduleAt") + { + throw new InvalidOperationException( + $"{Name}.{attr.ScheduledAt} is marked with `ScheduledAt`, but doesn't have the expected type `SpacetimeDB.ScheduleAt`." + ); + } } - if (table.Members[Scheduled.ScheduledAtColumn].Type != "SpacetimeDB.ScheduleAt") + catch (Exception e) { - throw new InvalidOperationException( - $"The `{Name}.{attr.ScheduledAt}` column is marked with `ScheduledAt`, but doesn't have the expected type `SpacetimeDB.ScheduleAt`." - ); + diag.Report(ErrorDescriptor.InvalidScheduledDeclaration, (data, e.Message)); } } } @@ -289,7 +299,9 @@ public TableDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter di container = container.ContainingType; } - Views = new(context.Attributes.Select(a => new TableView(this, a)).ToImmutableArray()); + Views = new( + context.Attributes.Select(a => new TableView(this, a, diag)).ToImmutableArray() + ); Indexes = new( context .TargetSymbol.GetAttributes() diff --git a/crates/bindings-csharp/Runtime/Attrs.cs b/crates/bindings-csharp/Runtime/Attrs.cs index 73001a1a49..a3390fb343 100644 --- a/crates/bindings-csharp/Runtime/Attrs.cs +++ b/crates/bindings-csharp/Runtime/Attrs.cs @@ -14,7 +14,7 @@ public enum ColumnAttrs : byte PrimaryKeyAuto = PrimaryKey | AutoInc, } - [AttributeUsage(AttributeTargets.Field)] + [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] public abstract class ColumnAttribute : Attribute { public string? Table { get; init; }