From 5d763b2b1e0233195254330d570f492df2db6044 Mon Sep 17 00:00:00 2001 From: Joe Harjung Date: Thu, 14 Apr 2022 20:06:37 -0400 Subject: [PATCH 1/5] Improving STJ source generator support for record types Improving the analyzer to not flag a warning about init-only properties that are actually constructor parameters. In particular, record types with positional parameters desugar to classes with both a constructor and init-only properties (eg, to support "with" expressions). Since these properties are deserialized via the constructor and not the property setter, they support the fast path and round trip just fine, so the diagonstic analyzer should not raise the SYSLIB1037 warning. Issue 58770 --- .../gen/JsonSourceGenerator.Parser.cs | 5 +++- .../CompilationHelper.cs | 30 +++++++++++++++++++ .../JsonSourceGeneratorDiagnosticsTests.cs | 13 ++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 9590a3b90f07d2..9c5c53e149e8ff 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -1028,7 +1028,7 @@ void CacheMemberHelper(Location memberLocation) _implicitlyRegisteredTypes.Add(dataExtensionPropGenSpec); } - if (!hasInitOnlyProperties && spec.CanUseSetter && spec.IsInitOnlySetter) + if (!hasInitOnlyProperties && spec.CanUseSetter && spec.IsInitOnlySetter && !PropertyIsConstructorParameter(spec, paramGenSpecArray)) { _sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(InitOnlyPropertyDeserializationNotSupported, memberLocation, new string[] { type.Name })); hasInitOnlyProperties = true; @@ -1119,6 +1119,9 @@ private void CacheMember( } } + private static bool PropertyIsConstructorParameter(PropertyGenerationSpec propSpec, ParameterGenerationSpec[]? paramGenSpecArray) + => paramGenSpecArray != null && paramGenSpecArray.Any(paramSpec => propSpec.ClrName == paramSpec.ParameterInfo.Name); + private static bool PropertyIsOverridenAndIgnored( string currentMemberName, Type currentMemberType, diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs index 485a79cd333cd6..d9fd1b76a32aac 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs @@ -275,6 +275,36 @@ public class Location return CreateCompilation(source); } + public static Compilation CreateCompilationWithRecordPositionalParameters() + { + string source = @" + using System; + using System.Text.Json.Serialization; + + namespace HelloWorld + { + public record Location + ( + int Id, + string Address1, + string Address2, + string City, + string State, + string PostalCode, + string Name, + string PhoneNumber, + string Country + ) + + [JsonSerializable(typeof(Location))] + public partial class MyJsonContext : JsonSerializerContext + { + } + }"; + + return CreateCompilation(source); + } + public static Compilation CreateCompilationWithInitOnlyProperties() { string source = @" diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs index d3378b84f2f83a..644e8aa0e78630 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs @@ -216,6 +216,19 @@ public static void Main() CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Error, generatorDiags, Array.Empty<(Location, string)>()); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/58770", TestPlatforms.Browser)] + public void DoNotWarnOnRecordsWithPositionalParameters() + { + Compilation compilation = CompilationHelper.CreateCompilationWithRecordPositionalParameters(); + JsonSourceGenerator generator = new JsonSourceGenerator(); + CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator); + + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Info, generatorDiags, Array.Empty<(Location, string)>()); + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Warning, generatorDiags, Array.Empty<(Location, string)>()); + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Error, generatorDiags, Array.Empty<(Location, string)>()); + } + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/58226", TestPlatforms.Browser)] public void WarnOnClassesWithInitOnlyProperties() From 09369ee92a60ce4dc551d9c3fbb85d40aebce62b Mon Sep 17 00:00:00 2001 From: Joe Harjung Date: Thu, 14 Apr 2022 20:12:21 -0400 Subject: [PATCH 2/5] Improving STJ source generator support for record types Edit to fix missing semi colon in unit test's CreateCompiliation string Issue 58770 --- .../CompilationHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs index d9fd1b76a32aac..b6078d48d14fc8 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs @@ -294,7 +294,7 @@ public record Location string Name, string PhoneNumber, string Country - ) + ); [JsonSerializable(typeof(Location))] public partial class MyJsonContext : JsonSerializerContext From 7b31309212482ddf599bff7713977bea71316b3a Mon Sep 17 00:00:00 2001 From: Joe Harjung Date: Sat, 16 Apr 2022 18:15:10 -0400 Subject: [PATCH 3/5] Case insensitive matching of props with ctor params --- .../System.Text.Json/gen/JsonSourceGenerator.Parser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 9c5c53e149e8ff..0f1043f8c8cce8 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -1120,7 +1120,7 @@ private void CacheMember( } private static bool PropertyIsConstructorParameter(PropertyGenerationSpec propSpec, ParameterGenerationSpec[]? paramGenSpecArray) - => paramGenSpecArray != null && paramGenSpecArray.Any(paramSpec => propSpec.ClrName == paramSpec.ParameterInfo.Name); + => paramGenSpecArray != null && paramGenSpecArray.Any(paramSpec => propSpec.ClrName.Equals(paramSpec.ParameterInfo.Name, StringComparison.OrdinalIgnoreCase)); private static bool PropertyIsOverridenAndIgnored( string currentMemberName, From a6e20ef2487bd8f4f224769800de56f491fe1de8 Mon Sep 17 00:00:00 2001 From: Joe Harjung Date: Sat, 16 Apr 2022 18:15:33 -0400 Subject: [PATCH 4/5] Additional unit tests --- .../CompilationHelper.cs | 109 +++++++++++++----- .../JsonSourceGeneratorDiagnosticsTests.cs | 49 ++++++-- 2 files changed, 123 insertions(+), 35 deletions(-) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs index b6078d48d14fc8..332b8123bcb43d 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs @@ -274,8 +274,8 @@ public class Location return CreateCompilation(source); } - - public static Compilation CreateCompilationWithRecordPositionalParameters() + + public static Compilation CreateCompilationWithInitOnlyProperties() { string source = @" using System; @@ -283,18 +283,18 @@ public static Compilation CreateCompilationWithRecordPositionalParameters() namespace HelloWorld { - public record Location - ( - int Id, - string Address1, - string Address2, - string City, - string State, - string PostalCode, - string Name, - string PhoneNumber, - string Country - ); + public class Location + { + public int Id { get; init; } + public string Address1 { get; init; } + public string Address2 { get; init; } + public string City { get; init; } + public string State { get; init; } + public string PostalCode { get; init; } + public string Name { get; init; } + public string PhoneNumber { get; init; } + public string Country { get; init; } + } [JsonSerializable(typeof(Location))] public partial class MyJsonContext : JsonSerializerContext @@ -305,28 +305,25 @@ public partial class MyJsonContext : JsonSerializerContext return CreateCompilation(source); } - public static Compilation CreateCompilationWithInitOnlyProperties() + public static Compilation CreateCompilationWithConstructorInitOnlyProperties() { string source = @" using System; using System.Text.Json.Serialization; namespace HelloWorld - { - public class Location + { + public class MyClass { - public int Id { get; init; } - public string Address1 { get; init; } - public string Address2 { get; init; } - public string City { get; init; } - public string State { get; init; } - public string PostalCode { get; init; } - public string Name { get; init; } - public string PhoneNumber { get; init; } - public string Country { get; init; } + public MyClass(int value) + { + Value = value; + } + + public int Value { get; init; } } - [JsonSerializable(typeof(Location))] + [JsonSerializable(typeof(MyClass))] public partial class MyJsonContext : JsonSerializerContext { } @@ -334,7 +331,65 @@ public partial class MyJsonContext : JsonSerializerContext return CreateCompilation(source); } + + public static Compilation CreateCompilationWithMixedInitOnlyProperties() + { + string source = @" + using System; + using System.Text.Json.Serialization; + + namespace HelloWorld + { + public class MyClass + { + public MyClass(int value) + { + Value = value; + } + + public int Value { get; init; } + public string Orphaned { get; init; } + } + + [JsonSerializable(typeof(MyClass))] + public partial class MyJsonContext : JsonSerializerContext + { + } + }"; + return CreateCompilation(source); + } + + public static Compilation CreateCompilationWithRecordPositionalParameters() + { + string source = @" + using System; + using System.Text.Json.Serialization; + + namespace HelloWorld + { + public record Location + ( + int Id, + string Address1, + string Address2, + string City, + string State, + string PostalCode, + string Name, + string PhoneNumber, + string Country + ); + + [JsonSerializable(typeof(Location))] + public partial class MyJsonContext : JsonSerializerContext + { + } + }"; + + return CreateCompilation(source); + } + public static Compilation CreateCompilationWithInaccessibleJsonIncludeProperties() { string source = @" diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs index 644e8aa0e78630..77f76fa92c4d80 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs @@ -216,11 +216,31 @@ public static void Main() CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Error, generatorDiags, Array.Empty<(Location, string)>()); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/58226", TestPlatforms.Browser)] + public void WarnOnClassesWithInitOnlyProperties() + { + Compilation compilation = CompilationHelper.CreateCompilationWithInitOnlyProperties(); + JsonSourceGenerator generator = new JsonSourceGenerator(); + CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator); + + Location location = compilation.GetSymbolsWithName("Id").First().Locations[0]; + + (Location, string)[] expectedWarningDiagnostics = new (Location, string)[] + { + (location, "The type 'Location' defines init-only properties, deserialization of which is currently not supported in source generation mode.") + }; + + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Info, generatorDiags, Array.Empty<(Location, string)>()); + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Warning, generatorDiags, expectedWarningDiagnostics); + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Error, generatorDiags, Array.Empty<(Location, string)>()); + } + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/58770", TestPlatforms.Browser)] - public void DoNotWarnOnRecordsWithPositionalParameters() + public void DoNotWarnOnClassesWithConstructorInitOnlyProperties() { - Compilation compilation = CompilationHelper.CreateCompilationWithRecordPositionalParameters(); + Compilation compilation = CompilationHelper.CreateCompilationWithConstructorInitOnlyProperties(); JsonSourceGenerator generator = new JsonSourceGenerator(); CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator); @@ -228,20 +248,20 @@ public void DoNotWarnOnRecordsWithPositionalParameters() CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Warning, generatorDiags, Array.Empty<(Location, string)>()); CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Error, generatorDiags, Array.Empty<(Location, string)>()); } - + [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/58226", TestPlatforms.Browser)] - public void WarnOnClassesWithInitOnlyProperties() + [ActiveIssue("https://github.com/dotnet/runtime/issues/58770", TestPlatforms.Browser)] + public void WarnOnClassesWithMixedInitOnlyProperties() { - Compilation compilation = CompilationHelper.CreateCompilationWithInitOnlyProperties(); + Compilation compilation = CompilationHelper.CreateCompilationWithMixedInitOnlyProperties(); JsonSourceGenerator generator = new JsonSourceGenerator(); CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator); - Location location = compilation.GetSymbolsWithName("Id").First().Locations[0]; + Location location = compilation.GetSymbolsWithName("Orphaned").First().Locations[0]; (Location, string)[] expectedWarningDiagnostics = new (Location, string)[] { - (location, "The type 'Location' defines init-only properties, deserialization of which is currently not supported in source generation mode.") + (location, "The type 'MyClass' defines init-only properties, deserialization of which is currently not supported in source generation mode.") }; CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Info, generatorDiags, Array.Empty<(Location, string)>()); @@ -249,6 +269,19 @@ public void WarnOnClassesWithInitOnlyProperties() CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Error, generatorDiags, Array.Empty<(Location, string)>()); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/58770", TestPlatforms.Browser)] + public void DoNotWarnOnRecordsWithInitOnlyPositionalParameters() + { + Compilation compilation = CompilationHelper.CreateCompilationWithRecordPositionalParameters(); + JsonSourceGenerator generator = new JsonSourceGenerator(); + CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator); + + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Info, generatorDiags, Array.Empty<(Location, string)>()); + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Warning, generatorDiags, Array.Empty<(Location, string)>()); + CompilationHelper.CheckDiagnosticMessages(DiagnosticSeverity.Error, generatorDiags, Array.Empty<(Location, string)>()); + } + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/58226", TestPlatforms.Browser)] public void WarnOnClassesWithInaccessibleJsonIncludeProperties() From 409dd7eed548c3155cb6f9c33a2211c8ba1055cf Mon Sep 17 00:00:00 2001 From: Joe Harjung Date: Mon, 18 Apr 2022 11:21:01 -0400 Subject: [PATCH 5/5] Test for source gen positional records roundtrip --- .../ContextClasses.cs | 1 + .../MetadataAndSerializationContextTests.cs | 2 ++ .../MetadataContextTests.cs | 4 ++++ .../MixedModeContextTests.cs | 2 ++ .../RealWorldContextTests.cs | 12 ++++++++++++ .../SerializationContextTests.cs | 15 +++++++++++++++ .../TestClasses.cs | 2 ++ 7 files changed, 38 insertions(+) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs index a21ab179cc8eda..ef78645435fc27 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs @@ -28,6 +28,7 @@ public interface ITestContext public JsonTypeInfo MyTypeWithPropertyOrdering { get; } public JsonTypeInfo MyIntermediateType { get; } public JsonTypeInfo HighLowTempsImmutable { get; } + public JsonTypeInfo HighLowTempsRecord { get; } public JsonTypeInfo MyNestedClass { get; } public JsonTypeInfo MyNestedNestedClass { get; } public JsonTypeInfo ObjectArray { get; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs index 092d6110170739..8544ccbb934da2 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs @@ -22,6 +22,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyTypeWithPropertyOrdering))] [JsonSerializable(typeof(MyIntermediateType))] [JsonSerializable(typeof(HighLowTempsImmutable))] + [JsonSerializable(typeof(HighLowTempsRecord))] [JsonSerializable(typeof(byte[]))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] @@ -70,6 +71,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(MetadataAndSerializationContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); Assert.NotNull(MetadataAndSerializationContext.Default.MyIntermediateType.SerializeHandler); Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.NotNull(MetadataAndSerializationContext.Default.HighLowTempsRecord.SerializeHandler); Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedClass.SerializeHandler); Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedNestedClass.SerializeHandler); Assert.Null(MetadataAndSerializationContext.Default.ObjectArray.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs index 0f9df146d25450..6a180d517f63d4 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs @@ -21,6 +21,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(HighLowTempsRecord), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)] @@ -67,6 +68,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType2.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyIntermediateType.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTempsRecord.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedClass.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedNestedClass.SerializeHandler); Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.SerializeHandler); @@ -109,6 +111,7 @@ public override void EnsureFastPathGeneratedAsExpected() [JsonSerializable(typeof(MyTypeWithPropertyOrdering))] [JsonSerializable(typeof(MyIntermediateType))] [JsonSerializable(typeof(HighLowTempsImmutable))] + [JsonSerializable(typeof(HighLowTempsRecord))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] [JsonSerializable(typeof(object[]))] @@ -178,6 +181,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(MetadataContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); Assert.Null(MetadataContext.Default.MyIntermediateType.SerializeHandler); Assert.Null(MetadataContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.Null(MetadataContext.Default.HighLowTempsRecord.SerializeHandler); Assert.Null(MetadataContext.Default.MyNestedClass.SerializeHandler); Assert.Null(MetadataContext.Default.MyNestedNestedClass.SerializeHandler); Assert.Null(MetadataContext.Default.ObjectArray.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs index 202a014f2d6077..e274c9ec04372f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs @@ -22,6 +22,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof(HighLowTempsRecord), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)] @@ -69,6 +70,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(MixedModeContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); Assert.NotNull(MixedModeContext.Default.MyIntermediateType.SerializeHandler); Assert.Null(MixedModeContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.Null(MixedModeContext.Default.HighLowTempsRecord.SerializeHandler); Assert.NotNull(MixedModeContext.Default.MyNestedClass.SerializeHandler); Assert.NotNull(MixedModeContext.Default.MyNestedNestedClass.SerializeHandler); Assert.Null(MixedModeContext.Default.ObjectArray.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index cfc6d486c7dd03..f9aded985c69a7 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -736,6 +736,18 @@ public virtual void ParameterizedConstructor() Assert.Equal(2, obj.Low); } + [Fact] + public virtual void PositionalRecord() + { + string json = JsonSerializer.Serialize(new HighLowTempsRecord(1, 2), DefaultContext.HighLowTempsRecord); + Assert.Contains(@"""High"":1", json); + Assert.Contains(@"""Low"":2", json); + + HighLowTempsRecord obj = JsonSerializer.Deserialize(json, DefaultContext.HighLowTempsRecord); + Assert.Equal(1, obj.High); + Assert.Equal(2, obj.Low); + } + [Fact] public virtual void EnumAndNullable() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs index b177cfff8ea117..e3cda3daa9d3b0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs @@ -22,6 +22,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(MyTypeWithPropertyOrdering))] [JsonSerializable(typeof(MyIntermediateType))] [JsonSerializable(typeof(HighLowTempsImmutable))] + [JsonSerializable(typeof(HighLowTempsRecord))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] [JsonSerializable(typeof(object[]))] @@ -63,6 +64,7 @@ internal partial class SerializationContext : JsonSerializerContext, ITestContex [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(HighLowTempsRecord), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -105,6 +107,7 @@ internal partial class SerializationWithPerTypeAttributeContext : JsonSerializer [JsonSerializable(typeof(MyTypeWithPropertyOrdering), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof(HighLowTempsRecord), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -158,6 +161,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(SerializationContext.Default.MyTypeWithPropertyOrdering.SerializeHandler); Assert.NotNull(SerializationContext.Default.MyIntermediateType.SerializeHandler); Assert.NotNull(SerializationContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.NotNull(SerializationContext.Default.HighLowTempsRecord.SerializeHandler); Assert.NotNull(SerializationContext.Default.MyNestedClass.SerializeHandler); Assert.NotNull(SerializationContext.Default.MyNestedNestedClass.SerializeHandler); Assert.Null(SerializationContext.Default.ObjectArray.SerializeHandler); @@ -436,6 +440,16 @@ public override void ParameterizedConstructor() JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.HighLowTempsImmutable), typeof(HighLowTempsImmutable)); } + [Fact] + public override void PositionalRecord() + { + string json = JsonSerializer.Serialize(new HighLowTempsRecord(1, 2), DefaultContext.HighLowTempsRecord); + Assert.Contains(@"""High"":1", json); + Assert.Contains(@"""Low"":2", json); + + JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.HighLowTempsRecord), typeof(HighLowTempsRecord)); + } + [Fact] public void OnSerializeCallbacks() { @@ -482,6 +496,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType2.SerializeHandler); Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyIntermediateType.SerializeHandler); Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTempsImmutable.SerializeHandler); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTempsRecord.SerializeHandler); Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedClass.SerializeHandler); Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedNestedClass.SerializeHandler); Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.SerializeHandler); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs index a9124235863371..42578ddc950787 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs @@ -106,6 +106,8 @@ public class HighLowTempsImmutable public HighLowTempsImmutable(int high, int low) => (High, Low) = (high, low); } + public record HighLowTempsRecord(int High, int Low); + public class EmptyPoco { }