Avoid null ref in JsonSeparatorNamingPolicy (#85002)
authorLayomi Akinrinade <laakinri@microsoft.com>
Fri, 21 Apr 2023 21:26:56 +0000 (14:26 -0700)
committerGitHub <noreply@github.com>
Fri, 21 Apr 2023 21:26:56 +0000 (14:26 -0700)
* Avoid null ref in JsonSeparatorNamingPolicy

* Implement desired null-handling behavior

* Use throw helper for exception

* Create shared throw helper and fix CI issues

src/libraries/System.Text.Json/Common/JsonSeparatorNamingPolicy.cs
src/libraries/System.Text.Json/Common/ThrowHelper.cs [new file with mode: 0644]
src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets
src/libraries/System.Text.Json/src/System.Text.Json.csproj
src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PropertyNameTests.cs

index 9e54127c65ad95544e17eb97bfaced3ec51a9267..77eb1d8af32c3161462481a05f5e762b258fa3b1 100644 (file)
@@ -16,6 +16,11 @@ namespace System.Text.Json
 
         public sealed override string ConvertName(string name)
         {
+            if (name is null)
+            {
+                ThrowHelper.ThrowArgumentNullException(nameof(name));
+            }
+
             // Rented buffer 20% longer that the input.
             int rentedBufferLength = (12 * name.Length) / 10;
             char[]? rentedBuffer = rentedBufferLength > JsonConstants.StackallocCharThreshold
diff --git a/src/libraries/System.Text.Json/Common/ThrowHelper.cs b/src/libraries/System.Text.Json/Common/ThrowHelper.cs
new file mode 100644 (file)
index 0000000..3f381ce
--- /dev/null
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Text.Json
+{
+    internal static partial class ThrowHelper
+    {
+        [DoesNotReturn]
+        public static void ThrowArgumentNullException(string parameterName)
+        {
+            throw new ArgumentNullException(parameterName);
+        }
+    }
+}
index 6c977db11d6bbece27616151b0aaf99021b451e2..42094702dcc3ad974574b58135ce751410521ae6 100644 (file)
@@ -43,6 +43,7 @@
     <Compile Include="..\Common\JsonSourceGenerationOptionsAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationOptionsAttribute.cs" />
     <Compile Include="..\Common\ReflectionExtensions.cs" Link="Common\System\Text\Json\Serialization\ReflectionExtensions.cs" />
     <Compile Include="..\Common\JsonUnmappedMemberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonUnmappedMemberHandling.cs" />
+    <Compile Include="..\Common\ThrowHelper.cs" Link="Common\System\Text\Json\ThrowHelper.cs" />
     <Compile Include="$(CommonPath)\Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
     <Compile Include="ClassType.cs" />
     <Compile Include="CollectionType.cs" />
index 261abe0130e7940813f8ddb0bf8b77da74dcf019..682fa0aba8fc8c24d43ab4595a27b6f0b3722e93 100644 (file)
@@ -43,6 +43,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
     <Compile Include="..\Common\JsonSourceGenerationMode.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationMode.cs" />
     <Compile Include="..\Common\JsonSourceGenerationOptionsAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationOptionsAttribute.cs" />
     <Compile Include="..\Common\ReflectionExtensions.cs" Link="Common\System\Text\Json\Serialization\ReflectionExtensions.cs" />
+    <Compile Include="..\Common\ThrowHelper.cs" Link="Common\System\Text\Json\ThrowHelper.cs" />
     <Compile Include="System\Text\Json\AppContextSwitchHelper.cs" />
     <Compile Include="System\Text\Json\BitStack.cs" />
     <Compile Include="System\Text\Json\Document\JsonDocument.cs" />
index f1b20d3edd79da76ce0a6cda8f7e27c646a00a3a..7537f720abf11c161ffdef03865a998203719409 100644 (file)
@@ -4,7 +4,6 @@
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
-using System.Text.Json.Serialization.Metadata;
 
 namespace System.Text.Json
 {
@@ -13,12 +12,6 @@ namespace System.Text.Json
         // If the exception source is this value, the serializer will re-throw as JsonException.
         public const string ExceptionSourceValueToRethrowAsJsonException = "System.Text.Json.Rethrowable";
 
-        [DoesNotReturn]
-        public static void ThrowArgumentNullException(string parameterName)
-        {
-            throw new ArgumentNullException(parameterName);
-        }
-
         [DoesNotReturn]
         public static void ThrowArgumentOutOfRangeException_MaxDepthMustBePositive(string parameterName)
         {
index 6da381d144dab7c528602dc0f73524a4137dd140..09fb2c312875780a67897354ce10af77dd8a7f00 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Collections.Generic;
 using System.Threading.Tasks;
 using Xunit;
 
@@ -34,5 +35,34 @@ namespace System.Text.Json.Serialization.Tests
                 await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.SerializeWrapper(new IntPropertyNamesDifferentByCaseOnly_TestClass(), options));
             }
         }
+
+        [Fact]
+        public void CamelCasePolicyToleratesNullOrEmpty()
+        {
+            Assert.Null(JsonNamingPolicy.CamelCase.ConvertName(null));
+            Assert.Equal(string.Empty, JsonNamingPolicy.CamelCase.ConvertName(string.Empty));
+        }
+
+        [Theory]
+        [MemberData(nameof(JsonSeparatorNamingPolicyInstances))]
+        public void InboxSeparatorNamingPolicies_ThrowsOnNullInput(JsonNamingPolicy policy)
+        {
+            Assert.Throws<ArgumentNullException>(() => policy.ConvertName(null));
+        }
+
+        [Theory]
+        [MemberData(nameof(JsonSeparatorNamingPolicyInstances))]
+        public void InboxSeparatorNamingPolicies_EmptyInput(JsonNamingPolicy policy)
+        {
+            Assert.Equal(string.Empty, policy.ConvertName(string.Empty));
+        }
+
+        public static IEnumerable<object[]> JsonSeparatorNamingPolicyInstances()
+        {
+            yield return new object[] { JsonNamingPolicy.SnakeCaseLower };
+            yield return new object[] { JsonNamingPolicy.SnakeCaseUpper };
+            yield return new object[] { JsonNamingPolicy.KebabCaseLower };
+            yield return new object[] { JsonNamingPolicy.KebabCaseUpper };
+        }
     }
 }