dotnet / dotnet/aspnetcore

RequestDelegateGenerator: [AsParameters] emits unqualified type names — CS0234 when user namespace collides

Open
#66,950 1 comment 0 reactions 1 assignee Claimed by @DeagleGross View on GitHub
area-minimal feature-rdg
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Describe the bug

The minimal-API request delegate source generator emits unqualified type names in the `PropertyAsParameterInfo` metadata it builds for `[AsParameters]` containers. The generated file is wrapped in `namespace Microsoft.AspNetCore.Http.Generated { ... }`, so any user type whose top-level namespace **shadows a name on the C# resolution walk-up path** resolves to the wrong namespace and the generated file fails to compile with `CS0234`.

A small repro: an `[AsParameters]` container that lives in a top-level namespace called `Http` (or `Routing`, `Builder`, `Mvc`, `Json`, `Features`, `Metadata`, `Extensions`, `AspNetCore`, …) will produce a generator output that references e.g.

```csharp
typeof(Http.MyContainer)!.GetProperty("…")
```

inside `namespace Microsoft.AspNetCore.Http.Generated`. C# walks enclosing namespaces first, finds `Http` as the child of `Microsoft.AspNetCore` (= `Microsoft.AspNetCore.Http`), looks for `MyContainer` there, and reports CS0234.

Same story for the constructor lookup helpers in `SymbolExtensions.GetParameterInfoFromConstructorCode` (used by `PropertyAsParameterInfo`), which emit both the container type and each constructor parameter type unqualified.

### Affected call sites

- `src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/EndpointParameter.cs:46`
- `src/Shared/RoslynUtils/SymbolExtensions.cs:299`
- `src/Shared/RoslynUtils/SymbolExtensions.cs:300`

All three call `ITypeSymbol.ToDisplayString()` with no `SymbolDisplayFormat`, which defaults to a short form that does not include `global::`. By contrast, other emission sites in the generator already use `SymbolDisplayFormat.FullyQualifiedFormat` (or the project-local `EmitterConstants.DisplayFormat`, which sets `globalNamespaceStyle: Included`) and therefore correctly emit `global::Http.MyType`.

### Why this is rarely hit today

Two conditions must coincide:

1. The code path emits via the buggy `typeof(...)` metadata. Only `[AsParameters]` triggers that path — direct body / route / query / header binding for the same user type goes through `EmitterConstants.DisplayFormat`, which already includes `global::`.
2. The user type's namespace first segment shadows a namespace visible during the resolution walk-up inside `Microsoft.AspNetCore.Http.Generated`. The repository's own `[AsParameters]` test containers all live in `Microsoft.AspNetCore.Http.Generators.Tests`, which never collides, so the latent bug never surfaced in CI.

Real applications that organize types under short top-level namespaces (e.g. `Models`, `Http.Dtos`, `Mvc.Inputs`, `Routing.Configuration`, `Json.Models`) will trip CS0234 in generated `.g.cs` files with diagnostics that point at lines they cannot edit.

### Expected behavior

The generator should always emit fully-qualified type references (with `global::`) in the metadata it constructs. Switching the three `ToDisplayString()` calls above to `ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)` is enough; the existing `VerifyAsParametersBaseline.generated.txt` baseline needs to be regenerated to pick up `global::` prefixes on the affected `typeof(...)` expressions.

### Steps to reproduce

1. In a Minimal API project with `EnableRequestDelegateGenerator=true`, declare:

```csharp
namespace Http;

public record TenantArgs(HttpContext HttpContext, [FromRoute] int TenantId, MyBody Payload);
public record MyBody(string Value);
```

2. Map:

```csharp
app.MapPost("/t/{TenantId}", ([AsParameters] Http.TenantArgs args) => args.TenantId);
```

3. Build. Generated `GeneratedRouteBuilderExtensions.g.cs` fails with:

> error CS0234: The type or namespace name 'TenantArgs' does not exist in the namespace 'Microsoft.AspNetCore.Http'

### Workaround

Rename the user namespace so its first segment does not collide with any namespace reachable from `Microsoft.AspNetCore.Http.Generated` (e.g. `MyApp.Http` instead of `Http`).

Related https://github.com/dotnet/aspnetcore/issues/58633

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.