diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index da1639e0d110f..c0cfc97026782 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -263,7 +263,7 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL | __`SYSLIB1222`__ | Constructor annotated with JsonConstructorAttribute is inaccessible. | | __`SYSLIB1223`__ | Attributes deriving from JsonConverterAttribute are not supported by the source generator. | | __`SYSLIB1224`__ | Types annotated with JsonSerializableAttribute must be classes deriving from JsonSerializerContext. | -| __`SYSLIB1225`__ | *`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration.* | +| __`SYSLIB1225`__ | Type includes ref like property, field or constructor parameter. | | __`SYSLIB1226`__ | *`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration.* | | __`SYSLIB1227`__ | *`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration.* | | __`SYSLIB1228`__ | *`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration.* | diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs index 3d19fbe868815..688cc31f86697 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs @@ -123,6 +123,14 @@ internal static class DiagnosticDescriptors category: JsonConstants.SystemTextJsonSourceGenerationName, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public static DiagnosticDescriptor TypeContainsRefLikeMember { get; } = DiagnosticDescriptorHelper.Create( + id: "SYSLIB1225", + title: new LocalizableResourceString(nameof(SR.TypeContainsRefLikeMemberTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.TypeContainsRefLikeMemberFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)), + category: JsonConstants.SystemTextJsonSourceGenerationName, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); } } } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 5c3d3aa9474e2..d7c9bc72041b2 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -522,7 +522,7 @@ private TypeGenerationSpec ParseTypeGenerationSpec(in TypeToGenerate typeToGener out TypeRef? customConverterType, out bool isPolymorphic); - if (type is INamedTypeSymbol { IsUnboundGenericType: true } or IErrorTypeSymbol) + if (type is { IsRefLikeType: true } or INamedTypeSymbol { IsUnboundGenericType: true } or IErrorTypeSymbol) { classType = ClassType.TypeUnsupportedBySourceGen; } @@ -574,6 +574,12 @@ private TypeGenerationSpec ParseTypeGenerationSpec(in TypeToGenerate typeToGener immutableCollectionFactoryTypeFullName = null; collectionType = default; } + else if (valueType.IsRefLikeType || keyType?.IsRefLikeType is true) + { + classType = ClassType.TypeUnsupportedBySourceGen; + immutableCollectionFactoryTypeFullName = null; + collectionType = default; + } else { if (type.CanUseDefaultConstructorForDeserialization(out IMethodSymbol? defaultCtor)) @@ -1165,6 +1171,17 @@ private bool IsValidDataExtensionPropertyType(ITypeSymbol type) return null; } + if (memberType.IsRefLikeType) + { + // Skip all ref-like members and emit a diagnostic unless the property is being explicitly ignored. + if (ignoreCondition is not JsonIgnoreCondition.Always) + { + ReportDiagnostic(DiagnosticDescriptors.TypeContainsRefLikeMember, memberInfo.GetLocation(), declaringType.Name, memberInfo.Name); + } + + return null; + } + string effectiveJsonPropertyName = DetermineEffectiveJsonPropertyName(memberInfo.Name, jsonPropertyName, options); string propertyNameFieldName = DeterminePropertyNameFieldName(effectiveJsonPropertyName); @@ -1246,62 +1263,60 @@ private void ProcessMemberCustomAttributes( switch (attributeType.ToDisplayString()) { case JsonIgnoreAttributeFullName: + { + ImmutableArray> namedArgs = attributeData.NamedArguments; + + if (namedArgs.Length == 0) { - ImmutableArray> namedArgs = attributeData.NamedArguments; - - if (namedArgs.Length == 0) - { - ignoreCondition = JsonIgnoreCondition.Always; - } - else if (namedArgs.Length == 1 && - namedArgs[0].Value.Type?.ToDisplayString() == JsonIgnoreConditionFullName) - { - ignoreCondition = (JsonIgnoreCondition)namedArgs[0].Value.Value!; - } + ignoreCondition = JsonIgnoreCondition.Always; } - break; - case JsonIncludeAttributeFullName: + else if (namedArgs.Length == 1 && + namedArgs[0].Value.Type?.ToDisplayString() == JsonIgnoreConditionFullName) { - hasJsonInclude = true; + ignoreCondition = (JsonIgnoreCondition)namedArgs[0].Value.Value!; } break; + } + case JsonIncludeAttributeFullName: + { + hasJsonInclude = true; + break; + } case JsonNumberHandlingAttributeFullName: - { - ImmutableArray ctorArgs = attributeData.ConstructorArguments; - numberHandling = (JsonNumberHandling)ctorArgs[0].Value!; - } + { + ImmutableArray ctorArgs = attributeData.ConstructorArguments; + numberHandling = (JsonNumberHandling)ctorArgs[0].Value!; break; + } case JsonObjectCreationHandlingAttributeFullName: - { - ImmutableArray ctorArgs = attributeData.ConstructorArguments; - objectCreationHandling = (JsonObjectCreationHandling)ctorArgs[0].Value!; - } + { + ImmutableArray ctorArgs = attributeData.ConstructorArguments; + objectCreationHandling = (JsonObjectCreationHandling)ctorArgs[0].Value!; break; + } case JsonPropertyNameAttributeFullName: - { - ImmutableArray ctorArgs = attributeData.ConstructorArguments; - jsonPropertyName = (string)ctorArgs[0].Value!; - // Null check here is done at runtime within JsonSerializer. - } + { + ImmutableArray ctorArgs = attributeData.ConstructorArguments; + jsonPropertyName = (string)ctorArgs[0].Value!; + // Null check here is done at runtime within JsonSerializer. break; + } case JsonPropertyOrderAttributeFullName: - { - ImmutableArray ctorArgs = attributeData.ConstructorArguments; - order = (int)ctorArgs[0].Value!; - } + { + ImmutableArray ctorArgs = attributeData.ConstructorArguments; + order = (int)ctorArgs[0].Value!; break; + } case JsonExtensionDataAttributeFullName: - { - isExtensionData = true; - } + { + isExtensionData = true; break; + } case JsonRequiredAttributeFullName: - { - hasJsonRequiredAttribute = true; - } - break; - default: + { + hasJsonRequiredAttribute = true; break; + } } } } @@ -1433,7 +1448,7 @@ private void ProcessMember( if (paramCount == 0) { constructionStrategy = ObjectConstructionStrategy.ParameterlessConstructor; - constructorParameters = Array.Empty(); + constructorParameters = []; } else { @@ -1445,6 +1460,14 @@ private void ProcessMember( for (int i = 0; i < paramCount; i++) { IParameterSymbol parameterInfo = constructor.Parameters[i]; + + if (parameterInfo.Type.IsRefLikeType) + { + ReportDiagnostic(DiagnosticDescriptors.TypeContainsRefLikeMember, parameterInfo.GetLocation(), type.Name, parameterInfo.Name); + constructionStrategy = ObjectConstructionStrategy.NotApplicable; + continue; + } + TypeRef parameterTypeRef = EnqueueType(parameterInfo.Type, typeToGenerate.Mode); constructorParameters[i] = new ParameterGenerationSpec @@ -1459,7 +1482,7 @@ private void ProcessMember( } } - return constructorParameters; + return constructionStrategy is ObjectConstructionStrategy.NotApplicable ? null : constructorParameters; } private List? ParsePropertyInitializers( diff --git a/src/libraries/System.Text.Json/gen/Resources/Strings.resx b/src/libraries/System.Text.Json/gen/Resources/Strings.resx index 519cbffa3ec09..5c2d06929aacb 100644 --- a/src/libraries/System.Text.Json/gen/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/gen/Resources/Strings.resx @@ -207,4 +207,10 @@ The type '{0}' has been annotated with JsonSerializableAttribute but does not derive from JsonSerializerContext. No source code will be generated. + + Type includes ref like property, field or constructor parameter. + + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf index 96bb2680ffcb7..10a4b772a005d 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf @@ -142,6 +142,16 @@ Typ obsahuje více členů s komentářem JsonExtensionDataAttribute + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. Nevygenerovala se metadata serializace pro typ {0}. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf index dbc6ca2ae2b90..fbec6a988af34 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf @@ -142,6 +142,16 @@ Der Typ enthält mehrere Elemente, die mit dem JsonExtensionDataAttribute versehen sind. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. Die Serialisierungsmetadaten für den Typ "{0}" wurden nicht generiert. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf index 878fa15bc7efb..068c218f73142 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf @@ -142,6 +142,16 @@ El tipo tiene varios miembros anotados con JsonExtensionDataAttribute. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. No generó metadatos de serialización para el tipo '{0}". diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf index fbf12b5a733d4..7f22492403d44 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf @@ -142,6 +142,16 @@ Le type comporte plusieurs membres annotés avec JsonExtensionDataAttribute. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. Les métadonnées de sérialisation pour le type « {0} » n’ont pas été générées. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf index 04d5cf68ef4a9..cae8ac97938d2 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf @@ -142,6 +142,16 @@ Nel tipo sono presenti più membri annotati con JsonExtensionDataAttribute. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. Non sono stati generati metadati di serializzazione per il tipo '{0}'. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf index 0224cb65fc26e..1ddb45583e3a2 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf @@ -142,6 +142,16 @@ 型には、'JsonExtensionDataAttribute' に注釈が付けられた複数のメンバーが含まれます。 + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. '{0}'型 のシリアル化メタデータを生成ませんでした。 diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf index 9bc14e0cc3d55..0aa409cc9f661 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf @@ -142,6 +142,16 @@ 형식에 JsonExtensionDataAttribute로 주석이 추가 된 멤버가 여러 개 있습니다. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. '{0}' 형식에 대한 직렬화 메타데이터가 생성되지 않았습니다. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf index d8e7a990013fa..c8eef43ef7deb 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf @@ -142,6 +142,16 @@ Typ ma wiele składowych opatrzonych adnotacjami za pomocą atrybutu JsonExtensionDataAttribute. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. Nie wygenerowano metadanych serializacji dla typu „{0}”. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf index d31fa77f16ee4..d2ae1c3c28a8c 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf @@ -142,6 +142,16 @@ Tipo tem vários membros anotados com JsonExtensionDataAttribute. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. Não gerou metadados de serialização para o tipo '{0}'. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf index de31bb76e6f2d..ffc7012e2fbce 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf @@ -142,6 +142,16 @@ Тип содержит несколько элементов, помеченных с помощью JsonExtensionDataAttribute. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. Метаданные сериализации для типа "{0}" не сформированы. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf index 3ab7559fc8228..2082f57da492c 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf @@ -142,6 +142,16 @@ Tür, JsonExtensionDataAttribute ile açıklanan birden çok üyeye sahip. + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. '{0}' türü için serileştirme meta verileri oluşturulmadı. diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf index 03ce3792f5ec8..34e4c4ff7c0f0 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -142,6 +142,16 @@ 类型具有多个带有 JsonExtensionDataAttribute 注释的成员。 + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. 未生成类型 '{0}' 的序列化元数据。 diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf index ed207add481d9..64c1dca8a9869 100644 --- a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -142,6 +142,16 @@ 類型具有使用 JsonExtensionDataAttribute 標註的多個成員。 + + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor. + + + + Type includes ref like property, field or constructor parameter. + Type includes ref like property, field or constructor parameter. + + Did not generate serialization metadata for type '{0}'. 未產生類型 '{0}' 的序列化中繼資料。 diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs index 054e4f8648a3c..59b2591152955 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json.Reflection; +using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { @@ -185,11 +186,17 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer } else if (numberOfGenericArgs == 2) { + JsonTypeInfo.ValidateType(elementType!); + genericType = converterType.MakeGenericType(typeToConvert, elementType!); } else { Debug.Assert(numberOfGenericArgs == 3); + + JsonTypeInfo.ValidateType(elementType!); + JsonTypeInfo.ValidateType(dictionaryKeyType!); + genericType = converterType.MakeGenericType(typeToConvert, dictionaryKeyType!, elementType!); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs index e0b04f3014279..b82733f591792 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverterFactory.cs @@ -59,6 +59,12 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer { int parameterCount = parameters.Length; + foreach (ParameterInfo parameter in parameters) + { + // Every argument must be of supported type. + JsonTypeInfo.ValidateType(parameter.ParameterType); + } + if (parameterCount <= JsonConstants.UnboxedParameterCountThreshold) { Type placeHolderType = JsonTypeInfo.ObjectType; diff --git a/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs b/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs index 967a3f4366a13..0d92b29633991 100644 --- a/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Text.Json.Serialization.Metadata; +using System.Threading.Tasks; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -304,6 +305,61 @@ public void RespectRequiredConstructorParameters_true_ReportsCorrespondingProper }); } + [Fact] + public async Task ClassWithRefStructProperty_Serialization() + { + if (Serializer.IsSourceGeneratedSerializer) + { + // The source generator warns but otherwise skips ref struct properties. + ClassWithRefStructProperty value = new(); + string json = await Serializer.SerializeWrapper(value); + Assert.Equal("{}", json); + + ClassWithRefStructProperty deserialized = await Serializer.DeserializeWrapper("""{"Value":"abc"}"""); + Assert.True(deserialized.Value.IsEmpty); + } + else + { + // The reflection-based serializer throws. + Assert.Throws(() => Serializer.GetTypeInfo()); + } + } + + [Fact] + public async Task ClassWithRefStructConstructorParameter_Serialization() + { + if (Serializer.IsSourceGeneratedSerializer) + { + // The source generator warns but otherwise skips constructors with ref struct parameters. + ClassWithRefStructConstructorParameter value = new(); + string json = await Serializer.SerializeWrapper(value); + Assert.Equal("""{"Value":"default"}""", json); + + await Assert.ThrowsAsync(() => Serializer.DeserializeWrapper("{}")); + } + else + { + // The reflection-based serializer throws. + Assert.Throws(() => Serializer.GetTypeInfo()); + } + } + +#if NET9_0_OR_GREATER + [Fact] + public void CollectionWithRefStructElement_Serialization() + { + if (Serializer.IsSourceGeneratedSerializer) + { + Assert.Throws(() => Serializer.GetTypeInfo()); + } + else + { + // The reflection-based serializer throws. + Assert.Throws(() => Serializer.GetTypeInfo()); + } + } +#endif + private static object? GetDefaultValue(ParameterInfo parameterInfo) { Type parameterType = parameterInfo.ParameterType; @@ -551,6 +607,52 @@ public ClassWithRequiredAndOptionalConstructorParameters(string? x, string? y = public string? X { get; } public string? Y { get; } } + + public class ClassWithRefStructProperty + { + public ReadOnlySpan Value + { + get => _value.AsSpan(); + set => _value = value.ToString(); + } + + private string? _value; + } + + public class ClassWithRefStructConstructorParameter + { + public ClassWithRefStructConstructorParameter() + { + Value = "default"; + } + + [JsonConstructor] + public ClassWithRefStructConstructorParameter(ReadOnlySpan value) + { + Value = value.ToString(); + } + + public string Value { get; } + } + +#if NET9_0_OR_GREATER + public class CollectionWithRefStructElement : IEnumerable> + { + private List _values = new(); + public void Add(ReadOnlySpan value) => _values.Add(value.ToString()); + IEnumerator> IEnumerable>.GetEnumerator() => new SpanEnumerator(_values.GetEnumerator()); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + + private sealed class SpanEnumerator(IEnumerator inner) : IEnumerator> + { + public ReadOnlySpan Current => inner.Current.AsSpan(); + object IEnumerator.Current => throw new NotSupportedException(); + public void Dispose() => inner.Dispose(); + public bool MoveNext() => inner.MoveNext(); + public void Reset() => inner.Reset(); + } + } +#endif } internal class WeatherForecastWithPOCOs diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/MetadataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/MetadataTests.cs index f7030eb7c45e9..bdeaf59da2b78 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/MetadataTests.cs @@ -40,6 +40,11 @@ public partial class MetadataTests_SourceGen() : MetadataTests(new StringSeriali [JsonSerializable(typeof(DerivedClassWithShadowingProperties))] [JsonSerializable(typeof(IDerivedInterface))] [JsonSerializable(typeof(ClassWithRequiredAndOptionalConstructorParameters))] + [JsonSerializable(typeof(ClassWithRefStructProperty))] + [JsonSerializable(typeof(ClassWithRefStructConstructorParameter))] +#if NET9_0_OR_GREATER + [JsonSerializable(typeof(CollectionWithRefStructElement))] +#endif partial class Context : JsonSerializerContext; } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets index eede2cd52eeb0..7982d11db5ee0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets @@ -7,6 +7,7 @@ false + @@ -14,7 +15,8 @@ - $(NoWarn);SYSLIB0020;SYSLIB0049;SYSLIB1034;SYSLIB1037;SYSLIB1038;SYSLIB1039;SYSLIB1220;SYSLIB1222;SYSLIB1223 + + $(NoWarn);SYSLIB0020;SYSLIB0049;SYSLIB1030;SYSLIB1034;SYSLIB1037;SYSLIB1038;SYSLIB1039;SYSLIB1220;SYSLIB1222;SYSLIB1223;SYSLIB1225 true 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 5e5d83de2c09f..915c3fed31e8f 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 @@ -631,5 +631,129 @@ public class MyPoco { } CompilationHelper.AssertEqualDiagnosticMessages(expectedDiagnostics, result.Diagnostics); } + + [Fact] + public void RefStructPropertyWithoutJsonIgnore_CompilesWithWarning() + { + // Regression test for https://github.com/dotnet/runtime/issues/98590 + + string source = """ + using System; + using System.Text.Json.Serialization; + + public class MyPoco + { + public ReadOnlySpan Values => "abc".AsSpan(); + } + + [JsonSerializable(typeof(MyPoco))] + public partial class MyContext : JsonSerializerContext + { + } + """; + + Compilation compilation = CompilationHelper.CreateCompilation(source); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, disableDiagnosticValidation: true); + + Location propertyLocation = ((INamedTypeSymbol)compilation.GetSymbolsWithName("MyPoco").First()).GetMembers("Values").First().Locations[0]; + + var expectedDiagnostics = new DiagnosticData[] + { + new(DiagnosticSeverity.Warning, propertyLocation, "The type 'MyPoco' includes the ref like property, field or constructor parameter 'Values'. No source code will be generated for the property, field or constructor."), + }; + + CompilationHelper.AssertEqualDiagnosticMessages(expectedDiagnostics, result.Diagnostics); + } + + [Fact] + public void RefStructCtorParam_CompilesWithWarning() + { + // Regression test for https://github.com/dotnet/runtime/issues/98590 + + string source = """ + using System; + using System.Text.Json.Serialization; + + public class MyPoco + { + public MyPoco(ReadOnlySpan value) + { + Value = value.ToString(); + } + + public string Value { get; } + } + + [JsonSerializable(typeof(MyPoco))] + public partial class MyContext : JsonSerializerContext + { + } + """; + + Compilation compilation = CompilationHelper.CreateCompilation(source); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, disableDiagnosticValidation: true); + + ITypeSymbol type = (INamedTypeSymbol)compilation.GetSymbolsWithName("MyPoco").First(); + IMethodSymbol ctor = (IMethodSymbol)type.GetMembers(".ctor").First(); + IParameterSymbol param = ctor.Parameters.First(); + + var expectedDiagnostics = new DiagnosticData[] + { + new(DiagnosticSeverity.Warning, param.Locations.First(), "The type 'MyPoco' includes the ref like property, field or constructor parameter 'value'. No source code will be generated for the property, field or constructor."), + }; + + CompilationHelper.AssertEqualDiagnosticMessages(expectedDiagnostics, result.Diagnostics); + } + +#if NET9_0_OR_GREATER + [Fact] + public void CollectionWithRefStructElement_CompilesWithWarning() + { + // Regression test for https://github.com/dotnet/runtime/issues/98590 + + string source = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Text.Json.Serialization; + + public class CollectionWithRefStructElement : IEnumerable> + { + private List _values = new(); + public void Add(ReadOnlySpan value) => _values.Add(value.ToString()); + IEnumerator> IEnumerable>.GetEnumerator() => new SpanEnumerator(_values.GetEnumerator()); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + + private sealed class SpanEnumerator(IEnumerator inner) : IEnumerator> + { + public ReadOnlySpan Current => inner.Current.AsSpan(); + object IEnumerator.Current => throw new NotSupportedException(); + public void Dispose() => inner.Dispose(); + public bool MoveNext() => inner.MoveNext(); + public void Reset() => inner.Reset(); + } + } + + [JsonSerializable(typeof(CollectionWithRefStructElement))] + public partial class MyContext : JsonSerializerContext + { + } + """; + + CSharpParseOptions parseOptions = CompilationHelper.CreateParseOptions((LanguageVersion)1300); // C# 13 required for ref struct collection elements. + Compilation compilation = CompilationHelper.CreateCompilation(source, parseOptions: parseOptions); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, disableDiagnosticValidation: true); + + ISymbol contextSymbol = compilation.GetSymbolsWithName("MyContext").First(); + Collections.Immutable.ImmutableArray attributes = contextSymbol.GetAttributes(); + + var expectedDiagnostics = new DiagnosticData[] + { + new(DiagnosticSeverity.Warning, attributes[0].GetLocation(), "Did not generate serialization metadata for type 'CollectionWithRefStructElement'."), + }; + + CompilationHelper.AssertEqualDiagnosticMessages(expectedDiagnostics, result.Diagnostics); + } +#endif } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs index 99fc3d729a6cb..185881ccbcd16 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs @@ -858,5 +858,30 @@ internal partial class ModelContext : JsonSerializerContext Compilation compilation = CompilationHelper.CreateCompilation(source); CompilationHelper.RunJsonSourceGenerator(compilation); } + + [Fact] + public void RefStructPropertyWithJsonIgnore_CompilesSuccessfully() + { + // Regression test for https://github.com/dotnet/runtime/issues/98590 + + string source = """ + using System; + using System.Text.Json.Serialization; + + public class MyPoco + { + [JsonIgnore] + public ReadOnlySpan Values => "abc".AsSpan(); + } + + [JsonSerializable(typeof(MyPoco))] + public partial class MyContext : JsonSerializerContext + { + } + """; + + Compilation compilation = CompilationHelper.CreateCompilation(source); + CompilationHelper.RunJsonSourceGenerator(compilation); + } } }