[release/6.0] Emit diagnostics & exceptions for sourcegen handling init-only properti...
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Wed, 15 Sep 2021 22:03:50 +0000 (15:03 -0700)
committerGitHub <noreply@github.com>
Wed, 15 Sep 2021 22:03:50 +0000 (15:03 -0700)
* Emit diagnostic warnings and exceptions when sourcegen is handling init-only inaccessible JsonInclude properties.

* add localizations for runtime exception messages

* remove exception messages from resource strings

* fix alphabetical ordering

Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com>
25 files changed:
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.ExceptionMessages.cs [new file with mode: 0644]
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
src/libraries/System.Text.Json/gen/PropertyGenerationSpec.cs
src/libraries/System.Text.Json/gen/Resources/Strings.resx
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets
src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs
src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs

diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.ExceptionMessages.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.ExceptionMessages.cs
new file mode 100644 (file)
index 0000000..faa8529
--- /dev/null
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Text.Json.SourceGeneration
+{
+    public sealed partial class JsonSourceGenerator
+    {
+        private sealed partial class Emitter
+        {
+            /// <summary>
+            /// Unlike sourcegen warnings, exception messages should not be localized so we keep them in source.
+            /// </summary>
+            private static class ExceptionMessages
+            {
+                public const string InaccessibleJsonIncludePropertiesNotSupported =
+                    "The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.";
+
+                public const string IncompatibleConverterType =
+                    "The converter '{0}' is not compatible with the type '{1}'.";
+
+                public const string InitOnlyPropertyDeserializationNotSupported =
+                    "Deserialization of init-only properties is currently not supported in source generation mode.";
+
+                public const string InvalidJsonConverterFactoryOutput =
+                    "The converter '{0}' cannot return null or a JsonConverterFactory instance.";
+
+                public const string InvalidSerializablePropertyConfiguration =
+                    "Invalid serializable-property configuration specified for type '{0}'. For more information, see 'JsonSourceGenerationMode.Serialization'.";
+            };
+        }
+    }
+}
index 14bfda6..131e08d 100644 (file)
@@ -324,7 +324,7 @@ namespace {@namespace}
                             }}
                             else
                             {{
-                                throw new {InvalidOperationExceptionTypeRef}($""The converter '{{converter.GetType()}}' is not compatible with the type '{{typeToConvert}}'."");
+                                throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.IncompatibleConverterType}"", converter.GetType(), typeToConvert));
                             }}
                         }}");
                 }
@@ -333,7 +333,7 @@ namespace {@namespace}
                     metadataInitSource.Append($@"
                         if (!converter.CanConvert(typeToConvert))
                         {{
-                            throw new {InvalidOperationExceptionTypeRef}($""The converter '{{converter.GetType()}}' is not compatible with the type '{{typeToConvert}}'."");
+                            throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.IncompatibleConverterType}"", converter.GetType(), typeToConvert));
                         }}");
                 }
 
@@ -711,25 +711,28 @@ private static {JsonPropertyInfoTypeRef}[] {propInitMethodName}({JsonSerializerC
                         ? @$"jsonPropertyName: ""{memberMetadata.JsonPropertyName}"""
                         : "jsonPropertyName: null";
 
-                    string getterNamedArg = memberMetadata.CanUseGetter &&
-                        memberMetadata.DefaultIgnoreCondition != JsonIgnoreCondition.Always
-                        ? $"getter: static (obj) => (({declaringTypeCompilableName})obj).{clrPropertyName}"
-                        : "getter: null";
-
-                    string setterNamedArg;
-                    if (memberMetadata.CanUseSetter &&
-                        memberMetadata.DefaultIgnoreCondition != JsonIgnoreCondition.Always)
+                    string getterNamedArg = memberMetadata switch
                     {
-                        string propMutation = typeGenerationSpec.IsValueType
-                            ? @$"{UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value!"
-                            : $@"(({declaringTypeCompilableName})obj).{clrPropertyName} = value!";
-
-                        setterNamedArg = $"setter: static (obj, value) => {propMutation}";
-                    }
-                    else
+                        { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "getter: null",
+                        { CanUseGetter: true } => $"getter: static (obj) => (({declaringTypeCompilableName})obj).{clrPropertyName}",
+                        { CanUseGetter: false, HasJsonInclude: true }
+                            => @$"getter: static (obj) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")",
+                        _ => "getter: null"
+                    };
+
+                    string setterNamedArg = memberMetadata switch
                     {
-                        setterNamedArg = "setter: null";
-                    }
+                        { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "setter: null",
+                        { CanUseSetter: true, IsInitOnlySetter: true }
+                            => @$"setter: static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{ExceptionMessages.InitOnlyPropertyDeserializationNotSupported}"")",
+                        { CanUseSetter: true } when typeGenerationSpec.IsValueType
+                            => $@"setter: static (obj, value) => {UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value!",
+                        { CanUseSetter: true }
+                            => @$"setter: static (obj, value) => (({declaringTypeCompilableName})obj).{clrPropertyName} = value!",
+                        { CanUseSetter: false, HasJsonInclude: true }
+                            => @$"setter: static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")",
+                        _ => "setter: null",
+                    };
 
                     JsonIgnoreCondition? ignoreCondition = memberMetadata.DefaultIgnoreCondition;
                     string ignoreConditionNamedArg = ignoreCondition.HasValue
@@ -821,12 +824,12 @@ private static {JsonParameterInfoValuesTypeRef}[] {typeGenerationSpec.TypeInfoPr
                     out Dictionary<string, PropertyGenerationSpec>? serializableProperties,
                     out bool castingRequiredForProps))
                 {
-                    string exceptionMessage = @$"""Invalid serializable-property configuration specified for type '{typeRef}'. For more information, see 'JsonSourceGenerationMode.Serialization'.""";
+                    string exceptionMessage = string.Format(ExceptionMessages.InvalidSerializablePropertyConfiguration, typeRef);
 
                     return GenerateFastPathFuncForType(
                         serializeMethodName,
                         typeRef,
-                        $@"throw new {InvalidOperationExceptionTypeRef}({exceptionMessage});",
+                        $@"throw new {InvalidOperationExceptionTypeRef}(""{exceptionMessage}"");",
                         canBeNull: false); // Skip null check since we want to throw an exception straightaway.
                 }
 
@@ -1202,7 +1205,7 @@ private static {JsonSerializerOptionsTypeRef} {DefaultOptionsStaticVarName} {{ g
                 converter = factory.CreateConverter(type, {OptionsInstanceVariableName});
                 if (converter == null || converter is {JsonConverterFactoryTypeRef})
                 {{
-                    throw new {InvalidOperationExceptionTypeRef}($""The converter '{{factory.GetType()}}' cannot return null or a JsonConverterFactory instance."");
+                    throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.InvalidJsonConverterFactoryOutput}"", factory.GetType()));
                 }}
             }}
 
@@ -1233,7 +1236,7 @@ private {JsonConverterTypeRef} {GetConverterFromFactoryMethodName}({TypeTypeRef}
     {JsonConverterTypeRef}? converter = factory.CreateConverter(type, {Emitter.OptionsInstanceVariableName});
     if (converter == null || converter is {JsonConverterFactoryTypeRef})
     {{
-        throw new {InvalidOperationExceptionTypeRef}($""The converter '{{factory.GetType()}}' cannot return null or a JsonConverterFactory instance."");
+        throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.InvalidJsonConverterFactoryOutput}"", factory.GetType()));
     }}
      
     return converter;
index 63ab0e6..9ba0372 100644 (file)
@@ -139,6 +139,22 @@ namespace System.Text.Json.SourceGeneration
                 defaultSeverity: DiagnosticSeverity.Error,
                 isEnabledByDefault: true);
 
+            private static DiagnosticDescriptor InitOnlyPropertyDeserializationNotSupported { get; } = new DiagnosticDescriptor(
+                id: "SYSLIB1037",
+                title: new LocalizableResourceString(nameof(SR.InitOnlyPropertyDeserializationNotSupportedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+                messageFormat: new LocalizableResourceString(nameof(SR.InitOnlyPropertyDeserializationNotSupportedFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+                category: JsonConstants.SystemTextJsonSourceGenerationName,
+                defaultSeverity: DiagnosticSeverity.Warning,
+                isEnabledByDefault: true);
+
+            private static DiagnosticDescriptor InaccessibleJsonIncludePropertiesNotSupported { get; } = new DiagnosticDescriptor(
+                id: "SYSLIB1038",
+                title: new LocalizableResourceString(nameof(SR.InaccessibleJsonIncludePropertiesNotSupportedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+                messageFormat: new LocalizableResourceString(nameof(SR.InaccessibleJsonIncludePropertiesNotSupportedFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+                category: JsonConstants.SystemTextJsonSourceGenerationName,
+                defaultSeverity: DiagnosticSeverity.Warning,
+                isEnabledByDefault: true);
+
             public Parser(Compilation compilation, in JsonSourceGenerationContext sourceGenerationContext)
             {
                 _compilation = compilation;
@@ -624,6 +640,7 @@ namespace System.Text.Json.SourceGeneration
                 string? converterInstatiationLogic = null;
                 bool implementsIJsonOnSerialized = false;
                 bool implementsIJsonOnSerializing = false;
+                bool hasEncounteredInitOnlyProperties = false;
                 bool hasTypeFactoryConverter = false;
                 bool hasPropertyFactoryConverters = false;
 
@@ -954,6 +971,17 @@ namespace System.Text.Json.SourceGeneration
                                     dataExtensionPropGenSpec = GetOrAddTypeGenerationSpec(propType, generationMode);
                                     _implicitlyRegisteredTypes.Add(dataExtensionPropGenSpec);
                                 }
+
+                                if (!hasEncounteredInitOnlyProperties && spec.CanUseSetter && spec.IsInitOnlySetter)
+                                {
+                                    _sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(InitOnlyPropertyDeserializationNotSupported, Location.None, new string[] { type.Name }));
+                                    hasEncounteredInitOnlyProperties = true;
+                                }
+
+                                if (spec.HasJsonInclude && (!spec.CanUseGetter || !spec.CanUseSetter || !spec.IsPublic))
+                                {
+                                    _sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(InaccessibleJsonIncludePropertiesNotSupported, Location.None, new string[] { type.Name, spec.ClrName }));
+                                }
                             }
                         }
 
@@ -1079,7 +1107,8 @@ namespace System.Text.Json.SourceGeneration
                     out bool canUseGetter,
                     out bool canUseSetter,
                     out bool getterIsVirtual,
-                    out bool setterIsVirtual);
+                    out bool setterIsVirtual,
+                    out bool setterIsInitOnly);
 
                 string clrName = memberInfo.Name;
                 string runtimePropertyName = DetermineRuntimePropName(clrName, jsonPropertyName, _currentContextNamingPolicy);
@@ -1095,6 +1124,7 @@ namespace System.Text.Json.SourceGeneration
                     RuntimePropertyName = runtimePropertyName,
                     PropertyNameVarName = propertyNameVarName,
                     IsReadOnly = isReadOnly,
+                    IsInitOnlySetter = setterIsInitOnly,
                     CanUseGetter = canUseGetter,
                     CanUseSetter = canUseSetter,
                     GetterIsVirtual = getterIsVirtual,
@@ -1227,13 +1257,15 @@ namespace System.Text.Json.SourceGeneration
                 out bool canUseGetter,
                 out bool canUseSetter,
                 out bool getterIsVirtual,
-                out bool setterIsVirtual)
+                out bool setterIsVirtual,
+                out bool setterIsInitOnly)
             {
                 isPublic = false;
                 canUseGetter = false;
                 canUseSetter = false;
                 getterIsVirtual = false;
                 setterIsVirtual = false;
+                setterIsInitOnly = false;
 
                 switch (memberInfo)
                 {
@@ -1260,15 +1292,16 @@ namespace System.Text.Json.SourceGeneration
                             if (setMethod != null)
                             {
                                 isReadOnly = false;
+                                setterIsInitOnly = setMethod.IsInitOnly();
 
                                 if (setMethod.IsPublic)
                                 {
                                     isPublic = true;
-                                    canUseSetter = !setMethod.IsInitOnly();
+                                    canUseSetter = true;
                                 }
                                 else if (setMethod.IsAssembly)
                                 {
-                                    canUseSetter = hasJsonInclude && !setMethod.IsInitOnly();
+                                    canUseSetter = hasJsonInclude;
                                 }
 
                                 setterIsVirtual = setMethod.IsVirtual;
index 5040045..c9c4cbd 100644 (file)
@@ -16,6 +16,9 @@ namespace System.Text.Json.SourceGeneration
         /// </summary>
         public bool IsProperty { get; init; }
 
+        /// <summary>
+        /// If representing a property, returns true if either the getter or setter are public.
+        /// </summary>
         public bool IsPublic { get; init; }
 
         public bool IsVirtual { get; init; }
@@ -40,6 +43,11 @@ namespace System.Text.Json.SourceGeneration
         public bool IsReadOnly { get; init; }
 
         /// <summary>
+        /// Whether the property has an init-only set method.
+        /// </summary>
+        public bool IsInitOnlySetter { get; init; }
+
+        /// <summary>
         /// Whether the property has a public or internal (only usable when JsonIncludeAttribute is specified)
         /// getter that can be referenced in generated source code.
         /// </summary>
index 389b14e..1074f0e 100644 (file)
   <data name="DataExtensionPropertyInvalidTitle" xml:space="preserve">
     <value>Data extension property type invalid.</value>
   </data>
-</root>
\ No newline at end of file
+  <data name="InitOnlyPropertyDeserializationNotSupportedTitle" xml:space="preserve">
+    <value>Deserialization of init-only properties is currently not supported in source generation mode.</value>
+  </data>
+  <data name="InitOnlyPropertyDeserializationNotSupportedFormat" xml:space="preserve">
+    <value>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</value>
+  </data>
+  <data name="InaccessibleJsonIncludePropertiesNotSupportedTitle" xml:space="preserve">
+    <value>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</value>
+  </data>
+  <data name="InaccessibleJsonIncludePropertiesNotSupportedFormat" xml:space="preserve">
+    <value>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</value>
+  </data>
+</root>
index 710c988..3b613f3 100644 (file)
         <target state="translated">Duplicitní název typu</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">Typ {0} má více konstruktorů anotovaných s JsonConstructorAttribute. </target>
index e4c65b6..80ee5a4 100644 (file)
         <target state="translated">Doppelter Typname</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">Typ "{0}" weist mehrere Konstruktoren mit dem Kommentar "JsonConstructorAttribute" auf.</target>
index e97f578..383c9b6 100644 (file)
         <target state="translated">Nombre de tipo duplicado.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">El tipo '{0}' tiene varios constructores anotados con 'JsonConstructorAttribute'.</target>
index d4daa7c..abf9fec 100644 (file)
         <target state="translated">Nom de type dupliqué.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">Le type' {0} 'a plusieurs constructeurs annotés avec’JsonConstructorAttribute'.</target>
index b72f656..9edb25f 100644 (file)
         <target state="translated">Nome di tipo duplicato.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">Il tipo '{0}' contiene più costruttori che presentano l'annotazione 'JsonConstructorAttribute'.</target>
index 17f511d..b3312d6 100644 (file)
         <target state="translated">重複した種類名。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">型 '{0}' には、'JsonConstructorAttribute' で注釈が付けられた複数のコンストラクターがあります。</target>
index 1a45826..1214ae9 100644 (file)
         <target state="translated">중복된 형식 이름입니다.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">'{0}' 형식에 'JsonConstructorAttribute'로 주석이 추가된 여러 생성자가 있습니다.</target>
index e757ef4..257006c 100644 (file)
         <target state="translated">Zduplikowana nazwa typu.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">Typ "{0}" ma wiele konstruktorów z adnotacją "JsonConstructorAttribute".</target>
index 34765df..11492c2 100644 (file)
         <target state="translated">Nome de tipo duplicado.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">O tipo '{0}' tem vários construtores anotados com 'JsonConstructorAttribute'.</target>
index bdac6af..f1bb2a2 100644 (file)
         <target state="translated">Дублирующееся имя типа.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">Тип "{0}" имеет несколько конструкторов, аннотированных с использованием JsonConstructorAttribute.</target>
index e8eedfa..3d5e138 100644 (file)
         <target state="translated">Yinelenen tür adı.</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">'{0}' türünün 'JsonConstructorAttribute' ile açıklanan birden çok oluşturucusu var.</target>
index 12bee67..a934f5c 100644 (file)
         <target state="translated">重复的类型名称。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">类型“{0}”具有用 “JsonConstructorAttribute” 批注的多个构造函数。</target>
index 0f1c6dd..4e236ab 100644 (file)
         <target state="translated">重複類型名稱。</target>
         <note />
       </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+        <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+        <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+        <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+        <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+        <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+        <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+        <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+        <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+        <note />
+      </trans-unit>
       <trans-unit id="MultipleJsonConstructorAttributeFormat">
         <source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
         <target state="translated">類型 '{0}' 包含多個以 'JsonConstructorAttribute' 註解的建構函式。</target>
index 0153870..97d7815 100644 (file)
@@ -1,4 +1,4 @@
-<Project>
+<Project>
   <PropertyGroup>
     <TargetFrameworks>netstandard2.0</TargetFrameworks>
     <AssemblyName>$(MSBuildThisFileName)</AssemblyName>
@@ -43,6 +43,7 @@
     <Compile Include="CollectionType.cs" />
     <Compile Include="JsonConstants.cs" />
     <Compile Include="JsonSourceGenerator.Emitter.cs" />
+    <Compile Include="JsonSourceGenerator.Emitter.ExceptionMessages.cs" />
     <Compile Include="JsonSourceGenerator.Parser.cs" />
     <Compile Include="ObjectConstructionStrategy.cs" />
     <Compile Include="ParameterGenerationSpec.cs" />
index 819f952..02d51cd 100644 (file)
@@ -64,7 +64,7 @@ namespace System.Text.Json.SourceGeneration
         /// </summary>
         public string? RuntimeTypeRef { get; private set; }
 
-        public TypeGenerationSpec? ExtensionDataPropertyTypeSpec {  get; private set; }
+        public TypeGenerationSpec? ExtensionDataPropertyTypeSpec { get; private set; }
 
         public string? ConverterInstantiationLogic { get; private set; }
 
@@ -160,7 +160,6 @@ namespace System.Text.Json.SourceGeneration
             for (int i = 0; i < PropertyGenSpecList.Count; i++)
             {
                 PropertyGenerationSpec propGenSpec = PropertyGenSpecList[i];
-                bool hasJsonInclude = propGenSpec.HasJsonInclude;
                 JsonIgnoreCondition? ignoreCondition = propGenSpec.DefaultIgnoreCondition;
 
                 if (ignoreCondition == JsonIgnoreCondition.WhenWritingNull && !propGenSpec.TypeGenerationSpec.CanBeNull)
@@ -168,17 +167,21 @@ namespace System.Text.Json.SourceGeneration
                     goto ReturnFalse;
                 }
 
-                if (!propGenSpec.IsPublic)
+                // In case of JsonInclude fail if either:
+                // 1. the getter is not accessible by the source generator or
+                // 2. neither getter or setter methods are public.
+                if (propGenSpec.HasJsonInclude && (!propGenSpec.CanUseGetter || !propGenSpec.IsPublic))
                 {
-                    if (hasJsonInclude)
-                    {
-                        goto ReturnFalse;
-                    }
+                    goto ReturnFalse;
+                }
 
+                // Discard any getters not accessible by the source generator.
+                if (!propGenSpec.CanUseGetter)
+                {
                     continue;
                 }
 
-                if (!propGenSpec.IsProperty && !hasJsonInclude && !options.IncludeFields)
+                if (!propGenSpec.IsProperty && !propGenSpec.HasJsonInclude && !options.IncludeFields)
                 {
                     continue;
                 }
@@ -223,7 +226,7 @@ namespace System.Text.Json.SourceGeneration
             castingRequiredForProps = PropertyGenSpecList.Count > serializableProperties.Count;
             return true;
 
-ReturnFalse:
+        ReturnFalse:
             serializableProperties = null;
             castingRequiredForProps = false;
             return false;
index 0c8dc6a..af5159c 100644 (file)
@@ -216,31 +216,46 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
-        public virtual async Task HonorJsonPropertyName()
+        public virtual async Task HonorJsonPropertyName_PrivateGetter()
         {
-            string json = @"{""prop1"":1,""prop2"":2}";
+            string json = @"{""prop1"":1}";
 
-            var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName>(json);
-            Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum);
-            Assert.Equal(2, obj.MyInt);
+            var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateGetter>(json);
+            Assert.Equal(MySmallEnum.AnotherValue, obj.GetProxy());
 
             json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
             Assert.Contains(@"""prop1"":1", json);
+        }
+
+        [Fact]
+        public virtual async Task HonorJsonPropertyName_PrivateSetter()
+        {
+            string json = @"{""prop2"":2}";
+
+            var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateSetter>(json);
+            Assert.Equal(2, obj.MyInt);
+
+            json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
             Assert.Contains(@"""prop2"":2", json);
         }
 
-        public struct StructWithPropertiesWithJsonPropertyName
+        public struct StructWithPropertiesWithJsonPropertyName_PrivateGetter
         {
             [JsonInclude]
             [JsonPropertyName("prop1")]
             public MySmallEnum MyEnum { private get; set; }
 
+            // For test validation.
+            internal MySmallEnum GetProxy() => MyEnum;
+        }
+
+        public struct StructWithPropertiesWithJsonPropertyName_PrivateSetter
+        {
             [JsonInclude]
             [JsonPropertyName("prop2")]
             public int MyInt { get; private set; }
 
-            // For test validation.
-            internal MySmallEnum GetMyEnum => MyEnum;
+            internal void SetProxy(int myInt) => MyInt = myInt;
         }
 
         [Fact]
index e3cc69e..8b2243f 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Collections.Generic;
+using System.Reflection;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization.Tests;
 using System.Threading.Tasks;
@@ -46,17 +47,10 @@ namespace System.Text.Json.SourceGeneration.Tests
                 ""MyUri"":""https://microsoft.com""
             }";
 
-            var obj = await JsonSerializerWrapperForString.DeserializeWrapper<MyClass_WithNonPublicAccessors_WithPropertyAttributes>(json);
-            Assert.Equal(0, obj.MyInt); // Source gen can't use private setter
-            Assert.Equal("Hello", obj.MyString);
-            Assert.Equal(2f, obj.GetMyFloat);
-            Assert.Equal(new Uri("https://microsoft.com"), obj.MyUri);
-
-            json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
-            Assert.Contains(@"""MyInt"":0", json);
-            Assert.Contains(@"""MyString"":""Hello""", json);
-            Assert.DoesNotContain(@"""MyFloat"":", json); // Source gen can't use private setter
-            Assert.Contains(@"""MyUri"":""https://microsoft.com""", json);
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<MyClass_WithNonPublicAccessors_WithPropertyAttributes>(json));
+
+            var obj = new MyClass_WithNonPublicAccessors_WithPropertyAttributes();
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj));
         }
 
         [Theory]
@@ -64,12 +58,15 @@ namespace System.Text.Json.SourceGeneration.Tests
         [InlineData(typeof(StructWithInitOnlyProperty))]
         public override async Task InitOnlyProperties(Type type)
         {
-            // Init-only setters cannot be referenced as get/set helpers in generated code.
-            object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type);
-            Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj));
+            PropertyInfo property = type.GetProperty("MyInt");
 
             // Init-only properties can be serialized.
-            Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+            object obj = Activator.CreateInstance(type);
+            property.SetValue(obj, 1);
+            Assert.Equal(@"{""MyInt"":1}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+
+            // Deserializing init-only properties is not supported.
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type));
         }
 
         [Theory]
@@ -78,12 +75,15 @@ namespace System.Text.Json.SourceGeneration.Tests
         [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))]
         public override async Task NonPublicInitOnlySetter_With_JsonInclude(Type type)
         {
-            // Init-only setters cannot be referenced as get/set helpers in generated code.
-            object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type);
-            Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj));
+            PropertyInfo property = type.GetProperty("MyInt");
 
             // Init-only properties can be serialized.
-            Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+            object obj = Activator.CreateInstance(type);
+            property.SetValue(obj, 1);
+            Assert.Equal(@"{""MyInt"":1}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+
+            // Deserializing init-only properties is not supported.
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type));
         }
 
         [Fact]
@@ -94,15 +94,15 @@ namespace System.Text.Json.SourceGeneration.Tests
 
             string json = @"{""MyEnum"":""AnotherValue"",""MyInt"":2}";
 
-            // Deserialization baseline, without enum converter, we get JsonException.
+            // Deserialization baseline, without enum converter, we get JsonException. NB order of members in deserialized type is significant for this assertion to succeed.
             await Assert.ThrowsAsync<JsonException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithConverter>(json));
 
-            var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithConverter>(json, options);
-            Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum);
-            Assert.Equal(0, obj.MyInt); // Private setter can't be used with source-gen.
+            // JsonInclude not supported in source gen.
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithConverter>(json, options));
 
-            // ConverterForInt32 throws this exception.
-            await Assert.ThrowsAsync<NotImplementedException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options));
+            // JsonInclude on private getters not supported.
+            var obj = new StructWithPropertiesWithConverter();
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options));
         }
 
         [Fact]
@@ -116,25 +116,32 @@ namespace System.Text.Json.SourceGeneration.Tests
             Assert.Equal(3, obj.Y);
             Assert.Equal(4, obj.GetZ);
 
-            json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
-            Assert.Contains(@"""W"":1", json);
-            Assert.Contains(@"""X"":2", json);
-            Assert.Contains(@"""Y"":3", json);
-            Assert.DoesNotContain(@"""Z"":", json); // Private setter cannot be used with source gen.
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj));
+        }
+
+        [Fact]
+        public override async Task HonorJsonPropertyName_PrivateGetter()
+        {
+            string json = @"{""prop1"":1}";
+
+            var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateGetter>(json);
+            Assert.Equal(MySmallEnum.AnotherValue, obj.GetProxy());
+
+            // JsonInclude for private members not supported in source gen
+            await Assert.ThrowsAsync<InvalidOperationException>(async() => await JsonSerializerWrapperForString.SerializeWrapper(obj));
         }
 
         [Fact]
-        public override async Task HonorJsonPropertyName()
+        public override async Task HonorJsonPropertyName_PrivateSetter()
         {
-            string json = @"{""prop1"":1,""prop2"":2}";
+            string json = @"{""prop2"":2}";
 
-            var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName>(json);
-            Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum);
-            Assert.Equal(0, obj.MyInt); // Private setter cannot be used with source gen.
+            // JsonInclude for private members not supported in source gen
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateSetter>(json));
 
-            json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
-            Assert.DoesNotContain(@"""prop1"":", json); // Private getter cannot be used with source gen.
-            Assert.Contains(@"""prop2"":0", json);
+            var obj = new StructWithPropertiesWithJsonPropertyName_PrivateSetter();
+            obj.SetProxy(2);
+            Assert.Equal(json, await JsonSerializerWrapperForString.SerializeWrapper(obj));
         }
 
         [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
@@ -252,7 +259,8 @@ namespace System.Text.Json.SourceGeneration.Tests
         [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows))]
         [JsonSerializable(typeof(MyClass_WithNonPublicAccessors))]
         [JsonSerializable(typeof(ClassWithThingsToIgnore_PerProperty))]
-        [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName))]
+        [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateGetter))]
+        [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateSetter))]
         [JsonSerializable(typeof(ClassWithValueAndReferenceTypes))]
         [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))]
         internal sealed partial class PropertyVisibilityTestsContext_Metadata : JsonSerializerContext
@@ -417,7 +425,8 @@ namespace System.Text.Json.SourceGeneration.Tests
         [JsonSerializable(typeof(ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows))]
         [JsonSerializable(typeof(MyClass_WithNonPublicAccessors))]
         [JsonSerializable(typeof(ClassWithThingsToIgnore_PerProperty))]
-        [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName))]
+        [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateGetter))]
+        [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateSetter))]
         [JsonSerializable(typeof(ClassWithValueAndReferenceTypes))]
         [JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))]
         internal sealed partial class PropertyVisibilityTestsContext_Default : JsonSerializerContext
index cc60860..71681de 100644 (file)
@@ -4,7 +4,9 @@
     <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
     <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
     <!-- SYSLIB0020: JsonSerializerOptions.IgnoreNullValues is obsolete -->
-    <NoWarn>$(NoWarn);SYSLIB0020</NoWarn>
+    <!-- SYSLIB1037: Suppress init-only property deserialization warning -->
+    <!-- SYSLIB1038: Suppress JsonInclude on inaccessible members warning -->
+    <NoWarn>$(NoWarn);SYSLIB0020;SYSLIB1037;SYSLIB1038</NoWarn>
   </PropertyGroup>
 
   <PropertyGroup>
index c9457cd..b84c518 100644 (file)
@@ -275,11 +275,72 @@ namespace System.Text.Json.SourceGeneration.UnitTests
             return CreateCompilation(source);
         }
 
+        public static Compilation CreateCompilationWithInitOnlyProperties()
+        {
+            string source = @"
+            using System;
+            using System.Text.Json.Serialization;
+
+            namespace HelloWorld
+            {                
+                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
+                {
+                }
+            }";
+
+            return CreateCompilation(source);
+        }
+
+        public static Compilation CreateCompilationWithInaccessibleJsonIncludeProperties()
+        {
+            string source = @"
+            using System;
+            using System.Text.Json.Serialization;
+
+            namespace HelloWorld
+            {                
+                public class Location
+                {
+                    [JsonInclude]
+                    public int Id { get; private set; }
+                    [JsonInclude]
+                    public string Address1 { get; internal set; }
+                    [JsonInclude]
+                    private string Address2 { get; set; }
+                    [JsonInclude]
+                    public string PhoneNumber { internal get; set; }
+                    [JsonInclude]
+                    public string Country { private get; set; }
+                }
+
+                [JsonSerializable(typeof(Location))]
+                public partial class MyJsonContext : JsonSerializerContext
+                {
+                }
+            }";
+
+            return CreateCompilation(source);
+        }
+
         internal static void CheckDiagnosticMessages(ImmutableArray<Diagnostic> diagnostics, DiagnosticSeverity level, string[] expectedMessages)
         {
             string[] actualMessages = diagnostics.Where(diagnostic => diagnostic.Severity == level).Select(diagnostic => diagnostic.GetMessage()).ToArray();
 
-            // Can't depending on reflection order when generating type metadata.
+            // Can't depend on reflection order when generating type metadata.
             Array.Sort(actualMessages);
             Array.Sort(expectedMessages);
 
index caa283f..470c834 100644 (file)
@@ -190,5 +190,38 @@ public class Program
             CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Warning, Array.Empty<string>());
             CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Error, Array.Empty<string>());
         }
+
+        [Fact]
+        public void WarnOnClassesWithInitOnlyProperties()
+        {
+            Compilation compilation = CompilationHelper.CreateCompilationWithInitOnlyProperties();
+            JsonSourceGenerator generator = new JsonSourceGenerator();
+            CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator);
+
+            string[] expectedWarningDiagnostics = new string[] { "The type 'Location' defines init-only properties, deserialization of which is currently not supported in source generation mode." };
+
+            CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Info, Array.Empty<string>());
+            CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Warning, expectedWarningDiagnostics);
+            CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Error, Array.Empty<string>());
+        }
+
+        [Fact]
+        public void WarnOnClassesWithInaccessibleJsonIncludeProperties()
+        {
+            Compilation compilation = CompilationHelper.CreateCompilationWithInaccessibleJsonIncludeProperties();
+            JsonSourceGenerator generator = new JsonSourceGenerator();
+            CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator);
+
+            string[] expectedWarningDiagnostics = new string[]
+            {
+                "The member 'Location.Id' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.",
+                "The member 'Location.Address2' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.",
+                "The member 'Location.Country' has been annotated with the JsonIncludeAttribute but is not visible to the source generator."
+            };
+
+            CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Info, Array.Empty<string>());
+            CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Warning, expectedWarningDiagnostics);
+            CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Error, Array.Empty<string>());
+        }
     }
 }