Fix recursive type support when consuming source generated code from the .NET 6 sdk...
authorEirik Tsarpalis <eirik.tsarpalis@gmail.com>
Wed, 31 May 2023 13:59:31 +0000 (14:59 +0100)
committerGitHub <noreply@github.com>
Wed, 31 May 2023 13:59:31 +0000 (14:59 +0100)
19 files changed:
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfo.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.GetJsonTypeInfo.g.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.ListDateTimeOffset.g.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.MyLinkedList.g.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.PropertyNames.g.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.g.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60RegressionTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70/Net70GeneratedContext.GetJsonTypeInfo.g.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70/Net70GeneratedContext.MyLinkedList.g.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70/Net70GeneratedContext.PropertyNames.g.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70/Net70GeneratedContext.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70/Net70GeneratedContext.g.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70RegressionTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj

index 14f66ef..0fff894 100644 (file)
@@ -569,9 +569,9 @@ namespace System.Text.Json.SourceGeneration
                     string getterValue = property switch
                     {
                         { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "null",
-                        { CanUseGetter: true } => $"static (obj) => (({declaringTypeFQN})obj).{propertyName}",
+                        { CanUseGetter: true } => $"static obj => (({declaringTypeFQN})obj).{propertyName}",
                         { CanUseGetter: false, HasJsonInclude: true }
-                            => $"""static (obj) => throw new {InvalidOperationExceptionTypeRef}("{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.TypeRef.Name, propertyName)}")""",
+                            => $"""static _ => throw new {InvalidOperationExceptionTypeRef}("{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.TypeRef.Name, propertyName)}")""",
                         _ => "null"
                     };
 
@@ -838,7 +838,7 @@ namespace System.Text.Json.SourceGeneration
 
                 const string ArgsVarName = "args";
 
-                StringBuilder sb = new($"static ({ArgsVarName}) => new {typeGenerationSpec.TypeRef.FullyQualifiedName}(");
+                StringBuilder sb = new($"static {ArgsVarName} => new {typeGenerationSpec.TypeRef.FullyQualifiedName}(");
 
                 if (parameters.Count > 0)
                 {
index 75b81cf..8edd987 100644 (file)
@@ -102,7 +102,7 @@ namespace System.Text.Json.Serialization.Metadata
 
             if (state.IsPropertyOrderSpecified)
             {
-                typeInfo.SortProperties();
+                typeInfo.PropertyList.SortProperties();
             }
         }
 
@@ -210,7 +210,7 @@ namespace System.Text.Json.Serialization.Metadata
             }
 
             Debug.Assert(jsonPropertyInfo.Name != null);
-            typeInfo.AddProperty(jsonPropertyInfo, ref state);
+            typeInfo.PropertyList.AddPropertyWithConflictResolution(jsonPropertyInfo, ref state);
         }
 
         [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
index 381f3c4..7401296 100644 (file)
@@ -14,7 +14,7 @@ namespace System.Text.Json.Serialization.Metadata
         /// </summary>
         private static JsonTypeInfo<T> CreateCore<T>(JsonConverter converter, JsonSerializerOptions options)
         {
-            JsonTypeInfo<T> typeInfo = new JsonTypeInfo<T>(converter, options);
+            var typeInfo = new JsonTypeInfo<T>(converter, options);
             typeInfo.PopulatePolymorphismMetadata();
             typeInfo.MapInterfaceTypesToCallbacks();
 
@@ -29,7 +29,7 @@ namespace System.Text.Json.Serialization.Metadata
         private static JsonTypeInfo<T> CreateCore<T>(JsonSerializerOptions options, JsonObjectInfoValues<T> objectInfo)
         {
             JsonConverter<T> converter = GetConverter(objectInfo);
-            JsonTypeInfo<T> typeInfo = new JsonTypeInfo<T>(converter, options);
+            var typeInfo = new JsonTypeInfo<T>(converter, options);
             if (objectInfo.ObjectWithParameterizedConstructorCreator != null)
             {
                 typeInfo.CreateObjectWithArgs = objectInfo.ObjectWithParameterizedConstructorCreator;
@@ -41,7 +41,15 @@ namespace System.Text.Json.Serialization.Metadata
                 typeInfo.CreateObjectForExtensionDataProperty = ((JsonTypeInfo)typeInfo).CreateObject;
             }
 
-            PopulateProperties(typeInfo, objectInfo.PropertyMetadataInitializer);
+            if (objectInfo.PropertyMetadataInitializer != null)
+            {
+                typeInfo.SourceGenDelayedPropertyInitializer = objectInfo.PropertyMetadataInitializer;
+            }
+            else
+            {
+                typeInfo.PropertyMetadataSerializationNotSupported = true;
+            }
+
             typeInfo.SerializeHandler = objectInfo.SerializeHandler;
             typeInfo.NumberHandling = objectInfo.NumberHandling;
             typeInfo.PopulatePolymorphismMetadata();
@@ -62,15 +70,16 @@ namespace System.Text.Json.Serialization.Metadata
             object? createObjectWithArgs = null,
             object? addFunc = null)
         {
+            if (collectionInfo is null)
+            {
+                ThrowHelper.ThrowArgumentNullException(nameof(collectionInfo));
+            }
+
             converter = collectionInfo.SerializeHandler != null
                 ? new JsonMetadataServicesConverter<T>(converter)
                 : converter;
 
             JsonTypeInfo<T> typeInfo = new JsonTypeInfo<T>(converter, options);
-            if (collectionInfo is null)
-            {
-                ThrowHelper.ThrowArgumentNullException(nameof(collectionInfo));
-            }
 
             typeInfo.KeyTypeInfo = collectionInfo.KeyInfo;
             typeInfo.ElementTypeInfo = collectionInfo.ElementInfo;
@@ -116,28 +125,15 @@ namespace System.Text.Json.Serialization.Metadata
             }
         }
 
-        private static void PopulateProperties(JsonTypeInfo typeInfo, Func<JsonSerializerContext, JsonPropertyInfo[]?>? propInitFunc)
+        internal static void PopulateProperties(JsonTypeInfo typeInfo, JsonTypeInfo.JsonPropertyInfoList propertyList, Func<JsonSerializerContext, JsonPropertyInfo[]> propInitFunc)
         {
             Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object);
-            Debug.Assert(!typeInfo.IsReadOnly);
-
-            JsonSerializerContext? context = (typeInfo.OriginatingResolver ?? typeInfo.Options.TypeInfoResolver) as JsonSerializerContext;
-            if (propInitFunc?.Invoke(context!) is not JsonPropertyInfo[] properties)
-            {
-                if (typeInfo.Type == JsonTypeInfo.ObjectType)
-                {
-                    return;
-                }
+            Debug.Assert(!typeInfo.IsConfigured);
+            Debug.Assert(typeInfo.Type != JsonTypeInfo.ObjectType);
+            Debug.Assert(typeInfo.Converter.ElementType is null);
 
-                if (typeInfo.Converter.ElementType != null)
-                {
-                    // Nullable<> or F# optional converter strategy is set to element strategy
-                    return;
-                }
-
-                typeInfo.PropertyMetadataSerializationNotSupported = true;
-                return;
-            }
+            JsonSerializerContext? context = typeInfo.Options.TypeInfoResolver as JsonSerializerContext;
+            JsonPropertyInfo[] properties = propInitFunc(context!);
 
             // TODO update the source generator so that all property
             // hierarchy resolution is happening at compile time.
@@ -161,7 +157,7 @@ namespace System.Text.Json.Serialization.Metadata
                     continue;
                 }
 
-                typeInfo.AddProperty(jsonPropertyInfo, ref state);
+                propertyList.AddPropertyWithConflictResolution(jsonPropertyInfo, ref state);
             }
 
             // NB we don't need to sort source gen properties here since they were already sorted at compile time.
index 751d1fe..40edaca 100644 (file)
@@ -5,7 +5,6 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
-using System.Text.Json.Reflection;
 
 namespace System.Text.Json.Serialization.Metadata
 {
index c5355b7..b7ebe9b 100644 (file)
@@ -213,19 +213,48 @@ namespace System.Text.Json.Serialization.Metadata
         /// It is required that added <see cref="JsonPropertyInfo"/> entries are unique up to <see cref="JsonPropertyInfo.Name"/>,
         /// however this will only be validated on serialization, once the metadata instance gets locked for further modification.
         /// </remarks>
-        public IList<JsonPropertyInfo> Properties => _properties ??= new(this);
-        private JsonPropertyInfoList? _properties;
+        public IList<JsonPropertyInfo> Properties => PropertyList;
 
-        internal void SortProperties()
+        internal JsonPropertyInfoList PropertyList
         {
-            Debug.Assert(!IsConfigured);
-            Debug.Assert(_properties != null && _properties.Count > 0);
+            get
+            {
+                return _properties ?? CreatePropertyList();
+                JsonPropertyInfoList CreatePropertyList()
+                {
+                    var list = new JsonPropertyInfoList(this);
+                    if (_sourceGenDelayedPropertyInitializer is { } propInit)
+                    {
+                        // .NET 6 source gen backward compatibility -- ensure that the
+                        // property initializer delegate is invoked lazily.
+                        JsonMetadataServices.PopulateProperties(this, list, propInit);
+                    }
 
-            _properties.SortProperties();
-            PropertyCache?.List.StableSortByKey(static propInfo => propInfo.Value.Order);
+                    JsonPropertyInfoList? result = Interlocked.CompareExchange(ref _properties, list, null);
+                    _sourceGenDelayedPropertyInitializer = null;
+                    return result ?? list;
+                }
+            }
         }
 
         /// <summary>
+        /// Stores the .NET 6-style property initialization delegate for delayed evaluation.
+        /// </summary>
+        internal Func<JsonSerializerContext, JsonPropertyInfo[]>? SourceGenDelayedPropertyInitializer
+        {
+            get => _sourceGenDelayedPropertyInitializer;
+            set
+            {
+                Debug.Assert(!IsReadOnly);
+                Debug.Assert(_properties is null, "must not be set if a property list has been initialized.");
+                _sourceGenDelayedPropertyInitializer = value;
+            }
+        }
+
+        private Func<JsonSerializerContext, JsonPropertyInfo[]>? _sourceGenDelayedPropertyInitializer;
+        private JsonPropertyInfoList? _properties;
+
+        /// <summary>
         /// Gets or sets a configuration object specifying polymorphism metadata.
         /// </summary>
         /// <exception cref="ArgumentException">
@@ -956,76 +985,6 @@ namespace System.Text.Json.Serialization.Metadata
         internal abstract ValueTask<object?> DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken);
         internal abstract object? DeserializeAsObject(Stream utf8Json);
 
-        /// <summary>
-        /// Used by the built-in resolvers to add property metadata applying conflict resolution.
-        /// </summary>
-        internal void AddProperty(JsonPropertyInfo jsonPropertyInfo, ref PropertyHierarchyResolutionState state)
-        {
-            Debug.Assert(jsonPropertyInfo.MemberName != null, "MemberName can be null in custom JsonPropertyInfo instances and should never be passed in this method");
-            string memberName = jsonPropertyInfo.MemberName;
-
-            JsonPropertyInfoList properties = _properties ??= new(this);
-
-            if (state.AddedProperties.TryAdd(jsonPropertyInfo.Name, (jsonPropertyInfo, properties.Count)))
-            {
-                properties.Add(jsonPropertyInfo);
-                state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
-            }
-            else
-            {
-                // The JsonPropertyNameAttribute or naming policy resulted in a collision.
-                (JsonPropertyInfo other, int index) = state.AddedProperties[jsonPropertyInfo.Name];
-
-                if (other.IsIgnored)
-                {
-                    // Overwrite previously cached property since it has [JsonIgnore].
-                    state.AddedProperties[jsonPropertyInfo.Name] = (jsonPropertyInfo, index);
-                    properties[index] = jsonPropertyInfo;
-                    state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
-                }
-                else
-                {
-                    bool ignoreCurrentProperty;
-
-                    if (!Type.IsInterface)
-                    {
-                        ignoreCurrentProperty =
-                            // Does the current property have `JsonIgnoreAttribute`?
-                            jsonPropertyInfo.IsIgnored ||
-                            // Is the current property hidden by the previously cached property
-                            // (with `new` keyword, or by overriding)?
-                            other.MemberName == memberName ||
-                            // Was a property with the same CLR name ignored? That property hid the current property,
-                            // thus, if it was ignored, the current property should be ignored too.
-                            state.IgnoredProperties?.ContainsKey(memberName) == true;
-                    }
-                    else
-                    {
-                        // Unlike classes, interface hierarchies reject all naming conflicts for non-ignored properties.
-                        // Conflicts like this are possible in two cases:
-                        // 1. Diamond ambiguity in property names, or
-                        // 2. Linear interface hierarchies that use properties with DIMs.
-                        //
-                        // Diamond ambiguities are not supported. Assuming there is demand, we might consider
-                        // adding support for DIMs in the future, however that would require adding more APIs
-                        // for the case of source gen.
-
-                        ignoreCurrentProperty = jsonPropertyInfo.IsIgnored;
-                    }
-
-                    if (!ignoreCurrentProperty)
-                    {
-                        ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo.Name);
-                    }
-                }
-            }
-
-            if (jsonPropertyInfo.IsIgnored)
-            {
-                (state.IgnoredProperties ??= new()).Add(memberName, jsonPropertyInfo);
-            }
-        }
-
         internal ref struct PropertyHierarchyResolutionState
         {
             public PropertyHierarchyResolutionState() { }
@@ -1076,9 +1035,9 @@ namespace System.Text.Json.Serialization.Metadata
             Debug.Assert(PropertyCache is null);
             Debug.Assert(ExtensionDataProperty is null);
 
-            IList<JsonPropertyInfo> properties = (IList<JsonPropertyInfo>?)_properties ?? Array.Empty<JsonPropertyInfo>();
-
+            JsonPropertyInfoList properties = PropertyList;
             JsonPropertyDictionary<JsonPropertyInfo> propertyCache = CreatePropertyCache(capacity: properties.Count);
+
             int numberOfRequiredProperties = 0;
             bool arePropertiesSorted = true;
             int previousPropertyOrder = int.MinValue;
@@ -1123,15 +1082,16 @@ namespace System.Text.Json.Serialization.Metadata
                 property.Configure();
             }
 
-            NumberOfRequiredProperties = numberOfRequiredProperties;
-            PropertyCache = propertyCache;
-
             if (!arePropertiesSorted)
             {
                 // Properties have been configured by the user and require sorting.
-                SortProperties();
+                properties.SortProperties();
+                propertyCache.List.StableSortByKey(static propInfo => propInfo.Value.Order);
             }
 
+            NumberOfRequiredProperties = numberOfRequiredProperties;
+            PropertyCache = propertyCache;
+
             // Override global UnmappedMemberHandling configuration
             // if type specifies an extension data property.
             EffectiveUnmappedMemberHandling = UnmappedMemberHandling ??
@@ -1206,6 +1166,7 @@ namespace System.Text.Json.Serialization.Metadata
 
             ParameterCount = jsonParameters.Length;
             ParameterCache = parameterCache;
+            ParameterInfoValues = null;
         }
 
         internal static void ValidateType(Type type)
@@ -1346,7 +1307,7 @@ namespace System.Text.Json.Serialization.Metadata
             }
         }
 
-        private sealed class JsonPropertyInfoList : ConfigurationList<JsonPropertyInfo>
+        internal sealed class JsonPropertyInfoList : ConfigurationList<JsonPropertyInfo>
         {
             private readonly JsonTypeInfo _jsonTypeInfo;
 
@@ -1355,10 +1316,13 @@ namespace System.Text.Json.Serialization.Metadata
                 _jsonTypeInfo = jsonTypeInfo;
             }
 
-            public override bool IsReadOnly => _jsonTypeInfo.IsReadOnly || _jsonTypeInfo.Kind != JsonTypeInfoKind.Object;
+            public override bool IsReadOnly => _jsonTypeInfo._properties == this && _jsonTypeInfo.IsReadOnly || _jsonTypeInfo.Kind != JsonTypeInfoKind.Object;
             protected override void OnCollectionModifying()
             {
-                _jsonTypeInfo.VerifyMutable();
+                if (_jsonTypeInfo._properties == this)
+                {
+                    _jsonTypeInfo.VerifyMutable();
+                }
 
                 if (_jsonTypeInfo.Kind != JsonTypeInfoKind.Object)
                 {
@@ -1372,7 +1336,78 @@ namespace System.Text.Json.Serialization.Metadata
             }
 
             public void SortProperties()
-                => _list.StableSortByKey(static propInfo => propInfo.Order);
+            {
+                _list.StableSortByKey(static propInfo => propInfo.Order);
+            }
+
+            /// <summary>
+            /// Used by the built-in resolvers to add property metadata applying conflict resolution.
+            /// </summary>
+            public void AddPropertyWithConflictResolution(JsonPropertyInfo jsonPropertyInfo, ref PropertyHierarchyResolutionState state)
+            {
+                Debug.Assert(!_jsonTypeInfo.IsConfigured);
+                Debug.Assert(jsonPropertyInfo.MemberName != null, "MemberName can be null in custom JsonPropertyInfo instances and should never be passed in this method");
+                string memberName = jsonPropertyInfo.MemberName;
+
+                if (state.AddedProperties.TryAdd(jsonPropertyInfo.Name, (jsonPropertyInfo, Count)))
+                {
+                    Add(jsonPropertyInfo);
+                    state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
+                }
+                else
+                {
+                    // The JsonPropertyNameAttribute or naming policy resulted in a collision.
+                    (JsonPropertyInfo other, int index) = state.AddedProperties[jsonPropertyInfo.Name];
+
+                    if (other.IsIgnored)
+                    {
+                        // Overwrite previously cached property since it has [JsonIgnore].
+                        state.AddedProperties[jsonPropertyInfo.Name] = (jsonPropertyInfo, index);
+                        this[index] = jsonPropertyInfo;
+                        state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
+                    }
+                    else
+                    {
+                        bool ignoreCurrentProperty;
+
+                        if (!_jsonTypeInfo.Type.IsInterface)
+                        {
+                            ignoreCurrentProperty =
+                                // Does the current property have `JsonIgnoreAttribute`?
+                                jsonPropertyInfo.IsIgnored ||
+                                // Is the current property hidden by the previously cached property
+                                // (with `new` keyword, or by overriding)?
+                                other.MemberName == memberName ||
+                                // Was a property with the same CLR name ignored? That property hid the current property,
+                                // thus, if it was ignored, the current property should be ignored too.
+                                state.IgnoredProperties?.ContainsKey(memberName) == true;
+                        }
+                        else
+                        {
+                            // Unlike classes, interface hierarchies reject all naming conflicts for non-ignored properties.
+                            // Conflicts like this are possible in two cases:
+                            // 1. Diamond ambiguity in property names, or
+                            // 2. Linear interface hierarchies that use properties with DIMs.
+                            //
+                            // Diamond ambiguities are not supported. Assuming there is demand, we might consider
+                            // adding support for DIMs in the future, however that would require adding more APIs
+                            // for the case of source gen.
+
+                            ignoreCurrentProperty = jsonPropertyInfo.IsIgnored;
+                        }
+
+                        if (!ignoreCurrentProperty)
+                        {
+                            ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(_jsonTypeInfo.Type, jsonPropertyInfo.Name);
+                        }
+                    }
+                }
+
+                if (jsonPropertyInfo.IsIgnored)
+                {
+                    (state.IgnoredProperties ??= new()).Add(memberName, jsonPropertyInfo);
+                }
+            }
         }
 
         [DebuggerBrowsable(DebuggerBrowsableState.Never)]
index 61eec78..5d78355 100644 (file)
@@ -27,6 +27,11 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
                 return this.ClassWithCustomConverter;
             }
         
+            if (type == typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList))
+            {
+                return this.MyLinkedList;
+            }
+        
             return null!;
         }
     }
index f969da8..576f4f0 100644 (file)
@@ -1,4 +1,11 @@
-// <auto-generated/>
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Source files represent a source generated JsonSerializerContext as produced by the .NET 6 SDK.
+// Used to validate correctness of contexts generated by previous SDKs against the current System.Text.Json runtime components.
+// Unless absolutely necessary DO NOT MODIFY any of these files -- it would invalidate the purpose of the regression tests.
+
+// <auto-generated/>
 #nullable enable
 
 // Suppress warnings about [Obsolete] member usage in generated code.
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.MyLinkedList.g.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net60/Net60GeneratedContext.MyLinkedList.g.cs
new file mode 100644 (file)
index 0000000..fecbc6b
--- /dev/null
@@ -0,0 +1,144 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Source files represent a source generated JsonSerializerContext as produced by the .NET 6 SDK.
+// Used to validate correctness of contexts generated by previous SDKs against the current System.Text.Json runtime components.
+// Unless absolutely necessary DO NOT MODIFY any of these files -- it would invalidate the purpose of the regression tests.
+
+// <auto-generated/>
+#nullable enable
+
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+
+namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
+{
+    public partial class Net60GeneratedContext
+    {
+        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>? _MyLinkedList;
+        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList> MyLinkedList
+        {
+            get
+            {
+                if (_MyLinkedList == null)
+                {
+                    global::System.Text.Json.Serialization.JsonConverter? customConverter;
+                    if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList))) != null)
+                    {
+                        _MyLinkedList = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>(Options, customConverter);
+                    }
+                    else
+                    {
+                        global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList> objectInfo = new global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>()
+                        {
+                            ObjectCreator = null,
+                            ObjectWithParameterizedConstructorCreator = static (args) => new global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList((global::System.Int32)args[0], (global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)args[1]),
+                            PropertyMetadataInitializer = MyLinkedListPropInit,
+                            ConstructorParameterMetadataInitializer = MyLinkedListCtorParamInit,
+                            NumberHandling = default,
+                            SerializeHandler = MyLinkedListSerializeHandler
+                        };
+
+                        _MyLinkedList = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>(Options, objectInfo);
+                    }
+                }
+
+                return _MyLinkedList;
+            }
+        }
+
+        private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] MyLinkedListPropInit(global::System.Text.Json.Serialization.JsonSerializerContext context)
+        {
+            global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.Net60GeneratedContext jsonContext = (global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.Net60GeneratedContext)context;
+            global::System.Text.Json.JsonSerializerOptions options = context.Options;
+
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[2];
+
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32> info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32>()
+            {
+                IsProperty = true,
+                IsPublic = true,
+                IsVirtual = false,
+                DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList),
+                PropertyTypeInfo = jsonContext.Int32,
+                Converter = null,
+                Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Value,
+                Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Value = value!,
+                IgnoreCondition = null,
+                HasJsonInclude = false,
+                IsExtensionData = false,
+                NumberHandling = default,
+                PropertyName = "Value",
+                JsonPropertyName = null
+            };
+
+            properties[0] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Int32>(options, info0);
+
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList> info1 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>()
+            {
+                IsProperty = true,
+                IsPublic = true,
+                IsVirtual = false,
+                DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList),
+                PropertyTypeInfo = jsonContext.MyLinkedList,
+                Converter = null,
+                Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Nested!,
+                Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Nested = value!,
+                IgnoreCondition = null,
+                HasJsonInclude = false,
+                IsExtensionData = false,
+                NumberHandling = default,
+                PropertyName = "Nested",
+                JsonPropertyName = null
+            };
+
+            properties[1] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>(options, info1);
+
+            return properties;
+        }
+
+        private static void MyLinkedListSerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList? value)
+        {
+            if (value == null)
+            {
+                writer.WriteNullValue();
+                return;
+            }
+
+            writer.WriteStartObject();
+            writer.WriteNumber(PropName_Value, value.Value);
+            writer.WritePropertyName(PropName_Nested);
+            MyLinkedListSerializeHandler(writer, value.Nested!);
+
+            writer.WriteEndObject();
+        }
+
+        private static global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] MyLinkedListCtorParamInit()
+        {
+            global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] parameters = new global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[2];
+            global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues info;
+
+            info = new()
+            {
+                Name = "value",
+                ParameterType = typeof(global::System.Int32),
+                Position = 0,
+                HasDefaultValue = false,
+                DefaultValue = default(global::System.Int32)
+            };
+            parameters[0] = info;
+
+            info = new()
+            {
+                Name = "nested",
+                ParameterType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList),
+                Position = 1,
+                HasDefaultValue = false,
+                DefaultValue = default(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)
+            };
+            parameters[1] = info;
+
+            return parameters;
+        }
+    }
+}
index 84f4b42..21963c2 100644 (file)
@@ -24,5 +24,7 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
         private static readonly global::System.Text.Json.JsonEncodedText PropName_SummaryWords = global::System.Text.Json.JsonEncodedText.Encode("SummaryWords");
         private static readonly global::System.Text.Json.JsonEncodedText PropName_High = global::System.Text.Json.JsonEncodedText.Encode("High");
         private static readonly global::System.Text.Json.JsonEncodedText PropName_Low = global::System.Text.Json.JsonEncodedText.Encode("Low");
+        private static readonly global::System.Text.Json.JsonEncodedText PropName_Value = global::System.Text.Json.JsonEncodedText.Encode("Value");
+        private static readonly global::System.Text.Json.JsonEncodedText PropName_Nested = global::System.Text.Json.JsonEncodedText.Encode("Nested");
     }
 }
index 7158db2..207878b 100644 (file)
@@ -12,6 +12,7 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
 {
     //[JsonSerializable(typeof(WeatherForecastWithPOCOs))]
     //[JsonSerializable(typeof(ClassWithCustomConverter))]
+    //[JsonSerializable(typeof(MyLinkedList))]
     public partial class Net60GeneratedContext : JsonSerializerContext { }
 
     public class WeatherForecastWithPOCOs
@@ -31,6 +32,18 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
         public int Low { get; set; }
     }
 
+    public class MyLinkedList
+    {
+        public MyLinkedList(int value, MyLinkedList? nested)
+        {
+            Value = value;
+            Nested = nested;
+        }
+
+        public int Value { get; set; }
+        public MyLinkedList? Nested { get; set; }
+    }
+
     [JsonConverter(typeof(CustomConverter))]
     public class ClassWithCustomConverter
     {
index 6b6bf15..caf7ac4 100644 (file)
@@ -14,7 +14,7 @@
 namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
 {
     
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "6.0.6.21309")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "6.0.8.17311")]
     public partial class Net60GeneratedContext
     {
         
index 21a87bb..3dba6f8 100644 (file)
@@ -141,6 +141,26 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests
         }
 
         [Fact]
+        public static void SupportsRecursiveTypeSerialization()
+        {
+            JsonTypeInfo<MyLinkedList> jsonTypeInfo = Net60GeneratedContext.Default.MyLinkedList;
+
+            MyLinkedList linkedList = new(
+                value: 0,
+                nested: new(
+                    value: 1,
+                    nested: new(
+                        value: 2,
+                        nested: null)));
+
+            string json = JsonSerializer.Serialize(linkedList, jsonTypeInfo);
+            Assert.Equal("""{"Value":0,"Nested":{"Value":1,"Nested":{"Value":2,"Nested":null}}}""", json);
+
+            linkedList = JsonSerializer.Deserialize(json, jsonTypeInfo);
+            Assert.Equal(2, linkedList.Nested.Nested.Value);
+        }
+
+        [Fact]
         public static void CombinedContexts_ThrowsInvalidOperationException()
         {
             var options = new JsonSerializerOptions
index 1d59adb..ccb1c80 100644 (file)
@@ -65,6 +65,11 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
                 return this.ClassWithCustomConverter;
             }
         
+            if (type == typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList))
+            {
+                return this.MyLinkedList;
+            }
+        
             return null!;
         }
         
@@ -115,6 +120,11 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
                 return Create_ClassWithCustomConverter(options, makeReadOnly: false);
             }
         
+            if (type == typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList))
+            {
+                return Create_MyLinkedList(options, makeReadOnly: false);
+            }
+        
             return null;
         }
         
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70/Net70GeneratedContext.MyLinkedList.g.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/SourceGenRegressionTests/Net70/Net70GeneratedContext.MyLinkedList.g.cs
new file mode 100644 (file)
index 0000000..78c6e81
--- /dev/null
@@ -0,0 +1,153 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Source files represent a source generated JsonSerializerContext as produced by the .NET 7 SDK.
+// Used to validate correctness of contexts generated by previous SDKs against the current System.Text.Json runtime components.
+// Unless absolutely necessary DO NOT MODIFY any of these files -- it would invalidate the purpose of the regression tests.
+
+// <auto-generated/>
+
+#nullable enable annotations
+#nullable disable warnings
+
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+
+namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
+{
+    public partial class Net70GeneratedContext
+    {
+        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>? _MyLinkedList;
+        /// <summary>
+        /// Defines the source generated JSON serialization contract metadata for a given type.
+        /// </summary>
+        public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> MyLinkedList
+        {
+            get => _MyLinkedList ??= Create_MyLinkedList(Options, makeReadOnly: true);
+        }
+
+        private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> Create_MyLinkedList(global::System.Text.Json.JsonSerializerOptions options, bool makeReadOnly)
+        {
+            global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>? jsonTypeInfo = null;
+            global::System.Text.Json.Serialization.JsonConverter? customConverter;
+            if (options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(options, typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList))) != null)
+            {
+                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>(options, customConverter);
+            }
+            else
+            {
+                global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> objectInfo = new global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>()
+                {
+                    ObjectCreator = null,
+                    ObjectWithParameterizedConstructorCreator = static (args) => new global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList((global::System.Int32)args[0], (global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)args[1]),
+                    PropertyMetadataInitializer = _ => MyLinkedListPropInit(options),
+                    ConstructorParameterMetadataInitializer = MyLinkedListCtorParamInit,
+                    NumberHandling = default,
+                    SerializeHandler = MyLinkedListSerializeHandler
+                };
+
+                jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>(options, objectInfo);
+            }
+
+            if (makeReadOnly)
+            {
+                jsonTypeInfo.MakeReadOnly();
+            }
+
+            return jsonTypeInfo;
+        }
+
+        private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] MyLinkedListPropInit(global::System.Text.Json.JsonSerializerOptions options)
+        {
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[2];
+
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32> info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32>()
+            {
+                IsProperty = true,
+                IsPublic = true,
+                IsVirtual = false,
+                DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList),
+                Converter = null,
+                Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Value,
+                Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Value = value!,
+                IgnoreCondition = null,
+                HasJsonInclude = false,
+                IsExtensionData = false,
+                NumberHandling = default,
+                PropertyName = "Value",
+                JsonPropertyName = null
+            };
+
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo propertyInfo0 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Int32>(options, info0);
+            properties[0] = propertyInfo0;
+
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> info1 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>()
+            {
+                IsProperty = true,
+                IsPublic = true,
+                IsVirtual = false,
+                DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList),
+                Converter = null,
+                Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Nested!,
+                Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Nested = value!,
+                IgnoreCondition = null,
+                HasJsonInclude = false,
+                IsExtensionData = false,
+                NumberHandling = default,
+                PropertyName = "Nested",
+                JsonPropertyName = null
+            };
+
+            global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo propertyInfo1 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>(options, info1);
+            properties[1] = propertyInfo1;
+
+            return properties;
+        }
+
+        // Intentionally not a static method because we create a delegate to it. Invoking delegates to instance
+        // methods is almost as fast as virtual calls. Static methods need to go through a shuffle thunk.
+        private void MyLinkedListSerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList? value)
+        {
+            if (value == null)
+            {
+                writer.WriteNullValue();
+                return;
+            }
+
+            writer.WriteStartObject();
+            writer.WriteNumber(PropName_Value, value.Value);
+            writer.WritePropertyName(PropName_Nested);
+            MyLinkedListSerializeHandler(writer, value.Nested!);
+
+            writer.WriteEndObject();
+        }
+
+        private static global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] MyLinkedListCtorParamInit()
+        {
+            global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] parameters = new global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[2];
+            global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues info;
+
+            info = new()
+            {
+                Name = "value",
+                ParameterType = typeof(global::System.Int32),
+                Position = 0,
+                HasDefaultValue = false,
+                DefaultValue = default(global::System.Int32)
+            };
+            parameters[0] = info;
+
+            info = new()
+            {
+                Name = "nested",
+                ParameterType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList),
+                Position = 1,
+                HasDefaultValue = false,
+                DefaultValue = default(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)
+            };
+            parameters[1] = info;
+
+            return parameters;
+        }
+    }
+}
index d2edcb1..7ad6a3b 100644 (file)
@@ -26,5 +26,7 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
         private static readonly global::System.Text.Json.JsonEncodedText PropName_SummaryWords = global::System.Text.Json.JsonEncodedText.Encode("SummaryWords");
         private static readonly global::System.Text.Json.JsonEncodedText PropName_High = global::System.Text.Json.JsonEncodedText.Encode("High");
         private static readonly global::System.Text.Json.JsonEncodedText PropName_Low = global::System.Text.Json.JsonEncodedText.Encode("Low");
+        private static readonly global::System.Text.Json.JsonEncodedText PropName_Value = global::System.Text.Json.JsonEncodedText.Encode("Value");
+        private static readonly global::System.Text.Json.JsonEncodedText PropName_Nested = global::System.Text.Json.JsonEncodedText.Encode("Nested");
     }
 }
index ebfbbd7..bcc9cf2 100644 (file)
@@ -12,6 +12,7 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
 {
     //[JsonSerializable(typeof(WeatherForecastWithPOCOs))]
     //[JsonSerializable(typeof(ClassWithCustomConverter))]
+    //[JsonSerializable(typeof(MyLinkedList))]
     public partial class Net70GeneratedContext : JsonSerializerContext { }
 
     public class WeatherForecastWithPOCOs
@@ -31,6 +32,18 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
         public int Low { get; set; }
     }
 
+    public class MyLinkedList
+    {
+        public MyLinkedList(int value, MyLinkedList? nested)
+        {
+            Value = value;
+            Nested = nested;
+        }
+
+        public int Value { get; set; }
+        public MyLinkedList? Nested { get; set; }
+    }
+
     [JsonConverter(typeof(CustomConverter))]
     public class ClassWithCustomConverter
     {
index d6427a7..daa9d4a 100644 (file)
@@ -16,7 +16,7 @@
 namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
 {
     
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "7.0.7.1805")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "7.0.8.17405")]
     public partial class Net70GeneratedContext
     {
         
index 38c06bc..af0bcfd 100644 (file)
@@ -141,6 +141,26 @@ namespace System.Text.Json.Tests.SourceGenRegressionTests
         }
 
         [Fact]
+        public static void SupportsRecursiveTypeSerialization()
+        {
+            JsonTypeInfo<MyLinkedList> jsonTypeInfo = Net70GeneratedContext.Default.MyLinkedList;
+
+            MyLinkedList linkedList = new(
+                value: 0,
+                nested: new(
+                    value: 1,
+                    nested: new(
+                        value: 2,
+                        nested: null)));
+
+            string json = JsonSerializer.Serialize(linkedList, jsonTypeInfo);
+            Assert.Equal("""{"Value":0,"Nested":{"Value":1,"Nested":{"Value":2,"Nested":null}}}""", json);
+
+            linkedList = JsonSerializer.Deserialize(json, jsonTypeInfo);
+            Assert.Equal(2, linkedList.Nested.Nested.Value);
+        }
+
+        [Fact]
         public static void CombinedContexts_WorksAsExpected()
         {
             var options = new JsonSerializerOptions
index 8d0e4e6..d34d3d1 100644 (file)
     <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.HighLowTemps.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.Int32.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.ListDateTimeOffset.g.cs" />
+    <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.MyLinkedList.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.PropertyNames.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.String.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.StringArray.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.HighLowTemps.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.Int32.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.ListDateTimeOffset.g.cs" />
+    <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.MyLinkedList.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.PropertyNames.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.String.g.cs" />
     <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.StringArray.g.cs" />