RequestDelegateGenerator: [AsParameters] emits unqualified type names — CS0234 when user namespace collides
Is there an existing issue for this?
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.
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:
- 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::.
- 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
-
In a Minimal API project with EnableRequestDelegateGenerator=true, declare:
namespace Http;
public record TenantArgs(HttpContext HttpContext, [FromRoute] int TenantId, MyBody Payload);
public record MyBody(string Value);
-
Map:
app.MapPost("/t/{TenantId}", ([AsParameters] Http.TenantArgs args) => args.TenantId);
-
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).
RequestDelegateGenerator:
[AsParameters]emits unqualified type names — CS0234 when user namespace collidesIs there an existing issue for this?
Describe the bug
The minimal-API request delegate source generator emits unqualified type names in the
PropertyAsParameterInfometadata it builds for[AsParameters]containers. The generated file is wrapped innamespace 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 withCS0234.A small repro: an
[AsParameters]container that lives in a top-level namespace calledHttp(orRouting,Builder,Mvc,Json,Features,Metadata,Extensions,AspNetCore, …) will produce a generator output that references e.g.inside
namespace Microsoft.AspNetCore.Http.Generated. C# walks enclosing namespaces first, findsHttpas the child ofMicrosoft.AspNetCore(=Microsoft.AspNetCore.Http), looks forMyContainerthere, and reports CS0234.Same story for the constructor lookup helpers in
SymbolExtensions.GetParameterInfoFromConstructorCode(used byPropertyAsParameterInfo), 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:46src/Shared/RoslynUtils/SymbolExtensions.cs:299src/Shared/RoslynUtils/SymbolExtensions.cs:300All three call
ITypeSymbol.ToDisplayString()with noSymbolDisplayFormat, which defaults to a short form that does not includeglobal::. By contrast, other emission sites in the generator already useSymbolDisplayFormat.FullyQualifiedFormat(or the project-localEmitterConstants.DisplayFormat, which setsglobalNamespaceStyle: Included) and therefore correctly emitglobal::Http.MyType.Why this is rarely hit today
Two conditions must coincide:
typeof(...)metadata. Only[AsParameters]triggers that path — direct body / route / query / header binding for the same user type goes throughEmitterConstants.DisplayFormat, which already includesglobal::.Microsoft.AspNetCore.Http.Generated. The repository's own[AsParameters]test containers all live inMicrosoft.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.csfiles 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 threeToDisplayString()calls above toToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)is enough; the existingVerifyAsParametersBaseline.generated.txtbaseline needs to be regenerated to pick upglobal::prefixes on the affectedtypeof(...)expressions.Steps to reproduce
In a Minimal API project with
EnableRequestDelegateGenerator=true, declare:Map:
Build. Generated
GeneratedRouteBuilderExtensions.g.csfails with:Workaround
Rename the user namespace so its first segment does not collide with any namespace reachable from
Microsoft.AspNetCore.Http.Generated(e.g.MyApp.Httpinstead ofHttp).