Skip to content

[csharp] Generated back-compat overloads in ModelFactory can have non-compiling bodies after large signature changes #10595

@ArcturusZhang

Description

@ArcturusZhang

Bug

When generating C# back-compat overloads in *ModelFactory based on the previously-shipped contract, the generator can emit overloads whose body does not compile — the return expression forwards to the primary overload using arguments that are inconsistent with that primary overload's current parameter list (wrong types, missing/renamed parameters, named-then-positional ordering, etc.).

The signatures themselves are fine; only the bodies are wrong.

Encountered while migrating Azure.ResourceManager.Compute (Swagger → TypeSpec). After regeneration we observed 96 raw / 48 unique compilation errors across 13 distinct factory methods, all in EditorBrowsable(Never) back-compat overloads:

C# error Count Example
CS1739 (parameter name doesn't exist) 78 'RestorePointData' has no parameter 'instantAccessDurationMinutes'
CS1503 (cannot convert) 12 cannot convert 'GalleryImageIdentifier' to 'SharedGalleryIdentifier'
CS1744 (positional after named) 6 back-compat call passes reservationType: default then a positional arg

100% of the broken methods carry [EditorBrowsable(EditorBrowsableState.Never)] (i.e. they are auto-generated back-compat shims, not user-authored code).

Reproducer

Azure.ResourceManager.Compute migration branch (will share repo + branch on request).

Representative case — CapacityReservationGroupData:

Primary overload (current generated, valid):

public static CapacityReservationGroupData CapacityReservationGroupData(
    ResourceIdentifier id = default, string name = default, ResourceType resourceType = default, SystemData systemData = default,
    IDictionary<string, string> tags = default, AzureLocation location = default,
    IEnumerable<string> zones = default,
    IEnumerable<SubResource> capacityReservations = default,
    IEnumerable<SubResource> virtualMachinesAssociated = default,
    CapacityReservationGroupInstanceView instanceView = default,
    IEnumerable<WritableSubResource> sharingSubscriptionIds = default,
    CapacityReservationType? reservationType = default)
{ ... }

Back-compat overload emitted by generator (does not compile):

[EditorBrowsable(EditorBrowsableState.Never)]
public static CapacityReservationGroupData CapacityReservationGroupData(
    ResourceIdentifier id, string name, ResourceType resourceType, SystemData systemData,
    IDictionary<string, string> tags, AzureLocation location,
    IEnumerable<string> zones,
    IEnumerable<SubResource> capacityReservations,
    IEnumerable<SubResource> virtualMachinesAssociated,
    CapacityReservationGroupInstanceView instanceView,
    IEnumerable<WritableSubResource> sharingSubscriptionIds)
{
    return CapacityReservationGroupData(
        id, name, resourceType, systemData, tags, location,
        capacityReservations, virtualMachinesAssociated, instanceView,
        reservationType: default,        // CS1744 follows ↓
        sharingSubscriptionIds, zones);  // positional after named
}

Three distinct issues in this single body:

  1. zones is passed last; in the primary it's the 7th parameter (after location).
  2. reservationType: default is a named argument followed by positional arguments.
  3. Several other methods (RestorePointData, ManagedDiskData, CapacityReservationData, SharedGalleryImageData, SnapshotData, AvailabilitySetData/Patch, GalleryData/ImageData, ManagedDiskPatch, CapacityReservationGroupPatch, CapacityReservationPatch) follow the same pattern with renamed/added parameters that the back-compat call site doesn't account for.

Expected

For back-compat overload generation, only emit the overload when the generator can produce a valid forwarding body. If the parameter list of the current primary diverges in a way the generator can't reconcile (renames, type changes, removed parameters), it is more useful to omit the overload and let the SDK author add a hand-written back-compat shim, than to emit code that does not compile.

A "compiles or skips" guarantee on emitted back-compat overloads would dramatically reduce the manual cleanup cost during large migrations.

Workaround used today

Exclude the entire generated ArmComputeModelFactory.cs from compilation via <Compile Remove="..." /> plus [CodeGenSuppress] for the affected overloads, and hand-author replacements in customization code. Both have to be re-evaluated each regen.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingemitter: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