Generate AOT-compatible C# primitive responses - #11931
Jorge Rangel (jorgerangel-msft) with Copilot wants to merge 49 commits into
Conversation
|
Azure Pipelines: Successfully started running 1 pipeline(s). 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
…ection Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
commit: |
There was a problem hiding this comment.
🟢 Approval recommended
The implementation covers the intended response types and includes focused generation and runtime tests.
Pull request overview
Updates the C# client generator to deserialize primitive and enum responses without reflection, enabling Native AOT compatibility while preserving BOM and nullable handling.
Changes:
- Uses typed
JsonElementreads for primitive and enum responses. - Preserves plain-text behavior and constant-format
TimeSpanconversion. - Adds generation baselines and runtime BOM coverage for sync/async enum responses.
File summaries
| File | Description |
|---|---|
TestProjects/Local/Sample-TypeSpec/src/Generated/SampleTypeSpecClient.cs |
Updates generated enum deserialization. |
TestProjects/Local.Tests/ExtensibleEnumTests.cs |
Tests sync/async BOM handling. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpanNullable).cs |
Adds nullable TimeSpan baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(TimeSpan).cs |
Updates TimeSpan baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(String).cs |
Updates string baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Single).cs |
Updates float baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int64).cs |
Updates long baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32NullableAsync).cs |
Adds async nullable integer baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32Nullable).cs |
Adds nullable integer baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32Async).cs |
Adds async integer baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Int32).cs |
Updates integer baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Double).cs |
Updates double baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Decimal).cs |
Adds decimal baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(DateTimeOffset).cs |
Updates date-time baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(BooleanNullable).cs |
Adds nullable boolean baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/ScalarReturnTypeMethods(Boolean).cs |
Updates boolean baseline. |
test/Providers/TestData/ScmMethodProviderCollectionTests/PlainTextReturnTypeMethods.cs |
Verifies plain-text handling. |
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(True,True,True).cs |
Covers nullable extensible string enum. |
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(True,True,False).cs |
Covers extensible string enum. |
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(True,False,False).cs |
Covers fixed string enum. |
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(False,True,False).cs |
Covers extensible integer enum. |
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(False,False,True).cs |
Covers nullable fixed integer enum. |
test/Providers/TestData/ScmMethodProviderCollectionTests/EnumReturnTypeMethods(False,False,False).cs |
Covers fixed integer enum. |
test/Providers/ScmMethodProviderCollectionTests.cs |
Expands scalar and enum generation tests. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewRequiredParameterDoesNotAddBackCompatOverload.cs |
Refreshes generated baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalParameterWithReservedName.cs |
Refreshes generated baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalNonBodyParameterAddedWithPathAndHeaderParameters.cs |
Refreshes generated baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalNonBodyParameterAddedWithModelBody.cs |
Refreshes generated baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalNonBodyParameterAdded.cs |
Refreshes generated baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_NewOptionalBodyParameterDoesNotAddBackCompatOverload.cs |
Refreshes generated baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_MultipleNewOptionalNonBodyParametersAdded.cs |
Refreshes generated baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_ConvenienceMethodParamOrderChanged(Sync).cs |
Adds sync body baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_ConvenienceMethodParamOrderChanged(Async).cs |
Adds async body baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_BothMethodsParamOrderChanged(Sync).cs |
Adds sync compatibility baseline. |
test/Providers/ClientProviders/TestData/ClientProviderTests/BackCompatibility_BothMethodsParamOrderChanged(Async).cs |
Adds async compatibility baseline. |
test/Providers/ClientProviders/ClientProviderTests.cs |
Moves body assertions to baseline files. |
src/Providers/ScmMethodProviderCollection.cs |
Implements typed response deserialization. |
Review details
- Files reviewed: 36/37 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Jorge Rangel (jorgerangel-msft)
left a comment
There was a problem hiding this comment.
Copilot there's spelling errors we should address by simply changing the test to avoid the warning / error :
cspell --no-progress .
packages/http-client-csharp/generator/TestProjects/Local.Tests/ExtensibleEnumTests.cs:25:62 - Unknown word (FEFF)
There was a problem hiding this comment.
🟢 Approval recommended
The implementation is scoped, preserves documented compatibility behavior, and has representative generation and runtime coverage.
Review details
- Files reviewed: 38/39 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Fixed in 5c0d5d6 by adding a |
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
BOM-prefixed JSON still fails parsing, and custom duration encodings can now crash generation.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 85/87 changed files
- Comments generated: 2
- Review effort level: Balanced
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
BOM handling misses some primitive types, and several generated and baseline outputs remain stale.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
packages/http-client-csharp/generator/TestProjects/Local.Tests/ExtensibleEnumTests.cs:48
- This BOM test currently calls the checked-in
SampleTypeSpecClient, whose changedGetInt32Valuemethods still useJsonDocument.Parse(stream)(SampleTypeSpecClient.cs:1147-1160) and therefore reject this BOM-prefixed payload. Regenerate the Local sample after the newTrimStart('\uFEFF')generator path so the test exercises—and passes with—the implementation under review.
- Files reviewed: 86/89 changed files
- Comments generated: 2
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
Valid custom duration encodings still cause a generation-time exception instead of a diagnostic and fallback.
Review details
Suppressed comments (1)
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:988
- Custom
DurationKnownEncodingvalues are valid inputs (Microsoft.TypeSpec.Generator.Input/test/DurationKnownEncodingTests.cs:34-39), so this still crashes generation for a supported code model. Follow the existingunsupported-serializationwarning-and-fallback pattern inMrwSerializationTypeDefinition.cs:2064-2071and update the test that currently expectsInvalidOperationException.
_ => content.As<string>().ParseTimeSpan(Literal(format.ToFormatSpecifier() ?? throw new InvalidOperationException($"Unsupported duration serialization format: {format}")))
- Files reviewed: 86/89 changed files
- Comments generated: 0 new
- Review effort level: Balanced
…g on unsupported duration encodings Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
|
Jorge Rangel (@jorgerangel-msft) Unfortunately I hit an unexpected error while processing your comment. I've automatically reported this to GitHub. You can ask me to try again later by mentioning me in a new comment. If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: Sorry for the inconvenience! |
There was a problem hiding this comment.
🟡 Changes recommended
Some JSON primitive types still bypass BOM-safe parsing, and the new diagnostic is not fully asserted.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:305
- JSON
Uri,byte, andsbyteresponses still take this fallback becauseTypeFactory.cs:157-167produces those CLR types butIsConvertibleFromBinaryDataexcludes them. The fallback at lines 310-311 parsesBinaryDatawithout the BOM stripping used at lines 650-657, so a BOM-prefixed response still throws; route these primitive types through the same BOM-safe document setup and cover one in the runtime matrix.
IsConvertibleFromBinaryData(responseBodyType)
|| (IsPlainTextParsableType(responseBodyType) && HasOnlyPlainTextContentType())
- Files reviewed: 86/89 changed files
- Comments generated: 1
- Review effort level: Balanced
…n-BinaryData-convertible types Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The unsupported-duration fallback test does not assert the newly required diagnostic code or severity.
Review details
Suppressed comments (1)
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs:1793
- This fallback test only compares generated code, so it still passes if the newly required
unsupported-serializationdiagnostic is removed or its code/severity regresses. Capture the emitter diagnostic and assert it alongside the fallback baseline so the new diagnostic path is actually covered.
// An unsupported/custom duration encoding has no known format specifier, so generation reports a
// diagnostic and falls back to the constant ("c") format instead of throwing.
Assert.AreEqual(Helpers.GetExpectedFromFile("Constant", method: "PlainTextDurationReturnTypeMethods"), writer.ToString(false));
- Files reviewed: 86/89 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Supported numeric duration encodings currently emit a spurious unsupported-serialization diagnostic.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 89/92 changed files
- Comments generated: 1
- Review effort level: Balanced
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Plain-text media types are matched too strictly, and the primary JSON primitive path lacks runtime coverage.
Get a fresh assessment by requesting another Copilot review.
Review details
- Files reviewed: 87/88 changed files
- Comments generated: 2
- Review effort level: Balanced
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Mixed JSON/plain-text responses can select JSON parsing even when the server legally returns raw text.
Review details
Suppressed comments (1)
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:1061
- Mixed
application/json/text/plainresponses are always sent down the JSON path, butRestClientProvider.cs:1794-1808advertises every declared media type, so a server may legally return raw text; a string or enum body such asMondaywill then reachJsonDocument.Parseand throw. Choose the conversion from the actual responseContent-Type(or request only one representation), and cover the mixed case with a non-JSON-compatible string/enum value.
private bool HasOnlyPlainTextContentType()
{
var contentTypes = ServiceMethod.Operation.Responses
.Where(r => r.IsErrorResponse is false)
.SelectMany(r => r.ContentTypes);
return contentTypes.Any() && contentTypes.All(contentType => contentType.Contains("text/plain", StringComparison.OrdinalIgnoreCase));
- Files reviewed: 87/88 changed files
- Comments generated: 0 new
- Review effort level: Balanced
JoshLove-msft
left a comment
There was a problem hiding this comment.
Re-reviewed the updates through 9b2f4e2. The earlier duration wire-encoding finding is addressed, and nullable plain-text null handling now accepts surrounding whitespace. No new actionable findings; existing review threads and explicitly deferred items were not duplicated.
--generated by Copilot
There was a problem hiding this comment.
🟡 Changes recommended
Media-type detection can incorrectly route JSON responses through raw-text parsing.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:654
- The PR’s documented generated shape parses
Content.ToStream(), but this instead transcodes every JSON response to a UTF-16 string before parsing.JsonDocument.Parse(Stream)accepts a leading UTF-8 BOM directly, so generate and dispose a content stream here (and in the fallback path) to avoid the extra full-body allocation.
- Files reviewed: 87/88 changed files
- Comments generated: 1
- Review effort level: Balanced
|
Copilot Please remove the BOM-specific handling ( Remove the BOM-specific test cases and comments while retaining the normal primitive, enum, nullable, and sync/async coverage. Update the affected baselines and regenerate the test libraries. Please do not reintroduce BOM handling in response to earlier review comments; this is an intentional decision. --generated by Copilot |
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
String transcoding weakens malformed UTF-8 validation in both new JSON parsing paths.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:310
- This fallback also transcodes the raw bytes through a replacement-fallback UTF-8 string before JSON parsing, which can silently accept malformed response bytes. Use the stream overload so BOM handling does not weaken JSON's UTF-8 validation.
This issue also appears on line 650 of the same file.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:654
- Decoding the response through
BinaryData.ToString()replaces malformed UTF-8 with U+FFFD, so an invalid JSON string can now deserialize successfully instead of raising a JSON error. ParseContent.ToStream()directly instead;JsonDocument.Parse(Stream)accepts a leading UTF-8 BOM while preserving strict UTF-8 validation.
var contentExpression = data.InvokeToString().Invoke(nameof(string.TrimStart), Literal('\uFEFF')).As<string>();
var statements = new MethodBodyStatement[]
{
Declare("responseContent", typeof(string), contentExpression, out var responseContent),
UsingDeclare("document", JsonDocumentSnippets.Parse(responseContent), out var document)
- Files reviewed: 87/88 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Addressed in 9a84ed9. JSON response conversion now parses |
There was a problem hiding this comment.
🟡 Changes recommended
Both JSON and plain-text conversion paths currently break the stated UTF-8 BOM compatibility requirement.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs:647
- Parsing
BinaryDatadirectly selects theReadOnlyMemory<byte>JSON overload, which explicitly rejects a UTF-8 BOM, so BOM-prefixed primitive responses fail despite the PR's compatibility requirement. Generate a stream and parse that overload instead, and cover this with a BOM regression test.
UsingDeclare("document", data.Parse(), out var document)
- Files reviewed: 87/88 changed files
- Comments generated: 1
- Review effort level: Balanced
C# convenience methods deserialize primitive responses with
BinaryData.ToObjectFromJson<T>(), which relies on reflection and is not Native AOT-compatible.JsonElementreads for primitive and enum responses, including sync and async methods.TimeSpanformatting. Leave plain-text, raw binary, model, and collection handling unchanged.Generated
int32response conversion: