Skip to content

Emit safe C# for enum constants in CodeFormatter.Write#197

Merged
jeremydmiller merged 1 commit intomainfrom
safe-enum-codeformatter
Apr 28, 2026
Merged

Emit safe C# for enum constants in CodeFormatter.Write#197
jeremydmiller merged 1 commit intomainfrom
safe-enum-codeformatter

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Problem

CodeFormatter.Write renders enum values with the single line:

return value.GetType().FullNameInCode() + "." + value;

That implicitly calls Enum.ToString(). For values that aren't a single defined member — particularly bit-OR'd combinations on non-[Flags] enums such as Npgsql.NpgsqlDbTypeToString returns the underlying integer literal as a string, so we emit uncompilable source like:

parameter0.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.-2147483629;

Roslyn rejects the generated assembly with CS1001 Identifier expected. This is the root cause of the Marten codegen bug tracked at JasperFx/marten#3702 (a [DuplicateField] on List<T> with DbType = NpgsqlDbType.Array | NpgsqlDbType.Text).

Fix

Replace the single-line implementation with WriteEnum, which handles three cases explicitly:

Case Output
Single defined member (Numbers.one) Namespace.Numbers.one (unchanged)
[Flags] combo (Toppings.Cheese | Toppings.Pepperoni) Type.Cheese | Type.Pepperoni
Undefined value (incl. bit-OR'd non-Flags enums) ((Type)(rawIntegerLiteral))

The [Flags] branch defends against the case where ToString still returns a numeric literal (e.g. an Or'd combination whose bits don't all map to defined members) by checking IsNumericLiteral and falling through to the cast form.

Tests

Three new tests in CodeFormatterTests, including a DirtyFlagless enum that mimics the exact NpgsqlDbType shape (non-[Flags] enum whose callers OR member values together).

Passed!  - Failed: 0, Passed: 9, Total: 9 (CodegenTests, all 3 TFMs)

Downstream verification

With this JasperFx built locally and dropped into the NuGet cache, the Marten reproduction test from #3702 (Bug_PR_3702_list_index_compile_error.can_create_array_duplicate_column_on_a_list_field) passes with no Marten-side changes, superseding the call-site patch that PR proposed. Marten will be bumped to the next JasperFx release in a follow-up PR.

🤖 Generated with Claude Code

The previous implementation rendered any enum value as
$"{Type}.{value}", which silently calls Enum.ToString().
For values that don't correspond to a single named member —
most notably bit-OR'd combinations on non-[Flags] enums like
Npgsql.NpgsqlDbType — ToString returns the underlying integer
literal as a string, so we'd emit uncompilable source such as
`NpgsqlTypes.NpgsqlDbType.-2147483629`. Roslyn then rejects the
generated assembly with CS1001.

Replace the single-line implementation with WriteEnum, which
handles three cases:

  1. Defined single member  → Type.Name (unchanged)
  2. [Flags] combination     → Type.A | Type.B
  3. Undefined value         → ((Type)(rawIntegerLiteral))

Three new CodeFormatterTests cover each branch, including the
exact non-[Flags] OR'd-bit shape that NpgsqlDbType uses.

Fixes the Marten codegen bug tracked in JasperFx/marten#3702.
@jeremydmiller jeremydmiller merged commit 0972005 into main Apr 28, 2026
1 check passed
@jeremydmiller jeremydmiller deleted the safe-enum-codeformatter branch April 28, 2026 23:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant