Skip to content

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

Open
Copilot wants to merge 18 commits into
mainfrom
copilot/support-exact-client-name
Open

Support TCGC exact() client name in C# emitter#10728
Copilot wants to merge 18 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. TCGC 0.68.2 extended the flag to enum values and to SdkClientType / SdkServiceMethodBase. 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, InputEnumValueType, InputLiteralType, InputUnionType, InputPropertyTypeBase, InputClient, InputServiceMethodBase, and InputOperation. Populated from the corresponding SDK types in type-converter.ts, operation-converter.ts, and client-converter.ts (including createEnumValueType for SdkEnumValueType / SdkConstantType). In client-converter.ts, the multi-service …Client suffix is also skipped when isExactName is true so the verbatim name is preserved.
  • Generator.Input — Added IsExactName to the InputType and InputProperty base classes, to InputClient, InputServiceMethod, and InputOperation, and extended the JSON converters (model, enum, enum value, literal, union, model property, method/header/query/path/body/endpoint parameter, client, basic/paging/LRO/LRO-paging service method, and operation) to deserialize the field.
  • Generator providersModelProvider.BuildName, EnumProvider.BuildName, PropertyProvider, FixedEnumProvider.BuildFields, ExtensibleEnumProvider.BuildEnumValues, and ClientProvider.BuildName skip .ToIdentifierName() when IsExactName is true. ClientProvider.GetCleanOperationName returns the operation name verbatim — skipping both casing and the ListGet convention rename — when the service method IsExactName is true. For extensible enums, the exact-name value is preserved verbatim on both the private …Value field and the public property. ParameterProvider.GetVariableExpression skips .ToVariableName() camelCasing when the underlying InputParameter.IsExactName is true so the generated C# parameter (in convenience methods, protocol methods, and rest client Create*Request signatures) uses the exact spec name (e.g. string api_key instead of string apiKey).
  • 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.2 carries isExactName on type interfaces (SdkModelType, SdkEnumType, SdkEnumValueType, SdkConstantType, SdkUnionType, SdkNullableType, SdkClientInitializationType), on SdkModelPropertyTypeBase, on SdkClientType, and on SdkServiceMethodBase. All of these targets are now 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 on types, properties, enum values, clients, methods, and operations (and, after merging main, the new serializationOptions field).
  • Tests
    • New PropertyProviderTests cases for exact-name model and property.
    • New EnumProviderTests cases for IsExactName-marked values on fixed string-based, fixed int-based, and extensible string-based enums.
    • Emitter model-type.test.ts cases verifying propagation from exact() on every target TCGC supports (model, model property, enum, enum value, union, method parameter, query parameter, header parameter, path parameter, body parameter, client, and operation), plus a negative case.
    • New generator ClientProviderTests cases — client name preserved verbatim, regression check that a non-exact client name still goes through PascalCase, exact-name operation preserved verbatim, exact-name List operation is NOT renamed to GetAll, and regression check that a non-exact List still becomes GetAll.
    • 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 verbatim (including the C# parameter identifier, not just the wire name) and that the rest client Create*Request method references the exact wire name when building the request URI.
    • Extended InputFactory.MethodParameter, InputFactory.QueryParameter, InputFactory.EnumMember.{String,Int32,Float32,Float64}, InputFactory.Operation, InputFactory.BasicServiceMethod, and InputFactory.Client test helpers with an isExactName parameter; promoted InputFactory.Enum from private to public for direct construction of enums with hand-built value lists.

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>
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 there are ci checks failing. We need to regenerate all the test libraries and ensure all existing tests, including spector tests, are passing

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

@copilot resolve merge conflicts

…-client-name

# Conflicts:
#	packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeValueConverter.cs
#	packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs

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

Copilot AI commented May 28, 2026

@copilot resolve merge conflicts

Merged origin/main in ed81d7b. Conflicts resolved in two files:

  • InputEnumTypeValueConverter.cs — kept main's expanded enum primitive type kind handling (Int8/Int16/Int32/UInt8/UInt16 → Int32, Int64/UInt32/UInt64/SafeInt → Int64, Float/Float32/Float64/Numeric/Decimal/Decimal128 → Float) and reapplied IsExactName = isExactName on each branch.
  • InputFactory.cs — kept main's new EnumMember.Int64 helper and extended it with the isExactName parameter to match the other helpers on this branch.

Full generator suite passes (1512) and ClientModel suite passes (1343).

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

4 participants