From 653b20030aea4245ff2fdc241a30acb414323981 Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Wed, 8 Jul 2026 13:46:32 -0500 Subject: [PATCH 1/5] Clean warnings --- crates/bindings-csharp/BSATN.Codegen/Type.cs | 12 +-- .../BSATN.Runtime/BSATN/Runtime.cs | 8 +- .../BSATN.Runtime/BSATN/U128.cs | 4 +- .../bindings-csharp/BSATN.Runtime/Builtins.cs | 38 +++---- .../BSATN.Runtime/QueryBuilder.cs | 53 ++-------- crates/bindings-csharp/Codegen.Tests/Tests.cs | 15 +-- crates/bindings-csharp/Codegen/Module.cs | 98 +++++++++---------- crates/bindings-csharp/Directory.Build.props | 6 -- crates/bindings-csharp/Runtime/Http.cs | 24 ++--- .../Runtime/Internal/Bounds.cs | 5 +- .../bindings-csharp/Runtime/Internal/FFI.cs | 2 +- .../Runtime/Internal/ITable.cs | 2 + .../Runtime/Internal/Module.cs | 14 +-- crates/bindings-csharp/Runtime/JwtClaims.cs | 12 ++- crates/bindings-csharp/Runtime/Router.cs | 4 +- .../Runtime/TransactionalContextState.cs | 6 +- 16 files changed, 126 insertions(+), 177 deletions(-) diff --git a/crates/bindings-csharp/BSATN.Codegen/Type.cs b/crates/bindings-csharp/BSATN.Codegen/Type.cs index c639b6db8ae..537c1c9f2dc 100644 --- a/crates/bindings-csharp/BSATN.Codegen/Type.cs +++ b/crates/bindings-csharp/BSATN.Codegen/Type.cs @@ -740,13 +740,13 @@ public override string ToString() => write = "value.WriteFields(writer);"; - var declHashName = (MemberDeclaration decl) => $"___hash{decl.Name}"; + static string DeclHashName(MemberDeclaration decl) => $"___hash{decl.Name}"; getHashCode = $$""" - {{string.Join("\n", bsatnDecls.Select(decl => decl.Type.GetHashCodeStatement(decl.Identifier, declHashName(decl))))}} + {{string.Join("\n", bsatnDecls.Select(decl => decl.Type.GetHashCodeStatement(decl.Identifier, DeclHashName(decl))))}} return {{JoinOrValue( " ^\n ", - bsatnDecls.Select(declHashName), + bsatnDecls.Select(DeclHashName), "0" // if there are no members, the hash is 0. )}}; """; @@ -789,7 +789,7 @@ public override int GetHashCode() // If we are a reference type, various equality methods need to take nullable references. // If we are a value type, everything is pleasantly by-value. var fullNameMaybeRef = $"{FullName}{(Scope.IsStruct ? "" : "?")}"; - var declEqualsName = (MemberDeclaration decl) => $"___eq{decl.Name}"; + static string DeclEqualsName(MemberDeclaration decl) => $"___eq{decl.Name}"; extensions.Contents.Append( $$""" @@ -798,10 +798,10 @@ public override int GetHashCode() public bool Equals({{fullNameMaybeRef}} that) { {{(Scope.IsStruct ? "" : "if (((object?)that) == null) { return false; }\n ")}} - {{string.Join("\n", bsatnDecls.Select(decl => decl.Type.EqualsStatement($"this.{decl.Identifier}", $"that.{decl.Identifier}", declEqualsName(decl))))}} + {{string.Join("\n", bsatnDecls.Select(decl => decl.Type.EqualsStatement($"this.{decl.Identifier}", $"that.{decl.Identifier}", DeclEqualsName(decl))))}} return {{JoinOrValue( " &&\n ", - bsatnDecls.Select(declEqualsName), + bsatnDecls.Select(DeclEqualsName), "true" // if there are no elements, the structs are equal :) )}}; } diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs index eeba596929b..badbb277c92 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs @@ -129,7 +129,9 @@ public interface IReadWrite /// Note: the [Type] macro rejects enums with explicitly set values (see Codegen.Tests), /// so this array is guaranteed to be continuous and indexed starting from 0. /// +#pragma warning disable CA2263, IDE0305 // netstandard2.1 lacks the generic Enum overloads. private static readonly T[] TagToValue = Enum.GetValues(typeof(T)).Cast().ToArray(); +#pragma warning restore CA2263, IDE0305 public T Read(BinaryReader reader) { @@ -177,9 +179,9 @@ public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) => registrar.RegisterType( (_) => new AlgebraicType.Sum( - Enum.GetNames(typeof(T)) - .Select(name => new AggregateElement(name, AlgebraicType.Unit)) - .ToArray() +#pragma warning disable CA2263 // netstandard2.1 lacks the generic Enum overloads. + [.. Enum.GetNames(typeof(T)).Select(name => new AggregateElement(name, AlgebraicType.Unit))] +#pragma warning restore CA2263 ) ); } diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN/U128.cs b/crates/bindings-csharp/BSATN.Runtime/BSATN/U128.cs index 936e4756d1b..5158d9fc213 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN/U128.cs +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN/U128.cs @@ -45,8 +45,8 @@ public static U128 FromBytesBigEndian(ReadOnlySpan bytes) ); } - var upper = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(0, 8)); - var lower = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(8, 8)); + var upper = BinaryPrimitives.ReadUInt64BigEndian(bytes[..8]); + var lower = BinaryPrimitives.ReadUInt64BigEndian(bytes[8..16]); return new U128(upper, lower); } diff --git a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs index e204985b54a..f8cb375f284 100644 --- a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs +++ b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs @@ -357,7 +357,7 @@ public static implicit operator DateTimeOffset(Timestamp t) => DateTimeOffset.UnixEpoch.AddTicks(t.MicrosecondsSinceUnixEpoch * Util.TicksPerMicrosecond); public static implicit operator Timestamp(DateTimeOffset offset) => - new Timestamp(offset.Subtract(DateTimeOffset.UnixEpoch).Ticks / Util.TicksPerMicrosecond); + new(offset.Subtract(DateTimeOffset.UnixEpoch).Ticks / Util.TicksPerMicrosecond); // For backwards-compatibility. public readonly DateTimeOffset ToStd() => this; @@ -373,7 +373,7 @@ public override readonly string ToString() public static readonly Timestamp UNIX_EPOCH = new(0); public static Timestamp FromTimeDurationSinceUnixEpoch(TimeDuration timeDuration) => - new Timestamp(timeDuration.Microseconds); + new(timeDuration.Microseconds); public readonly TimeDuration ToTimeDurationSinceUnixEpoch() => TimeDurationSince(UNIX_EPOCH); @@ -383,13 +383,13 @@ public static Timestamp FromTimeSpanSinceUnixEpoch(TimeSpan timeSpan) => public readonly TimeSpan ToTimeSpanSinceUnixEpoch() => (TimeSpan)ToTimeDurationSinceUnixEpoch(); public readonly TimeDuration TimeDurationSince(Timestamp earlier) => - new TimeDuration(checked(MicrosecondsSinceUnixEpoch - earlier.MicrosecondsSinceUnixEpoch)); + new(checked(MicrosecondsSinceUnixEpoch - earlier.MicrosecondsSinceUnixEpoch)); public static Timestamp operator +(Timestamp point, TimeDuration interval) => - new Timestamp(checked(point.MicrosecondsSinceUnixEpoch + interval.Microseconds)); + new(checked(point.MicrosecondsSinceUnixEpoch + interval.Microseconds)); public static Timestamp operator -(Timestamp point, TimeDuration interval) => - new Timestamp(checked(point.MicrosecondsSinceUnixEpoch - interval.Microseconds)); + new(checked(point.MicrosecondsSinceUnixEpoch - interval.Microseconds)); public readonly int CompareTo(Timestamp that) { @@ -492,10 +492,10 @@ public static implicit operator TimeDuration(TimeSpan timeSpan) => new(timeSpan.Ticks / Util.TicksPerMicrosecond); public static TimeDuration operator +(TimeDuration lhs, TimeDuration rhs) => - new TimeDuration(checked(lhs.Microseconds + rhs.Microseconds)); + new(checked(lhs.Microseconds + rhs.Microseconds)); public static TimeDuration operator -(TimeDuration lhs, TimeDuration rhs) => - new TimeDuration(checked(lhs.Microseconds + rhs.Microseconds)); + new(checked(lhs.Microseconds + rhs.Microseconds)); // For backwards-compatibility. public readonly TimeSpan ToStd() => this; @@ -574,7 +574,7 @@ long microsSinceUnixEpoch // --- auto-generated --- private ScheduleAt() { } - internal enum @enum : byte + internal enum ScheduleAtVariant : byte { Interval, Time, @@ -586,15 +586,15 @@ public sealed record Time(Timestamp Time_) : ScheduleAt; public readonly partial struct BSATN : IReadWrite { - internal static readonly SpacetimeDB.BSATN.Enum<@enum> __enumTag = new(); + internal static readonly SpacetimeDB.BSATN.Enum __enumTag = new(); internal static readonly TimeDuration.BSATN Interval = new(); internal static readonly Timestamp.BSATN Time = new(); public ScheduleAt Read(BinaryReader reader) => __enumTag.Read(reader) switch { - @enum.Interval => new Interval(Interval.Read(reader)), - @enum.Time => new Time(Time.Read(reader)), + ScheduleAtVariant.Interval => new Interval(Interval.Read(reader)), + ScheduleAtVariant.Time => new Time(Time.Read(reader)), _ => throw new InvalidOperationException( "Invalid tag value, this state should be unreachable." ), @@ -605,12 +605,12 @@ public void Write(BinaryWriter writer, ScheduleAt value) switch (value) { case Interval(var inner): - __enumTag.Write(writer, @enum.Interval); + __enumTag.Write(writer, ScheduleAtVariant.Interval); Interval.Write(writer, inner); break; case Time(var inner): - __enumTag.Write(writer, @enum.Time); + __enumTag.Write(writer, ScheduleAtVariant.Time); Time.Write(writer, inner); break; } @@ -682,7 +682,7 @@ public T UnwrapOrElse(Func f) => private Result() { } - internal enum @enum : byte + internal enum ResultVariant : byte { Ok, Err, @@ -702,15 +702,15 @@ private enum Variant : byte where OkRW : struct, IReadWrite where ErrRW : struct, IReadWrite { - private static readonly SpacetimeDB.BSATN.Enum<@enum> __enumTag = new(); + private static readonly SpacetimeDB.BSATN.Enum __enumTag = new(); private static readonly OkRW okRW = new(); private static readonly ErrRW errRW = new(); public Result Read(BinaryReader reader) => __enumTag.Read(reader) switch { - @enum.Ok => new OkR(okRW.Read(reader)), - @enum.Err => new ErrR(errRW.Read(reader)), + ResultVariant.Ok => new OkR(okRW.Read(reader)), + ResultVariant.Err => new ErrR(errRW.Read(reader)), _ => throw new InvalidOperationException(), }; @@ -719,12 +719,12 @@ public void Write(BinaryWriter writer, Result value) switch (value) { case OkR(var v): - __enumTag.Write(writer, @enum.Ok); + __enumTag.Write(writer, ResultVariant.Ok); okRW.Write(writer, v); break; case ErrR(var e): - __enumTag.Write(writer, @enum.Err); + __enumTag.Write(writer, ResultVariant.Err); errRW.Write(writer, e); break; } diff --git a/crates/bindings-csharp/BSATN.Runtime/QueryBuilder.cs b/crates/bindings-csharp/BSATN.Runtime/QueryBuilder.cs index db16bd56cd7..e8d52a084e6 100644 --- a/crates/bindings-csharp/BSATN.Runtime/QueryBuilder.cs +++ b/crates/bindings-csharp/BSATN.Runtime/QueryBuilder.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace SpacetimeDB; using System; @@ -68,14 +66,9 @@ public interface IQuery string ToSql(); } -public readonly struct BoolExpr +public readonly struct BoolExpr(string sql) { - public string Sql { get; } - - public BoolExpr(string sql) - { - Sql = sql; - } + public string Sql { get; } = sql; public BoolExpr And(BoolExpr other) => new($"({Sql} AND {other.Sql})"); @@ -120,18 +113,9 @@ internal IxJoinEq(string leftRefSql, string rightRefSql) } } -public readonly struct Col +public readonly struct Col(string tableName, string columnName) where TValue : notnull { - private readonly string tableName; - private readonly string columnName; - - public Col(string tableName, string columnName) - { - this.tableName = tableName; - this.columnName = columnName; - } - internal string RefSql => $"{SqlFormat.QuoteIdent(tableName)}.{SqlFormat.QuoteIdent(columnName)}"; @@ -162,18 +146,9 @@ public Col(string tableName, string columnName) public override string ToString() => RefSql; } -public readonly struct IxCol +public readonly struct IxCol(string tableName, string columnName) where TValue : notnull { - private readonly string tableName; - private readonly string columnName; - - public IxCol(string tableName, string columnName) - { - this.tableName = tableName; - this.columnName = columnName; - } - internal string RefSql => $"{SqlFormat.QuoteIdent(tableName)}.{SqlFormat.QuoteIdent(columnName)}"; @@ -187,19 +162,9 @@ public IxJoinEq Eq(IxCol other) = public override string ToString() => RefSql; } -public sealed class Table : IQuery +public sealed class Table(string tableName, TCols cols, TIxCols ixCols) + : IQuery { - private readonly string tableName; - private readonly TCols cols; - private readonly TIxCols ixCols; - - public Table(string tableName, TCols cols, TIxCols ixCols) - { - this.tableName = tableName; - this.cols = cols; - this.ixCols = ixCols; - } - internal string TableRefSql => SqlFormat.QuoteIdent(tableName); internal TCols Cols => cols; @@ -229,7 +194,7 @@ public LeftSemiJoin L >( Table right, Func> on - ) => new(this, right, on(ixCols, right.ixCols), whereExpr: null); + ) => new(this, right, on(ixCols, right.IxCols), whereExpr: null); public RightSemiJoin RightSemijoin< TRightRow, @@ -238,7 +203,7 @@ public RightSemiJoin >( Table right, Func> on - ) => new(this, right, on(ixCols, right.ixCols), leftWhereExpr: null); + ) => new(this, right, on(ixCols, right.IxCols), leftWhereExpr: null); } public sealed class FromWhere : IQuery @@ -889,7 +854,7 @@ public static string FormatHexLiteral(string hex) var s = hex; if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { - s = s.Substring(2); + s = s[2..]; } s = s.Replace("-", string.Empty); diff --git a/crates/bindings-csharp/Codegen.Tests/Tests.cs b/crates/bindings-csharp/Codegen.Tests/Tests.cs index a70a1bd3482..4133819ef9c 100644 --- a/crates/bindings-csharp/Codegen.Tests/Tests.cs +++ b/crates/bindings-csharp/Codegen.Tests/Tests.cs @@ -26,18 +26,11 @@ public static class GeneratorSnapshotTests record struct StepOutput(string Key, IncrementalStepRunReason Reason, object Value); - private class Fixture + private class Fixture(string projectDir, CSharpCompilation sampleCompilation) { - private readonly string projectDir; - public CSharpCompilation SampleCompilation { get; } - public CSharpParseOptions ParseOptions { get; } - - public Fixture(string projectDir, CSharpCompilation sampleCompilation) - { - this.projectDir = projectDir; - SampleCompilation = sampleCompilation; - ParseOptions = (CSharpParseOptions)sampleCompilation.SyntaxTrees.First().Options; - } + public CSharpCompilation SampleCompilation { get; } = sampleCompilation; + public CSharpParseOptions ParseOptions { get; } = + (CSharpParseOptions)sampleCompilation.SyntaxTrees.First().Options; public static async Task Compile(string name) { diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 2ded9cb3412..3e76b20c6ef 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -762,15 +762,15 @@ public sealed class {{{identifierName}}}Index /// Represents a generated accessor for a table, providing different access patterns /// and visibility levels for the underlying table data. /// - /// Name of the generated accessor type - /// Fully qualified name of the table type - /// C# source code for the accessor implementation - /// C# property getter for accessing the accessor + /// Name of the generated accessor type + /// Fully qualified name of the table type + /// C# source code for the accessor implementation + /// C# property getter for accessing the accessor public record struct GeneratedTableAccessor( - string tableAccessorName, - string tableName, - string tableAccessor, - string getter + string TableAccessorName, + string TableName, + string TableAccessor, + string Getter ); /// @@ -862,10 +862,10 @@ v.Scheduled is { } scheduled } public record struct GeneratedReadOnlyAccessor( - string tableAccessorName, - string tableName, - string readOnlyAccessor, - string readOnlyGetter + string TableAccessorName, + string TableName, + string ReadOnlyAccessor, + string ReadOnlyGetter ); public IEnumerable GenerateReadOnlyAccessors() @@ -1023,14 +1023,14 @@ public readonly partial struct QueryBuilder /// /// Represents a default value for a table field, used during table creation. /// - /// Name of the table containing the field - /// Index of the column in the table - /// String representation of the default value + /// Name of the table containing the field + /// Index of the column in the table + /// String representation of the default value /// BSATN Type name of the default value public record struct FieldDefaultValue( - string tableName, - string columnId, - string value, + string TableName, + string ColumnId, + string Value, string BSATNTypeName ); @@ -1059,8 +1059,7 @@ public IEnumerable GenerateDefaultValues() .Select(m => m.Attrs.FirstOrDefault(a => a.Mask == ColumnAttrs.Default)) ); - var withDefaultValues = - fieldsWithDefaultValues as ColumnDeclaration[] ?? fieldsWithDefaultValues.ToArray(); + var withDefaultValues = fieldsWithDefaultValues as ColumnDeclaration[] ?? [.. fieldsWithDefaultValues]; foreach (var fieldsWithDefaultValue in withDefaultValues) { if ( @@ -1722,49 +1721,48 @@ public string GenerateClass() if (HasWrongSignature) { - bodyLines = new[] - { + bodyLines = + [ "throw new System.InvalidOperationException(\"Invalid procedure signature.\");", - }; + ]; } else if (HasTxWrapper) { - var successLines = txPayloadIsUnit - ? new[] { "return System.Array.Empty();" } - : new[] - { + string[] successLines = txPayloadIsUnit + ? ["return System.Array.Empty();"] + : + [ "using var output = new MemoryStream();", "using var writer = new BinaryWriter(output);", "__txReturnRW.Write(writer, outcome.Value!);", "return output.ToArray();", - }; + ]; - bodyLines = new[] - { + bodyLines = + [ $"var outcome = {invocation};", "if (!outcome.IsSuccess)", "{", " throw outcome.Error ?? new System.InvalidOperationException(\"Transaction failed.\");", "}", - } - .Concat(successLines) - .ToArray(); + .. successLines, + ]; } else if (ReturnType.Name == "SpacetimeDB.Unit") { - bodyLines = new[] { $"{invocation};", "return System.Array.Empty();" }; + bodyLines = [$"{invocation};", "return System.Array.Empty();"]; } else { var serializer = $"new {ReturnType.ToBSATNString()}()"; - bodyLines = new[] - { + bodyLines = + [ $"var result = {invocation};", "using var output = new MemoryStream();", "using var writer = new BinaryWriter(output);", $"{serializer}.Write(writer, result);", "return output.ToArray();", - }; + ]; } var invokeBody = string.Join("\n", bodyLines.Select(line => $" {line}")); @@ -2348,8 +2346,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) tables .SelectMany((t, ct) => t.GenerateTableAccessors()) .WithTrackingName("SpacetimeDB.Table.GenerateTableAccessors"), - v => v.tableAccessorName, - v => v.tableName + v => v.TableAccessorName, + v => v.TableName ); var readOnlyAccessors = CollectDistinct( @@ -2358,8 +2356,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) tables .SelectMany((t, ct) => t.GenerateReadOnlyAccessors()) .WithTrackingName("SpacetimeDB.Table.GenerateReadOnlyAccessors"), - v => v.tableAccessorName + "ReadOnly", - v => v.tableName + v => v.TableAccessorName + "ReadOnly", + v => v.TableName ); var rlsFilters = context @@ -2391,8 +2389,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) tables .SelectMany((t, ct) => t.GenerateDefaultValues()) .WithTrackingName("SpacetimeDB.Table.GenerateDefaultValues"), - v => v.tableName + "_" + v.columnId, - v => v.tableName + "_" + v.columnId + v => v.TableName + "_" + v.ColumnId, + v => v.TableName + "_" + v.ColumnId ); var moduleOutputInputs = tableAccessors @@ -2749,7 +2747,7 @@ internal HandlerTxContext(Internal.TxContext inner) : base(inner) {} } public sealed class Local : global::SpacetimeDB.LocalBase { - {{string.Join("\n", tableAccessors.Select(v => v.getter))}} + {{string.Join("\n", tableAccessors.Select(v => v.Getter))}} } public sealed record ViewContext : DbContext, Internal.IViewContext @@ -2775,7 +2773,7 @@ internal AnonymousViewContext(Internal.LocalReadOnly db) } namespace SpacetimeDB.Internal.TableHandles { - {{string.Join("\n", tableAccessors.Select(v => v.tableAccessor))}} + {{string.Join("\n", tableAccessors.Select(v => v.TableAccessor))}} } {{string.Join("\n", @@ -2788,12 +2786,12 @@ namespace SpacetimeDB.Internal.TableHandles { )}} namespace SpacetimeDB.Internal.ViewHandles { - {{string.Join("\n", readOnlyAccessors.Array.Select(v => v.readOnlyAccessor))}} + {{string.Join("\n", readOnlyAccessors.Array.Select(v => v.ReadOnlyAccessor))}} } namespace SpacetimeDB.Internal { public sealed partial class LocalReadOnly { - {{string.Join("\n", readOnlyAccessors.Select(v => v.readOnlyGetter))}} + {{string.Join("\n", readOnlyAccessors.Select(v => v.ReadOnlyGetter))}} } } @@ -2865,7 +2863,7 @@ public static void Main() { {{string.Join( "\n", - tableAccessors.Select(t => $"SpacetimeDB.Internal.Module.RegisterTable<{t.tableName}, SpacetimeDB.Internal.TableHandles.{EscapeIdentifier(t.tableAccessorName)}>();") + tableAccessors.Select(t => $"SpacetimeDB.Internal.Module.RegisterTable<{t.TableName}, SpacetimeDB.Internal.TableHandles.{EscapeIdentifier(t.TableAccessorName)}>();") )}} {{( httpRouters.Array.FirstOrDefault(r => r.IsValid) is { } router @@ -2883,9 +2881,9 @@ public static void Main() { + $"var value = new {d.BSATNTypeName}();\n" + "__memoryStream.Position = 0;\n" + "__memoryStream.SetLength(0);\n" - + $"value.Write(__writer, {d.value});\n" + + $"value.Write(__writer, {d.Value});\n" + "var array = __memoryStream.ToArray();\n" - + $"SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\"{d.tableName}\", {d.columnId}, array);" + + $"SpacetimeDB.Internal.Module.RegisterTableDefaultValue(\"{d.TableName}\", {d.ColumnId}, array);" + "\n}\n") )}} } diff --git a/crates/bindings-csharp/Directory.Build.props b/crates/bindings-csharp/Directory.Build.props index 0b47ecdad6b..42d61d11810 100644 --- a/crates/bindings-csharp/Directory.Build.props +++ b/crates/bindings-csharp/Directory.Build.props @@ -25,12 +25,6 @@ true $(NoWarn);CS1591;CS1574 - - $(WarningsNotAsErrors); - CA1822;CA2263; - IDE0005;IDE0028;IDE0039;IDE0057;IDE0065;IDE0078;IDE0090; - IDE0240;IDE0290;IDE0300;IDE0301;IDE0305;IDE0306;IDE1006 - diff --git a/crates/bindings-csharp/Runtime/Http.cs b/crates/bindings-csharp/Runtime/Http.cs index be98b704fcc..8dfbe262e76 100644 --- a/crates/bindings-csharp/Runtime/Http.cs +++ b/crates/bindings-csharp/Runtime/Http.cs @@ -1,6 +1,7 @@ namespace SpacetimeDB; using System; +using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using System.IO; using System.Linq; @@ -57,7 +58,7 @@ public HttpHeader(string name, string value) /// public readonly record struct HttpBody(byte[] Bytes) { - public static HttpBody Empty => new(Array.Empty()); + public static HttpBody Empty => new([]); public byte[] ToBytes() => Bytes; @@ -82,7 +83,7 @@ public sealed class HttpRequest public HttpMethod Method { get; init; } = HttpMethod.Get; /// HTTP headers to include with the request. - public List Headers { get; init; } = new(); + public List Headers { get; init; } = []; /// Request body bytes. public HttpBody Body { get; init; } = HttpBody.Empty; @@ -271,6 +272,7 @@ public Result Get(string uri, TimeSpan? timeout = null) /// } /// /// + [SuppressMessage("Performance", "CA1822", Justification = "Public instance API exposed through ProcedureContext.Http.")] public Result Send(HttpRequest request) { // The host syscall expects BSATN-encoded spacetimedb_lib::http::Request bytes. @@ -306,10 +308,7 @@ public Result Send(HttpRequest request) var requestWire = new HttpRequestWire { Method = ToWireMethod(request.Method), - Headers = new HttpHeadersWire - { - Entries = request.Headers.Select(ToWireHeader).ToArray(), - }, + Headers = new HttpHeadersWire { Entries = [.. request.Headers.Select(ToWireHeader)] }, Timeout = timeout is null ? null : new HttpTimeoutWire { Timeout = (TimeDuration)timeout.Value }, @@ -423,9 +422,7 @@ internal static HttpRequest FromWire(HttpRequestWire requestWire, byte[] body) = { Uri = requestWire.Uri, Method = FromWireMethod(requestWire.Method), - Headers = requestWire - .Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false)) - .ToList(), + Headers = [.. requestWire.Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false))], Body = new HttpBody(body), Version = FromWireVersion(requestWire.Version), }; @@ -434,10 +431,7 @@ internal static (HttpResponseWire Response, byte[] Body) ToWire(HttpResponse res ( new HttpResponseWire { - Headers = new HttpHeadersWire - { - Entries = response.Headers.Select(ToWireHeader).ToArray(), - }, + Headers = new HttpHeadersWire { Entries = [.. response.Headers.Select(ToWireHeader)] }, Version = ToWireVersion(response.Version), Code = response.StatusCode, }, @@ -479,9 +473,7 @@ List headers { var version = FromWireVersion(responseWire.Version); - var headers = responseWire - .Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false)) - .ToList(); + var headers = responseWire.Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false)).ToList(); return (responseWire.Code, version, headers); } diff --git a/crates/bindings-csharp/Runtime/Internal/Bounds.cs b/crates/bindings-csharp/Runtime/Internal/Bounds.cs index 2bdd42c3aa1..f3e302aa8de 100644 --- a/crates/bindings-csharp/Runtime/Internal/Bounds.cs +++ b/crates/bindings-csharp/Runtime/Internal/Bounds.cs @@ -1,6 +1,3 @@ -using System.IO; -using SpacetimeDB.BSATN; - namespace SpacetimeDB { public readonly struct Bound(T min, T max) @@ -16,6 +13,8 @@ public readonly struct Bound(T min, T max) namespace SpacetimeDB.Internal { + using SpacetimeDB.BSATN; + enum BoundVariant : byte { Inclusive, diff --git a/crates/bindings-csharp/Runtime/Internal/FFI.cs b/crates/bindings-csharp/Runtime/Internal/FFI.cs index aa2ebf6f3be..c9f07994f1d 100644 --- a/crates/bindings-csharp/Runtime/Internal/FFI.cs +++ b/crates/bindings-csharp/Runtime/Internal/FFI.cs @@ -369,7 +369,7 @@ private ConsoleTimerId(uint id) )] internal static class ConsoleTimerIdMarshaller { - public static ConsoleTimerId ConvertToManaged(uint id) => new ConsoleTimerId(id); + public static ConsoleTimerId ConvertToManaged(uint id) => new(id); public static uint ConvertToUnmanaged(ConsoleTimerId id) => id.timer_id; } diff --git a/crates/bindings-csharp/Runtime/Internal/ITable.cs b/crates/bindings-csharp/Runtime/Internal/ITable.cs index 5294ce0e7fe..a322216a1d3 100644 --- a/crates/bindings-csharp/Runtime/Internal/ITable.cs +++ b/crates/bindings-csharp/Runtime/Internal/ITable.cs @@ -146,7 +146,9 @@ protected override void IterStart(out FFI.RowIter handle) => return out_; }); +#pragma warning disable IDE1006 // Used by static interface member call sites. internal static FFI.TableId tableId => tableId_.Value; +#pragma warning restore IDE1006 ulong Count { get; } diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index 4d15f0e93cc..ad5c8324381 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -31,11 +31,11 @@ partial class RawModuleDefV10 // Fix it up to a different mangling scheme if it causes problems. private static string GetFriendlyName(Type type) => type.IsGenericType - ? $"{type.Name.Remove(type.Name.IndexOf('`'))}_{string.Join("_", type.GetGenericArguments().Select(GetFriendlyName))}" + ? $"{type.Name[..type.Name.IndexOf('`')]}_{string.Join("_", type.GetGenericArguments().Select(GetFriendlyName))}" : type.Name; private static RawScopedTypeNameV10 MakeScopedTypeName(Type type) => - new(new List(), GetFriendlyName(type)); + new([], GetFriendlyName(type)); internal AlgebraicType.Ref RegisterType(Func makeType) { @@ -84,7 +84,7 @@ internal void RegisterTable(RawTableDefV10 table, RawScheduleDefV10? schedule) internal void RegisterView(RawViewDefV10 view) => viewDefs.Add(view); internal void RegisterViewPrimaryKey(string viewSourceName, IEnumerable columns) => - viewPrimaryKeyDefs.Add(new RawViewPrimaryKeyDefV10(viewSourceName, columns.ToList())); + viewPrimaryKeyDefs.Add(new RawViewPrimaryKeyDefV10(viewSourceName, [.. columns])); internal void RegisterRowLevelSecurity(RawRowLevelSecurityDefV9 rls) => rowLevelSecurityDefs.Add(rls); @@ -96,7 +96,7 @@ internal void RegisterTableDefaultValue(string table, ushort colId, byte[] value defaults = []; defaultValuesByTable.Add(table, defaults); } - defaults.Add(new RawColumnDefaultValueV10(colId, new List(value))); + defaults.Add(new RawColumnDefaultValueV10(colId, [.. value])); } internal void SetCaseConversionPolicy(SpacetimeDB.CaseConversionPolicy policy) => @@ -131,7 +131,7 @@ internal RawModuleDefV10 BuildModuleDefinition() TableAccess: table.TableAccess, DefaultValues: defaults is null ? [] - : new List(defaults), + : [.. defaults], IsEvent: table.IsEvent ) ); @@ -212,7 +212,7 @@ internal RawModuleDefV10 BuildModuleDefinition() { sections.Add( new RawModuleDefV10Section.ExplicitNames( - new ExplicitNames(new List(explicitNames)) + new ExplicitNames([.. explicitNames]) ) ); } @@ -244,7 +244,9 @@ private static void EnsureNativeAotTypeRoots() // These constructions are never executed at runtime — they exist solely // to make the IL scanner compute vtables for TaggedEnum subtypes. // The condition is always false but the scanner must assume it could be true. +#pragma warning disable IDE0078 // Keep this opaque to the NativeAOT IL scanner. if (Environment.TickCount < 0 && Environment.TickCount > 0) +#pragma warning restore IDE0078 { _ = new RawIndexAlgorithm.BTree(null!); _ = new RawConstraintDataV9.Unique(null!); diff --git a/crates/bindings-csharp/Runtime/JwtClaims.cs b/crates/bindings-csharp/Runtime/JwtClaims.cs index 6e656cca202..3ca3e11e029 100644 --- a/crates/bindings-csharp/Runtime/JwtClaims.cs +++ b/crates/bindings-csharp/Runtime/JwtClaims.cs @@ -72,11 +72,13 @@ private List ExtractAudience() return aud.ValueKind switch { - JsonValueKind.String => new List { aud.GetString()! }, - JsonValueKind.Array => aud.EnumerateArray() - .Where(e => e.ValueKind == JsonValueKind.String) - .Select(e => e.GetString()!) - .ToList(), + JsonValueKind.String => [aud.GetString()!], + JsonValueKind.Array => + [ + .. aud.EnumerateArray() + .Where(e => e.ValueKind == JsonValueKind.String) + .Select(e => e.GetString()!), + ], _ => throw new InvalidOperationException("Unexpected type for 'aud' claim in JWT"), }; } diff --git a/crates/bindings-csharp/Runtime/Router.cs b/crates/bindings-csharp/Runtime/Router.cs index e146bd5057d..7377d7b2561 100644 --- a/crates/bindings-csharp/Runtime/Router.cs +++ b/crates/bindings-csharp/Runtime/Router.cs @@ -91,7 +91,7 @@ private Router AddRoute(MethodOrAny method, string path, Handler handler) return new Router(merged); } - private List CloneRoutes() => new(routes); + private List CloneRoutes() => [.. routes]; private static void AddRoute( List routes, @@ -160,5 +160,5 @@ private static void AssertValidPath(string path) } private static bool CharacterIsAcceptableForRoutePath(char c) => - (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c is '-' or '_' or '~' or '/'; + c is (>= 'a' and <= 'z') or (>= '0' and <= '9') or '-' or '_' or '~' or '/'; } diff --git a/crates/bindings-csharp/Runtime/TransactionalContextState.cs b/crates/bindings-csharp/Runtime/TransactionalContextState.cs index ef0d72de410..bda7ff23f55 100644 --- a/crates/bindings-csharp/Runtime/TransactionalContextState.cs +++ b/crates/bindings-csharp/Runtime/TransactionalContextState.cs @@ -79,14 +79,14 @@ Func> body } } - private long StartMutTx() + private static long StartMutTx() { var status = FFI.procedure_start_mut_tx(out var micros); FFI.ErrnoHelpers.ThrowIfError(status); return micros; } - private void CommitMutTx() + private static void CommitMutTx() { var status = FFI.procedure_commit_mut_tx(); FFI.ErrnoHelpers.ThrowIfError(status); @@ -98,7 +98,7 @@ private void AbortMutTx() FFI.ErrnoHelpers.ThrowIfError(status); } - private bool CommitMutTxWithRetry(Func retryBody) + private static bool CommitMutTxWithRetry(Func retryBody) { try { From 21481b909a1ce0e47a6532ad82625ade3f9f6d90 Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Wed, 8 Jul 2026 13:51:50 -0500 Subject: [PATCH 2/5] Small cleanup from #5271 --- .github/workflows/ci.yml | 6 ++++-- crates/bindings-csharp/Runtime/TransactionalContextState.cs | 2 +- sdks/csharp/src/SpacetimeDBClient.cs | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d2513904f1..038334613ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1047,7 +1047,8 @@ jobs: run: dotnet restore --configfile ../../../NuGet.Config blackholio.csproj - name: Build Godot project - run: godot --headless --verbose --path demo/Blackholio/client-godot --build-solutions --quit + working-directory: demo/Blackholio/client-godot + run: godot --headless --verbose --build-solutions --quit - name: Start SpacetimeDB run: | @@ -1061,7 +1062,8 @@ jobs: bash ./publish.sh - name: Run Godot tests - run: godot --headless --path demo/Blackholio/client-godot --scene res://tests/GodotPlayModeTests.tscn + working-directory: demo/Blackholio/client-godot + run: godot --headless --scene res://tests/GodotPlayModeTests.tscn csharp-testsuite: needs: [merge_queue_noop, lints] diff --git a/crates/bindings-csharp/Runtime/TransactionalContextState.cs b/crates/bindings-csharp/Runtime/TransactionalContextState.cs index bda7ff23f55..9fbdaebaa70 100644 --- a/crates/bindings-csharp/Runtime/TransactionalContextState.cs +++ b/crates/bindings-csharp/Runtime/TransactionalContextState.cs @@ -92,7 +92,7 @@ private static void CommitMutTx() FFI.ErrnoHelpers.ThrowIfError(status); } - private void AbortMutTx() + private static void AbortMutTx() { var status = FFI.procedure_abort_mut_tx(); FFI.ErrnoHelpers.ThrowIfError(status); diff --git a/sdks/csharp/src/SpacetimeDBClient.cs b/sdks/csharp/src/SpacetimeDBClient.cs index ef202ed168b..34645cd60b0 100644 --- a/sdks/csharp/src/SpacetimeDBClient.cs +++ b/sdks/csharp/src/SpacetimeDBClient.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -133,6 +132,8 @@ public abstract class DbConnectionBase : IDbConne where DbConnection : DbConnectionBase, new() where Tables : RemoteTablesBase { + internal const bool IsTesting = false; + public static DbConnectionBuilder Builder() => new(); internal event Action? onConnect; @@ -281,7 +282,6 @@ internal struct ParsedMessage private readonly BlockingCollection _applyQueue = new(new ConcurrentQueue()); - internal static bool IsTesting; internal bool HasMessageToApply => _applyQueue.Count > 0; private readonly CancellationTokenSource _parseCancellationTokenSource = new(); From b4424162a13fb7e065cd575b0a66b430f676f82b Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Mon, 13 Jul 2026 11:11:22 -0500 Subject: [PATCH 3/5] Clean CommitMutTxWithRetry method --- .../Runtime/TransactionalContextState.cs | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/crates/bindings-csharp/Runtime/TransactionalContextState.cs b/crates/bindings-csharp/Runtime/TransactionalContextState.cs index 9fbdaebaa70..b6734b83875 100644 --- a/crates/bindings-csharp/Runtime/TransactionalContextState.cs +++ b/crates/bindings-csharp/Runtime/TransactionalContextState.cs @@ -98,29 +98,6 @@ private static void AbortMutTx() FFI.ErrnoHelpers.ThrowIfError(status); } - private static bool CommitMutTxWithRetry(Func retryBody) - { - try - { - CommitMutTx(); - return true; - } - catch (TransactionNotAnonymousException) - { - return false; - } - catch (StdbException) - { - Log.Warn("Committing anonymous transaction failed; retrying once."); - if (retryBody()) - { - CommitMutTx(); - return true; - } - return false; - } - } - private Result RunWithRetry( Func> body ) @@ -132,18 +109,28 @@ Func> body return result; } - bool Retry() + try { - result = RunOnce(body); - return result is Result.OkR; + CommitMutTx(); + return result; } - - if (!CommitMutTxWithRetry(Retry)) + catch (TransactionNotAnonymousException) { return result; } + catch (StdbException) + { + Log.Warn("Committing anonymous transaction failed; retrying once."); - return result; + var retryResult = RunOnce(body); + if (retryResult is Result.ErrR) + { + return retryResult; + } + + CommitMutTx(); + return retryResult; + } } private Result RunOnce( From 1cf81473ac4cd28addac2f58c4f9357b72ba4b6d Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Fri, 17 Jul 2026 09:13:38 -0500 Subject: [PATCH 4/5] Make IsTesting a property again --- sdks/csharp/src/SpacetimeDBClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/csharp/src/SpacetimeDBClient.cs b/sdks/csharp/src/SpacetimeDBClient.cs index 34645cd60b0..6df6c6155e8 100644 --- a/sdks/csharp/src/SpacetimeDBClient.cs +++ b/sdks/csharp/src/SpacetimeDBClient.cs @@ -132,7 +132,7 @@ public abstract class DbConnectionBase : IDbConne where DbConnection : DbConnectionBase, new() where Tables : RemoteTablesBase { - internal const bool IsTesting = false; + internal static bool IsTesting { get; set; } = false; public static DbConnectionBuilder Builder() => new(); From a491ccf8ae853f85256d10c9b1fd93b232432b43 Mon Sep 17 00:00:00 2001 From: Lisandro Crespo Date: Fri, 17 Jul 2026 14:42:16 -0500 Subject: [PATCH 5/5] Fix lint --- .../BSATN.Runtime/BSATN/Runtime.cs | 5 +++- crates/bindings-csharp/Codegen/Module.cs | 3 ++- crates/bindings-csharp/Runtime/Http.cs | 27 ++++++++++++++----- .../Runtime/Internal/Module.cs | 8 ++---- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs index badbb277c92..e7519e22765 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs @@ -180,7 +180,10 @@ public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) => (_) => new AlgebraicType.Sum( #pragma warning disable CA2263 // netstandard2.1 lacks the generic Enum overloads. - [.. Enum.GetNames(typeof(T)).Select(name => new AggregateElement(name, AlgebraicType.Unit))] + [ + .. Enum.GetNames(typeof(T)) + .Select(name => new AggregateElement(name, AlgebraicType.Unit)), + ] #pragma warning restore CA2263 ) ); diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 3e76b20c6ef..34040525b94 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1059,7 +1059,8 @@ public IEnumerable GenerateDefaultValues() .Select(m => m.Attrs.FirstOrDefault(a => a.Mask == ColumnAttrs.Default)) ); - var withDefaultValues = fieldsWithDefaultValues as ColumnDeclaration[] ?? [.. fieldsWithDefaultValues]; + var withDefaultValues = + fieldsWithDefaultValues as ColumnDeclaration[] ?? [.. fieldsWithDefaultValues]; foreach (var fieldsWithDefaultValue in withDefaultValues) { if ( diff --git a/crates/bindings-csharp/Runtime/Http.cs b/crates/bindings-csharp/Runtime/Http.cs index 8dfbe262e76..2567f4a11e5 100644 --- a/crates/bindings-csharp/Runtime/Http.cs +++ b/crates/bindings-csharp/Runtime/Http.cs @@ -1,8 +1,8 @@ namespace SpacetimeDB; using System; -using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text; @@ -272,7 +272,11 @@ public Result Get(string uri, TimeSpan? timeout = null) /// } /// /// - [SuppressMessage("Performance", "CA1822", Justification = "Public instance API exposed through ProcedureContext.Http.")] + [SuppressMessage( + "Performance", + "CA1822", + Justification = "Public instance API exposed through ProcedureContext.Http." + )] public Result Send(HttpRequest request) { // The host syscall expects BSATN-encoded spacetimedb_lib::http::Request bytes. @@ -308,7 +312,10 @@ public Result Send(HttpRequest request) var requestWire = new HttpRequestWire { Method = ToWireMethod(request.Method), - Headers = new HttpHeadersWire { Entries = [.. request.Headers.Select(ToWireHeader)] }, + Headers = new HttpHeadersWire + { + Entries = [.. request.Headers.Select(ToWireHeader)], + }, Timeout = timeout is null ? null : new HttpTimeoutWire { Timeout = (TimeDuration)timeout.Value }, @@ -422,7 +429,10 @@ internal static HttpRequest FromWire(HttpRequestWire requestWire, byte[] body) = { Uri = requestWire.Uri, Method = FromWireMethod(requestWire.Method), - Headers = [.. requestWire.Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false))], + Headers = + [ + .. requestWire.Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false)), + ], Body = new HttpBody(body), Version = FromWireVersion(requestWire.Version), }; @@ -431,7 +441,10 @@ internal static (HttpResponseWire Response, byte[] Body) ToWire(HttpResponse res ( new HttpResponseWire { - Headers = new HttpHeadersWire { Entries = [.. response.Headers.Select(ToWireHeader)] }, + Headers = new HttpHeadersWire + { + Entries = [.. response.Headers.Select(ToWireHeader)], + }, Version = ToWireVersion(response.Version), Code = response.StatusCode, }, @@ -473,7 +486,9 @@ List headers { var version = FromWireVersion(responseWire.Version); - var headers = responseWire.Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false)).ToList(); + var headers = responseWire + .Headers.Entries.Select(h => new HttpHeader(h.Name, h.Value, false)) + .ToList(); return (responseWire.Code, version, headers); } diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index ad5c8324381..eebd573cfc5 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -129,9 +129,7 @@ internal RawModuleDefV10 BuildModuleDefinition() Sequences: table.Sequences, TableType: table.TableType, TableAccess: table.TableAccess, - DefaultValues: defaults is null - ? [] - : [.. defaults], + DefaultValues: defaults is null ? [] : [.. defaults], IsEvent: table.IsEvent ) ); @@ -211,9 +209,7 @@ internal RawModuleDefV10 BuildModuleDefinition() if (explicitNames.Count > 0) { sections.Add( - new RawModuleDefV10Section.ExplicitNames( - new ExplicitNames([.. explicitNames]) - ) + new RawModuleDefV10Section.ExplicitNames(new ExplicitNames([.. explicitNames])) ); } if (rowLevelSecurityDefs.Count > 0)