Support TCGC `exact()` client name in C# emitter (#10728)
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.
```tsp
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 providers** — `ModelProvider.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 `List`→`Get` 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.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: copilot <copilot@github.com>
Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> C
Copilot committed
3a90abdc3d4e6c94d321bdc11025764bea05e2da
Parent: 9a52313
Committed by GitHub <noreply@github.com>
on 5/28/2026, 3:27:24 PM