Skip to content

fix(http-server-csharp): prevent _2 suffix on generated class/interface names from library namespace collisions - #11496

Draft
abatishchev with Copilot wants to merge 1 commit into
mainfrom
copilot/fix-generated-classes-naming
Draft

fix(http-server-csharp): prevent _2 suffix on generated class/interface names from library namespace collisions#11496
abatishchev with Copilot wants to merge 1 commit into
mainfrom
copilot/fix-generated-classes-naming

Conversation

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

When a library namespace (e.g. Azure.ResourceManager) defines interfaces or models with the same name as those in the user's @service namespace, the emitter was collecting both, causing alloy-js to rename the second occurrence with a _2 suffix — producing invalid C# where the class name and constructor name no longer match:

// Before fix — class and constructor names mismatch
public partial class OperationsController_2 : ControllerBase
{
    public OperationsController(IOperations_2 operations) { ... }
}

public interface IOperations_2 { }

public partial class ErrorResponse_2 : HttpServiceException
{
    public ErrorResponse(ErrorDetail error = default) { ... }
}

Root cause

getServiceInterfaces and getServiceModels traversed all non-standard namespaces in the global namespace tree, including library namespaces like Azure.ResourceManager. ARM RP specs define concrete Operations interfaces and common types (e.g. ErrorResponse) in those namespaces; when the same name appeared in the user's service namespace, both were emitted and the second got _2 appended to its class declaration.

Changes

  • service-discovery.tsgetServiceInterfaces: Now calls listServices(program) and only collects interfaces from @service-decorated namespaces. Falls back to the previous all-namespace scan when no @service decorator is present (e.g. unit tests).

  • service-resolution.tsgetServiceModels: Same scoping applied to the initial namespace model collection and the discoverReferencedModels starting points. Models transitively referenced by service operations (regardless of which namespace they live in) continue to be discovered and emitted via discoverModelsInType.

  • test/service-discovery.test.ts: New regression tests covering both the interface/controller case and the model case.

@azure-pipelines

Copy link
Copy Markdown
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.

Copilot AI linked an issue Jul 30, 2026 that may be closed by this pull request
4 tasks
Copilot AI changed the title [WIP] Fix generated classes with incorrect naming convention fix(http-server-csharp): prevent _2 suffix on generated class/interface names from library namespace collisions Jul 30, 2026
Copilot AI requested a review from abatishchev July 30, 2026 18:38
@pkg-pr-new

pkg-pr-new Bot commented Jul 30, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-server-csharp@11496

commit: 29baac6

…cting collection to @service namespaces

When a library namespace (e.g. Azure.ResourceManager) defines interfaces or
models with the same name as those in the user's @service namespace, the
emitter previously collected both, causing alloy-js to rename the second
occurrence with a _2 suffix (e.g. OperationsController_2, IOperations_2,
ErrorResponse_2).

Fix getServiceInterfaces (service-discovery.ts) and getServiceModels
(service-resolution.ts) to restrict their initial collection to
@service-decorated namespaces via listServices(program). A fallback to the
old all-namespace behaviour is kept for TypeSpec programs without @service.

Models transitively referenced by service operations (in any namespace) are
still discovered and emitted through discoverReferencedModels/discoverModelsInType.

Closes #11493

Co-authored-by: abatishchev <351644+abatishchev@users.noreply.github.com>
@abatishchev
abatishchev force-pushed the copilot/fix-generated-classes-naming branch from dfa6c41 to 29baac6 Compare July 31, 2026 17:11
@github-actions

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @typespec/http-server-csharp
Show changes

@typespec/http-server-csharp - fix ✏️

Fix generated class/interface names incorrectly receiving a _2 suffix (e.g. OperationsController_2, IOperations_2, ErrorResponse_2) when a non-service library namespace (such as Azure.ResourceManager) defines types with the same name as types in the @service namespace. The emitter now restricts its collection of interfaces and models to @service-decorated namespaces, preventing the naming collision.

@timotheeguerin timotheeguerin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Need to tweak a little the test and actual goal of the PR(comments are targetted for copilot to apply if they sound a little harsh)

// got a `_2` suffix, causing a class/constructor name mismatch in the generated C#.
it("does not emit _2 suffix when a non-service namespace has an interface with the same name", async () => {
const spec = `
// Simulate a library namespace (e.g. Azure.ResourceManager) that has its own

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Simplify this test, the azure mention are irrelevant, here we just want to test that we don't include unreachable types that are not in the service namespace
Do not include extra info keep the bare minimum needed in this test

const [, controllerContents] = controllerFile!;
const [, interfaceContents] = interfaceFile!;

assert.ok(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

those expect feel very speciifc to this instance and could easily not be testing the actual problem if the dedup logic change.
We should maybe simplify that to just check the whole set of generated models and make sure there is no unexpected one

collectModelsFromNamespace($, ns, models, seen, authModels);
}
const services = listServices(program);
if (services.length > 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lets reformulate this as we do not want to include type that are not defined in the service namespace unless they are referenced.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: http-server-csharp: Some generated classes have wrong name, include _2

3 participants