Skip to content

CodeWriter field dedup does not update references (AsValueExpression) after renaming declaration #10130

@ArcturusZhang

Description

@ArcturusZhang

Bug Report

Description

When CodeWriter deduplicates field identifiers (e.g., appending 0 to avoid name collisions), the deduplication only applies to the declaration but not to references made through FieldProvider.AsValueExpression. This causes the generated code to have mismatched field names between declarations and references.

Reproduction

When a TypeProvider defines two fields with the same name (e.g., one from a base class for an API parameter named scope, and one added by the derived class for internal use), the CodeWriter's dedup mechanism:

  1. Correctly renames the declaration: private readonly string _scope0;
  2. Incorrectly keeps the original name in references: _scope = scopeValue; and CreateScope(_scope) — instead of using _scope0

Root Cause

CodeWriterDeclaration tracks dedup correctly via RequestedName_actualNames (per-scope). However, FieldProvider.AsValueExpression returns a MemberExpression whose MemberName is a plain string set at construction time. When this expression is written, it goes through WriteIdentifier(string) which writes the string as-is — it does not consult the CodeWriterDeclaration's actual name.

So:

  • Declaration path: WriteDeclaration(CodeWriterDeclaration) → dedup works ✅
  • Reference path: MemberExpression.WriteWriteIdentifier(MemberName) → uses original name ❌

Impact

This affects any generator that adds fields to a TypeProvider subclass where the field name could collide with names from base fields (e.g., fields derived from API parameters). The generated code will:

  • Assign to the wrong field in constructors
  • Reference the wrong field in method bodies
  • Potentially compile with errors or silent bugs

Workaround

We are currently working around this in the Azure generator (Azure/azure-sdk-for-net#57337) by detecting collisions in BuildFields() and calling FieldProvider.Update(name: uniqueName) to rename our field before the CodeWriter sees it. This ensures no collision reaches the CodeWriter in the first place.

Environment

  • Microsoft.TypeSpec.Generator version: 1.0.0-alpha.20260320.2
  • Found in: Azure.Generator (AzureCollectionResultDefinition)

Metadata

Metadata

Labels

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

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions