Infer System.Text.Json polymorphism from closed type hierarchies#130808
Conversation
…erence Implements feature dotnet#129041: a global InferClosedTypePolymorphism flag on JsonSerializerOptions and JsonSourceGenerationOptionsAttribute that infers polymorphic \ discriminators from compiler-emitted [IsClosedType] metadata when no explicit [JsonDerivedType] set is present. - Reflection: DefaultJsonTypeInfoResolver infers one derived type per closed hierarchy member; throws for inaccessible/un-unifiable derived types. - Source-gen: forward-compat parser plumbing mirrors reflection parity (throwing factory for inaccessible/open-generic, kept entry for unassignable). - Wire flag into options copy-ctor and cache equality comparer. - New diagnostics SYSLIB1240-1242 for derived-type validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d derived types Source-gen surfaces invalid polymorphic configurations through build-time SYSLIB diagnostics (the established design), so the prior approach of baking a non-localized `throw new InvalidOperationException(...)` into the generated metadata was inconsistent and not localizable. Remove the UnresolvedDerivedTypeError mechanism (the dispatch hook, the GenerateForTypeWithUnresolvedPolymorphism emitter method, the PolymorphismOptionsSpec field, and the two non-localized message constants) and restore the build-diagnostic + drop behavior: - Explicit open-generic [JsonDerivedType] that cannot be unified: report SYSLIB1229 and drop the entry (matches the pre-existing base behavior). - Inferred closed-type derived types that are open-generic-unresolvable or less accessible than the base: report SYSLIB1229 / SYSLIB1241 and drop the entry. Track explicit [JsonDerivedType] presence with a dedicated flag so closed-type inference is still suppressed when an explicit registration exists, even if every explicit entry failed to resolve. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The closed-type inference tests forced an explicit DefaultJsonTypeInfoResolver, which fails under the NativeAOT/source-gen test legs where reflection is disabled. Closed-type polymorphism inference is reflection-only, so rely on the ambient default reflection resolver instead and guard each test on JsonSerializer.IsReflectionEnabledByDefault so they no-op when reflection is unavailable. The forced resolver's stated rationale never applied: this file is only compiled into the reflection test project, not the source-gen one. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The header claimed the closed-type fixtures serve both the reflection-based and source-generated serializers, but the fixture assembly is referenced only by System.Text.Json.Tests and the source-gen path does not support closed-type inference. Correct the comment to name the reflection-based tests as the sole consumer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Exercise both derived types of the basic hierarchy via [Theory], and add tests for collection-element inference, nested closed-type properties alongside regular properties (with payload round-trip), and the unknown-discriminator deserialization failure path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align reflection and source-generation validation, generic specialization, diagnostics, metadata inference, and test coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8967afcb-a170-497d-930a-a69e5ccf8773
|
Azure Pipelines: Successfully started running 4 pipeline(s). 11 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
There was a problem hiding this comment.
Pull request overview
This PR adds opt-in “closed type hierarchy” polymorphism inference to System.Text.Json for both reflection-based metadata generation and source generation. When enabled, the resolver/source generator reads compiler-emitted closed-hierarchy metadata (via System.Runtime.CompilerServices.IsClosedTypeAttribute) and synthesizes JsonDerivedType registrations with deterministic string discriminators.
Changes:
- Introduces
JsonSerializerOptions.InferClosedTypePolymorphismandJsonSourceGenerationOptionsAttribute.InferClosedTypePolymorphism(public API) and wires the setting through reflection + source generator pipelines. - Implements closed-hierarchy derived-type discovery (runtime via
CustomAttributeData; source-gen via Roslyn light-up/polyfills), plus accessibility validation and new SYSLIB diagnostics for unsupported inferred registrations. - Adds broad tests covering runtime behavior, source-gen behavior, and diagnostics; updates resources/diagnostic documentation.
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj | Includes new shared closed-type inference test file and IsClosedTypeAttribute source for tests. |
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CacheTests.cs | Ensures options caching equality/hash includes InferClosedTypePolymorphism. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs | Adds diagnostic coverage for inferred-closed-type scenarios and extends open-generic derived-type diagnostics assertions. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets | Adds shared closed-type inference tests and IsClosedTypeAttribute to source-gen test builds. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs | Registers additional types used by closed-type inference/open-generic polymorphism scenarios for source-gen contexts. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.ClosedTypeInference.cs | New source-gen runtime-behavior tests for closed-type inference + runtime guard behavior. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/TestClasses.cs | Adds referenced closed-type hierarchy types and local compiler-attribute polyfills for test library assembly. |
| src/libraries/System.Text.Json/tests/Common/PolymorphicTests.CustomTypeHierarchies.cs | Adjusts tests and warning suppressions to align with new diagnostics and source-gen vs reflection validation timing. |
| src/libraries/System.Text.Json/tests/Common/PolymorphicTests.ClosedTypeInference.cs | New shared (reflection + source-gen) functional coverage for closed-type inference across many hierarchy shapes. |
| src/libraries/System.Text.Json/tests/Common/JsonCreationHandlingTests.Object.cs | Narrows warning suppression for intentionally invalid derived-type configurations. |
| src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs | Adds a throw helper for inaccessible inferred derived types. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs | Formatting-only adjustment near polymorphism population callsite. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs | Implements runtime closed-hierarchy inference, discriminator synthesis, and accessibility validation. |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs | Adds new public option InferClosedTypePolymorphism (and copies it in the copy ctor). |
| src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Caching.cs | Updates options equality/hash to include _inferClosedTypePolymorphism. |
| src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs | Adds Type.IsAtLeastAsVisibleAs ported accessibility comparison used to validate inferred registrations. |
| src/libraries/System.Text.Json/src/Resources/Strings.resx | Adds runtime error string for inaccessible inferred derived types. |
| src/libraries/System.Text.Json/ref/System.Text.Json.cs | Adds the new public API surface in the ref assembly (InferClosedTypePolymorphism properties). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf | Adds new source-gen diagnostic resource entries (untranslated placeholders). |
| src/libraries/System.Text.Json/gen/Resources/Strings.resx | Adds new source-gen diagnostic strings for derived-type support/accessibility/collision. |
| src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs | Tracks closed-type-without-inferred-metadata marker for runtime guard emission. |
| src/libraries/System.Text.Json/gen/Model/SourceGenerationOptionsSpec.cs | Adds InferClosedTypePolymorphism to parsed source-gen options model. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs | Parses new option, performs closed-type inference, and centralizes derived-type validation/diagnostics. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.ExceptionMessages.cs | Adds runtime exception text for “runtime-only inference requested but metadata not generated”. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs | Emits runtime guard for closed types lacking inferred metadata; writes the new option into generated default options. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs | Introduces SYSLIB1240–1242 source-gen diagnostics for polymorphism validation. |
| src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs | Adds Roslyn light-up/polyfills for closed-type detection/derived-type enumeration and accessibility comparison. |
| src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs | Adds new public source-gen option property InferClosedTypePolymorphism. |
| docs/project/list-of-diagnostics.md | Documents new SYSLIB1240–1249 reservation range and new diagnostic IDs. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8967afcb-a170-497d-930a-a69e5ccf8773
tannergooding
left a comment
There was a problem hiding this comment.
LGTM. Only real feedback is that I didn't see any test validating reflection vs source-gen are equivalent, but I don't think we have existing tests for that either
The test suite targets an abstraction implemented by both reflection and source gen, so this should ensure the two implementations are mostly observationally equivalent. |
|
/ba-g test failure is unrelated |
Summary
JsonSerializerOptionsand source-generation optionsFixes #129041
Testing
src/libraries/System.Text.Json/System.Text.Json.slnxwith zero warnings and errorsNote
This pull request description was generated by GitHub Copilot.