Skip to content

fix: report a diagnostic for metadata shapes System.Text.Json cannot construct [patch] - #6

Merged
matt-edmondson merged 2 commits into
mainfrom
claude/nice-davinci-gvb62g
Sep 14, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
claude/nice-davinci-gvb62g

Conversation

@matt-edmondson

@matt-edmondson matt-edmondson commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Fixes #5

The problem

MetadataFile.Deserialize<T> wrapped JsonSerializer.Deserialize<T> in a try that caught only System.Text.Json.JsonException. But malformed input is not the only way deserialization fails:

  • A shape JsonSerializer cannot construct — an interface or abstract type, or a type with several parameterized constructors and no [JsonConstructor] — throws NotSupportedException.
  • Some converter-configuration failures, such as two properties claiming one JSON name, throw InvalidOperationException.

Neither derives from JsonException, so both escaped Deserialize<T>, escaped GeneratorBase<T>.Generate, and escaped the RegisterSourceOutput callback uncaught. The Roslyn driver then reported its own generic CS8785, which names neither the offending file nor the reason — and, because the throw unwinds the whole callback, every other file declared by the same generator went unread.

This contradicted the toolkit's own stated guarantee (CLAUDE.md): a missing or malformed file always reports.

The fix

Deserialize<T> now catches NotSupportedException and InvalidOperationException alongside JsonException, reporting MetadataParseFailed with the exception message exactly as the existing branch did.

Only the JsonSerializer.Deserialize call sits inside the guarded region — the deserialized-to-null report was moved out, so a failure while reporting a diagnostic is not itself swallowed by the new catches.

Tests

Four tests added to GeneratorBaseTests, covering both acceptance criteria on the issue plus each new catch:

Test Covers
AShapeTheSerializerCannotConstructIsReportedRatherThanCrashingTheGenerator Interface-typed model → TST002 error, not a crash (NotSupportedException)
AmbiguousConstructorsAreReportedRatherThanCrashingTheGenerator Two parameterized constructors, no [JsonConstructor] (NotSupportedException)
AConverterConfigurationFailureIsReportedRatherThanCrashingTheGenerator Two properties claiming one JSON name (InvalidOperationException)
OneFileFailingOnAnUnsupportedShapeStillLeavesTheOthersProcessed The second declared file is still read and still generates

Each asserts result.Exception is null, which is what distinguishes a reported diagnostic from a generator that threw.

Supporting fixtures in TestGenerators.cs: IThingsMetadata, AmbiguousMetadata, ConflictingNamesMetadata, their three generators, and ResilientPairGenerator — the last emits from whichever file parsed, so the test can tell one file failing apart from the whole invocation being abandoned before the second file was reached.

Verification

Each new catch was verified by removing it alone and confirming that the matching test — and only that test — then fails on the expected exception type:

  • Original MetadataFile.cs restored, new tests in place: 3 failed, 39 passed, each failure an uncaught NotSupportedException reaching the driver via GeneratorBase.Initialize.
  • InvalidOperationException catch removed alone: 1 failed, 42 passedThe JSON property name ... collides with another property.
  • Full fix in place: 43/43 passed.
  • Line coverage of both shipping assemblies: 100%.
  • dotnet build -c Release — 0 warnings, 0 errors.
  • Build with the SonarCloud analyzers injected (CustomAfterMicrosoftCommonProps=.sonarlint/sonar-local.props, per CLAUDE.md) — 0 warnings, 0 errors.

Docs

CLAUDE.md's "A missing or malformed file always reports" decision note now says what "malformed" covers, and the testing section lists the new fixtures. CHANGELOG.md/VERSION.md are pipeline-written and untouched; both commits carry [patch].

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ra5um9FcK7GaeADvaxRABt

…construct [patch]

Deserialize<T> caught only JsonException. JsonSerializer reports a shape it
cannot construct — an interface or abstract type, or a type with several
parameterized constructors and no [JsonConstructor] — as NotSupportedException,
and some converter-configuration failures as InvalidOperationException. Neither
derives from JsonException, so both escaped to the Roslyn driver, which reported
its own generic CS8785 naming neither the file nor the reason, and abandoned
every other declared file in the same RegisterSourceOutput invocation.

Catch all three and report MetadataParseFailed with the exception message, the
way malformed JSON already was. Only the deserialize call is guarded, so a
failure to report a diagnostic is not itself swallowed.

Fixes #5

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra5um9FcK7GaeADvaxRABt
SonarCloud's quality gate failed the PR at 75% coverage on new code: of the
three catches added to Deserialize<T>, only JsonException and
NotSupportedException had a test driving them.

Add ConflictingNamesMetadata — two properties claiming one JSON name — which
System.Text.Json rejects while building the contract, with
InvalidOperationException rather than JsonException or NotSupportedException.
Confirmed by removing that catch alone: this test, and only this test, then
fails on the collision. Line coverage of both shipping assemblies is now 100%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ra5um9FcK7GaeADvaxRABt
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 29f74e2 into main Sep 14, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the claude/nice-davinci-gvb62g branch September 14, 2026 14:37
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.

MetadataFile.Deserialize crashes the generator on unsupported types instead of reporting the documented diagnostic

2 participants