Skip to content

Support TCGC exact() client name in C# emitter#10728

Draft
Copilot wants to merge 10 commits into
mainfrom
copilot/support-exact-client-name
Draft

Support TCGC exact() client name in C# emitter#10728
Copilot wants to merge 10 commits into
mainfrom
copilot/support-exact-client-name

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 19, 2026

TCGC 0.68.0 added exact(), which marks a @clientName value to be preserved verbatim by emitters. This change plumbs the new isExactName flag through the C# emitter and generator so that marked names bypass .ToIdentifierName() casing.

model Book {
  @clientName(Azure.ClientGenerator.Core.exact("snake_case_name"), "csharp")
  name: string;
}
// Generated C#: public string snake_case_name { get; set; }

Changes

  • TypeScript emitter — Added optional isExactName?: boolean to InputModelType, InputEnumType, InputLiteralType, InputUnionType, and InputPropertyTypeBase. Populated from the corresponding SDK types in type-converter.ts, operation-converter.ts, and client-converter.ts.
  • Generator.Input — Added IsExactName to the InputType and InputProperty base classes and extended the JSON converters (model, enum, literal, union, model property, and method/header/query/path/body/endpoint parameter) to deserialize the field.
  • Generator providersModelProvider.BuildName, EnumProvider.BuildName, and PropertyProvider skip .ToIdentifierName() when IsExactName is true.
  • CanonicalTypeProvider fix_specPropertiesMap now keys spec properties by their exact name when IsExactName is true, so [CodeGenMember("snake_case_name")] lookup still matches when the spec property name is preserved exactly. This ensures custom code rename wins over IsExactName.
  • Scope note — TCGC 0.68.0 only carries isExactName on type interfaces (SdkModelType, SdkEnumType, SdkConstantType, SdkUnionType, SdkNullableType, SdkClientInitializationType) and on SdkModelPropertyTypeBase. SdkServiceMethodBase and SdkClientType do not carry the flag, so operations and clients themselves are not in scope; method parameters are covered because SdkMethodParameter extends SdkModelPropertyTypeBase.
  • Sample-TypeSpec demonstration — Added @clientName(Azure.ClientGenerator.Core.exact("exact_name_property"), "csharp") to RoundTripModel.exactNameProperty in Sample-TypeSpec.tsp so the generated RoundTripModel.cs exposes public string exact_name_property { get; set; } verbatim and the serializer writes the wire name via WritePropertyName("exact_name_property"u8).
  • Regenerated test libraries — All tspCodeModel.json test fixtures (Sample-TypeSpec + Spector projects) were regenerated to include the new isExactName field (and, after merging main, the new serializationOptions field).
  • Tests
    • New PropertyProviderTests cases for exact-name model and property.
    • Emitter model-type.test.ts cases verifying propagation from exact() on every target TCGC supports (model, model property, enum, union, method parameter, query parameter, header parameter, path parameter, body parameter), plus a negative case.
    • Generator test CustomCodeWinsOverIsExactName verifying that a [CodeGenMember] custom property still wins over an IsExactName-marked spec property (generated property is filtered out, custom name is used, spec serialized name is preserved on the custom property). Uses the realistic .NET-idiomatic access_token / AccessToken OAuth scenario.
    • Serialization coverage: IsExactNamePropertySerializationUsesExactName and IsExactNameModelSerializationUsesExactName validate the generated JsonModelWriteCore and Deserialize<ModelName> bodies via full-body Helpers.GetExpectedFromFile() comparisons against checked-in TestData files (IsExactNamePropertySerializationUsesExactName(serialize).cs, IsExactNamePropertySerializationUsesExactName(deserialize).cs, IsExactNameModelSerializationUsesExactName.cs).
    • Method-parameter coverage: ExactNameMethodParameterPreservedInRestClient asserts both convenience and protocol method parameters preserve the exact name and that the rest client Create*Request method references the exact wire name when building the request URI.
    • Extended InputFactory.MethodParameter and InputFactory.QueryParameter test helpers with an isExactName parameter.

Copilot AI linked an issue May 19, 2026 that may be closed by this pull request
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/b465cdf8-5db5-43ea-97f0-f615be72ddc1

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp label May 19, 2026
Copilot AI changed the title [WIP] Add support for exact client name in emitter Support TCGC exact() client name in C# emitter May 19, 2026
Copilot AI requested a review from jorgerangel-msft May 19, 2026 15:46
Copy link
Copy Markdown
Contributor

@jorgerangel-msft jorgerangel-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot we need to ensure we cover all targets specified in https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/decorators/#@Azure.ClientGenerator.Core.clientName and add tests in both the generator and in the emitter. Also, we need to add test cases for when there is also a custom type that attempts to rename the member. In this scenario, the custom code should still win even when exactName is specified

Comment thread packages/http-client-csharp/emitter/src/lib/client-converter.ts
@jorgerangel-msft
Copy link
Copy Markdown
Contributor

@copilot we need to also regenerate all the test libraries and ensure all existing tests are passing.

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/d49e2964-d8d7-4d25-a10c-3ed3b2380518

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot we need to ensure we cover all targets specified in https://azure.github.io/typespec-azure/docs/libraries/typespec-client-generator-core/reference/decorators/#@Azure.ClientGenerator.Core.clie...

Added in 43ccb46:

  • Emitter tests covering all targets TCGC propagates isExactName to: model, model property, enum, union, plus method, query, header, path, and body parameters.
  • Generator test CustomCodeWinsOverIsExactName verifying that [CodeGenMember("snake_case_name")] still wins (generated property filtered out, custom name used, spec serialized name preserved).
  • Also fixed CanonicalTypeProvider._specPropertiesMap to respect IsExactName when keying spec properties, so [CodeGenMember] lookup matches the preserved exact name.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • azure.github.io
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/e393b22c-6092-402e-b27d-2042b1f51026

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot we need to also regenerate all the test libraries and ensure all existing tests are passing.

Regenerated all test libraries (74 tspCodeModel.json files updated with the new isExactName field) in a846ba9. Full generator test suite passes — 1493 + 1334 + 114 + 37 + 15 Spector tests — and all 221 emitter tests pass.

Copy link
Copy Markdown
Contributor

@jorgerangel-msft jorgerangel-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot lets also add test coverage for all members and their related serialization. For model's and their properties, we should validate that the serialization is constructed with the exact name. Same for operations and their parameters. We need to be thorough here.

Comment thread packages/http-client-csharp/emitter/src/lib/client-converter.ts
… test value to access_token

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/9283803c-c529-4be6-aede-8fe1a7e8b509

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot lets also add test coverage for all members and their related serialization. For model's and their properties, we should validate that the serialization is constructed with the exact name. Sa...

Added serialization and method-parameter coverage in 870e9b8:

  • IsExactNamePropertySerializationUsesExactName: asserts the generated JsonModelWriteCore writes the JSON property via the wire name and references the C# property by its exact name (no PascalCase variant present), and the Deserialize method does the same.
  • IsExactNameModelSerializationUsesExactName: asserts the generated Deserialize<ModelName> method preserves the exact model name verbatim.
  • ExactNameMethodParameterPreservedInRestClient: asserts both convenience and protocol method parameter names are preserved verbatim, and the rest client Create*Request method body uses the exact wire name when building the request URI.

@jorgerangel-msft
Copy link
Copy Markdown
Contributor

@copilot lets pull the latest from main and resolve the merge conflicts. Once they are resolved, we should regenerate all the test projects.

…-client-name

# Conflicts:
#	packages/http-client-csharp/emitter/src/lib/operation-converter.ts
#	packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputBodyParameterConverter.cs
#	packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/documentation/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/encode/array/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/encode/bytes/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/encode/datetime/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/encode/duration/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/encode/numeric/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/parameters/basic/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/parameters/body-optionality/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/parameters/spread/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/payload/json-merge-patch/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/payload/media-type/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/payload/multipart/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/payload/pageable/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/payload/xml/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/serialization/encoded-name/json/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/special-words/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/array/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/dictionary/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/enum/extensible/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/enum/fixed/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/empty/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/inheritance/enum-discriminator/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/inheritance/nested-discriminator/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/inheritance/not-discriminated/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/inheritance/recursive/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/inheritance/single-discriminator/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/usage/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/model/visibility/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/property/additional-properties/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/property/nullable/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/property/optionality/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/property/value-types/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/scalar/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/type/union/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/added/v1/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/added/v2/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/madeOptional/v1/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/madeOptional/v2/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/removed/v1/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/removed/v2/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/removed/v2Preview/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/renamedFrom/v1/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/renamedFrom/v2/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/returnTypeChangedFrom/v1/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/returnTypeChangedFrom/v2/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/typeChangedFrom/v1/tspCodeModel.json
#	packages/http-client-csharp/generator/TestProjects/Spector/http/versioning/typeChangedFrom/v2/tspCodeModel.json

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/4fcb1063-c7b2-4ae0-bdda-fd4ba28c24ec

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot lets pull the latest from main and resolve the merge conflicts. Once they are resolved, we should regenerate all the test projects.

Done in 5080471. Merged origin/main and resolved the conflicts in operation-converter.ts and InputBodyParameterConverter.cs (both kept isExactName and the new serializationOptions field from main). Regenerated all test projects — the 48 tspCodeModel.json fixtures now include both fields. All generator tests pass (1493 + 1337 + 126 + 37 + 15) and emitter tests pass (223).

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 21, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@10728

commit: 81e306e

Copy link
Copy Markdown
Contributor

@jorgerangel-msft jorgerangel-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot AI and others added 2 commits May 21, 2026 19:50
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/415f88ad-5491-45f7-b9b0-45eff15ff8e6

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
…egenerate

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/415f88ad-5491-45f7-b9b0-45eff15ff8e6

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot lets update one of the model properties in https://github.com/microsoft/typespec/tree/main/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec to use exactName

Done in 997509a. Added @clientName(Azure.ClientGenerator.Core.exact("exact_name_property"), "csharp") to RoundTripModel.exactNameProperty in Sample-TypeSpec.tsp and regenerated. The generated RoundTripModel.cs exposes the C# property verbatim as public string exact_name_property { get; set; }, and the serializer writes writer.WritePropertyName("exact_name_property"u8) / writer.WriteStringValue(exact_name_property). (I initially put the property on ModelWithClientName, but that model is used as a TypeSpec spread (op projectedNameModel(...ModelWithClientName)) and adding a property tripped the existing ScmMethodProviderCollection.GetSpreadConversion constructor-arity lookup; RoundTripModel has no such constraint.)

@jorgerangel-msft
Copy link
Copy Markdown
Contributor

@copilot spell check is failing

…sesExactName test

Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/6657becf-b44b-42e2-b623-0ed820861ef2

Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot spell check is failing

Fixed in 81e306e. The Consistency / Spell check job flagged Deserializesnake in Assert.AreEqual("Deserializesnake_case_model", ...) (the verbatim concatenation Deserialize + the exact model name snake_case_model). Added a // cspell:ignore Deserializesnake directive above that line; npx cspell now reports 0 issues for the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Exact Client Name

2 participants