From f9984c90e5a19dea6ee32daef2b73a8e08ebe232 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:42:35 +0000 Subject: [PATCH 1/4] Initial plan From cf00a1e0ff20e7b9dd059b79c13e3a7e3b30b275 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:49:16 +0000 Subject: [PATCH 2/4] Fix PR review comments: replace Enum.IsDefined with power-of-2 check and use JsonNode.DeepEquals in tests Co-authored-by: desjoerd <2460430+desjoerd@users.noreply.github.com> --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 4 +- .../Models/OpenApiSchemaTests.cs | 43 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index c3836d22f..45f94f011 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -884,7 +884,9 @@ private static (IList? effective, JsonSchemaType? inferredType, commonType |= schema.Type.GetValueOrDefault() & ~JsonSchemaType.Null; } - if (System.Enum.IsDefined(commonType)) + // Check if commonType is a single flag (power of 2) indicating all schemas share the same type + var isSingleType = commonType != 0 && (commonType & (commonType - 1)) == 0; + if (isSingleType) { // Single common type return (nonNullSchemas, commonType, true); diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index 7f4fc4e9a..d4eb0aeab 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -814,7 +814,7 @@ public async Task SerializeOneOfWithNullAsV3ShouldUseNullableAsync() schema.SerializeAsV3(writer); await writer.FlushAsync(); - var v3Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + var v3Schema = outputStringWriter.GetStringBuilder().ToString(); var expectedV3Schema = """ @@ -828,10 +828,10 @@ public async Task SerializeOneOfWithNullAsV3ShouldUseNullableAsync() ], "nullable": true } - """.MakeLineBreaksEnvironmentNeutral(); + """; // Assert - Assert.Equal(expectedV3Schema, v3Schema); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expectedV3Schema), JsonNode.Parse(v3Schema))); } [Fact] @@ -855,7 +855,7 @@ public async Task SerializeOneOfWithNullAndMultipleSchemasAsV3ShouldMarkItAsNull schema.SerializeAsV3(writer); await writer.FlushAsync(); - var v3Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + var v3Schema = outputStringWriter.GetStringBuilder().ToString(); var expectedV3Schema = """ @@ -870,10 +870,10 @@ public async Task SerializeOneOfWithNullAndMultipleSchemasAsV3ShouldMarkItAsNull ], "nullable": true } - """.MakeLineBreaksEnvironmentNeutral(); + """; // Assert - Assert.Equal(expectedV3Schema, v3Schema); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expectedV3Schema), JsonNode.Parse(v3Schema))); } [Fact] @@ -903,7 +903,7 @@ public async Task SerializeAnyOfWithNullAsV3ShouldUseNullableAsync() schema.SerializeAsV3(writer); await writer.FlushAsync(); - var v3Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + var v3Schema = outputStringWriter.GetStringBuilder().ToString(); var expectedV3Schema = """ @@ -921,8 +921,9 @@ public async Task SerializeAnyOfWithNullAsV3ShouldUseNullableAsync() ], "nullable": true } - """.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.Equal(expectedV3Schema, v3Schema); + """; + // Assert + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expectedV3Schema), JsonNode.Parse(v3Schema))); } [Fact] @@ -946,7 +947,7 @@ public async Task SerializeAnyOfWithNullAndMultipleSchemasAsV3ShouldApplyNullabl schema.SerializeAsV3(writer); await writer.FlushAsync(); - var v3Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + var v3Schema = outputStringWriter.GetStringBuilder().ToString(); var expectedV3Schema = """ @@ -962,10 +963,10 @@ public async Task SerializeAnyOfWithNullAndMultipleSchemasAsV3ShouldApplyNullabl ], "nullable": true } - """.MakeLineBreaksEnvironmentNeutral(); + """; // Assert - Assert.Equal(expectedV3Schema, v3Schema); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expectedV3Schema), JsonNode.Parse(v3Schema))); } [Fact] @@ -987,17 +988,17 @@ public async Task SerializeOneOfWithOnlyNullAsV3ShouldJustBeNullableAsync() schema.SerializeAsV3(writer); await writer.FlushAsync(); - var v3Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + var v3Schema = outputStringWriter.GetStringBuilder().ToString(); var expectedV3Schema = """ { "nullable": true } - """.MakeLineBreaksEnvironmentNeutral(); + """; // Assert - Assert.Equal(expectedV3Schema, v3Schema); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expectedV3Schema), JsonNode.Parse(v3Schema))); } [Fact] @@ -1020,7 +1021,7 @@ public async Task SerializeOneOfWithNullAsV31ShouldNotChangeAsync() schema.SerializeAsV31(writer); await writer.FlushAsync(); - var v31Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + var v31Schema = outputStringWriter.GetStringBuilder().ToString(); var expectedV31Schema = """ @@ -1034,10 +1035,10 @@ public async Task SerializeOneOfWithNullAsV31ShouldNotChangeAsync() } ] } - """.MakeLineBreaksEnvironmentNeutral(); + """; // Assert - Assert.Equal(expectedV31Schema, v31Schema); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expectedV31Schema), JsonNode.Parse(v31Schema))); } [Fact] @@ -1084,7 +1085,7 @@ public async Task SerializeOneOfWithNullAndRefAsV3ShouldUseNullableAsync() schema.SerializeAsV3(writer); await writer.FlushAsync(); - var v3Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); + var v3Schema = outputStringWriter.GetStringBuilder().ToString(); var expectedV3Schema = """ @@ -1097,10 +1098,10 @@ public async Task SerializeOneOfWithNullAndRefAsV3ShouldUseNullableAsync() ], "nullable": true } - """.MakeLineBreaksEnvironmentNeutral(); + """; // Assert - Assert.Equal(expectedV3Schema, v3Schema); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expectedV3Schema), JsonNode.Parse(v3Schema))); } internal class SchemaVisitor : OpenApiVisitorBase From fadc853da8aac29e9bd734b7398ec3561db44817 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:58:51 +0000 Subject: [PATCH 3/4] Rewrite type checking logic to avoid cryptic binary operators Co-authored-by: desjoerd <2460430+desjoerd@users.noreply.github.com> --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 45f94f011..9ba6079d0 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -877,22 +877,35 @@ private static (IList? effective, JsonSchemaType? inferredType, if (nonNullSchemas.Count > 0) { - JsonSchemaType commonType = 0; + // Check if all schemas have the same type + JsonSchemaType? firstType = null; + bool allSameType = true; foreach (var schema in nonNullSchemas) { - commonType |= schema.Type.GetValueOrDefault() & ~JsonSchemaType.Null; + var schemaType = schema.Type; + if (schemaType.HasValue && schemaType.Value != JsonSchemaType.Null) + { + if (firstType == null) + { + firstType = schemaType.Value; + } + else if (firstType != schemaType.Value) + { + allSameType = false; + break; + } + } } - // Check if commonType is a single flag (power of 2) indicating all schemas share the same type - var isSingleType = commonType != 0 && (commonType & (commonType - 1)) == 0; - if (isSingleType) + if (allSameType && firstType.HasValue) { - // Single common type - return (nonNullSchemas, commonType, true); + // All schemas share the same type + return (nonNullSchemas, firstType.Value, true); } else { + // Multiple different types return (nonNullSchemas, null, true); } From 3f2402f28698bc9b983803706544d1bc6351d89c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:37:37 +0000 Subject: [PATCH 4/4] Use bitwise operator to remove Null flag from type comparison Co-authored-by: desjoerd <2460430+desjoerd@users.noreply.github.com> --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 9ba6079d0..36f10bd1d 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -877,23 +877,29 @@ private static (IList? effective, JsonSchemaType? inferredType, if (nonNullSchemas.Count > 0) { - // Check if all schemas have the same type + // Check if all schemas have the same type (excluding null) JsonSchemaType? firstType = null; bool allSameType = true; foreach (var schema in nonNullSchemas) { var schemaType = schema.Type; - if (schemaType.HasValue && schemaType.Value != JsonSchemaType.Null) + if (schemaType.HasValue) { - if (firstType == null) + // Remove null from the type using bitwise operator + var typeWithoutNull = schemaType.Value & ~JsonSchemaType.Null; + + if (typeWithoutNull != 0) { - firstType = schemaType.Value; - } - else if (firstType != schemaType.Value) - { - allSameType = false; - break; + if (firstType == null) + { + firstType = typeWithoutNull; + } + else if (firstType != typeWithoutNull) + { + allSameType = false; + break; + } } } }