fix(http-client-csharp): avoid swapping model factory parameter names when previous contract had same-typed params in different order#10491
Conversation
…compat baseline when no rename occurred The generic back-compat preservation introduced for non-paging parameters unconditionally rewrote any current parameter to the casing/spelling found in the previous published contract. When the spec parameter name had not been renamed by the generator (Name == OriginalName), this caused parameters whose casing had been intentionally changed in a more recent release to be silently rewritten back to a stale baseline's casing, producing a source-breaking rename for callers using named arguments. Restrict the generic preservation to actual generator-introduced renames (Name != OriginalName). The paging-specific renames (top -> maxCount, maxpagesize -> maxPageSize) continue to be preserved via their own explicit calls. Reproduced against Azure.Developer.LoadTesting where `metricname` was being silently rewritten to `metricName` because the API-compat baseline (an older 1.0.2 release) still had the capitalized form. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…om back-compat baseline when no rename occurred" This reverts commit fd196b9.
… when previous contract had same-typed params in a different order PreservePreviousParameterNames in ModelFactoryProvider does positional renames based on the matching previous method's parameters. When the previous and current contracts both have two same-typed parameters but in OPPOSITE order, the positional rename swapped the parameter NAMES while the body still referenced the original ParameterProvider object identities. The result was that GetCtorArgs (which matches by name) wired each parameter to the wrong constructor field — a real correctness regression and source-breaking change for users of model factory methods. Reproduced via Azure.AI.VoiceLive.SessionUpdateConversationItemDeleted, which emitted `new SessionUpdateConversationItemDeleted(.., itemId0, .., eventId0)` where the values intended for `itemId` ended up assigned to `eventId` and vice versa. Fix: skip the rename when applying it would cause `previousName` to collide with another current parameter's current name. This preserves the legitimate casing/spec-rename use case (e.g. metricname -> metricName) while preventing the swap pathology. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
commit: |
|
No changes needing a change description found. |
|
Good question — I considered named arguments but they wouldn't fix this particular bug. The root issue is that So even if new SessionUpdateConversationItemDeleted(@type, eventId: <P_evt.var>, additionalBinaryDataProperties: null, itemId: <P_itm.var>)after the swap-rename runs, new SessionUpdateConversationItemDeleted(@type, eventId: itemId, additionalBinaryDataProperties: null, itemId: eventId)— same semantic swap (eventId field gets To make named arguments help we'd have to either (a) rebuild the body after the rename, or (b) snapshot the original parameter names at body-build time so the body's references are decoupled from the --generated by Copilot |
Problem
PR #10464 introduced
ModelFactoryProvider.PreservePreviousParameterNames, which positionally renames current factory-method parameters to match the previous contract whenever the method signatures match by name + parameter types. When the previous and current contracts both have two same-typed parameters but in opposite order, this positional rename silently swaps which parameter feeds which constructor field via the name-based lookup inGetCtorArgs.This is a real correctness regression and a source-breaking change. Concretely,
Azure.AI.VoiceLive.VoiceLiveModelFactory.SessionUpdateConversationItemDeletedregenerates as:Note
itemId0is passed to the constructor'seventIdfield andeventId0to theitemIdfield — values silently end up in the wrong properties. Reproduced via the auto-generated Azure SDK PR Azure/azure-sdk-for-net#58632.Fix
In
PreservePreviousParameterNames, skip the rename when applying it would causepreviousNameto collide with another current parameter's current name. This preserves the legitimate casing/spec-rename use case (e.g.metricname->metricName) while preventing the swap pathology.After the fix, the same factory regenerates correctly:
Tests
Added
BackCompatibility_SwapTypeParamsDoesNotCorruptregression test covering the case where the previous contract has two same-typed parameters in swapped order. All 1434 Generator + 1312 ClientModel tests pass.--generated by Copilot