Skip to content

Fix CancellationToken parameter ordering and name collision in v3 OperationCompiler#341

Merged
sergey-tihon merged 4 commits intorepo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95from
copilot/sub-pr-336-another-one
Mar 25, 2026
Merged

Fix CancellationToken parameter ordering and name collision in v3 OperationCompiler#341
sergey-tihon merged 4 commits intorepo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95from
copilot/sub-pr-336-another-one

Conversation

Copy link
Contributor

Copilot AI commented Mar 23, 2026

Two bugs in the CT overload generation: CT was appended after optional parameters (invalid in .NET — required params must precede optional), and the CT parameter name was always cancellationToken with no collision check against generated OpenAPI parameter names.

Changes

  • Parameter ordering: Split parameter building into two passes (required, then optional) with the used-name set threaded between them. CT is now inserted at ctArgIndex = requiredParamCount, giving the signature: (required...) → CT → (optional...).

  • Name uniqueness: Collect all generated parameter names into a set before creating the CT param; fall back to cancellationToken1, cancellationToken2, etc. on collision.

  • invokeCode extraction: CT is now extracted by List.item ctArgIndex allArgs (index captured during parameter building) rather than by peeling off the last argument, which broke under the new ordering.

Before / After signature example

// Before: CT appended last — after optional params
MyMethod(requiredA: string, ?optB: int, cancellationToken: CancellationToken)

// After: CT sits between required and optional
MyMethod(requiredA: string, cancellationToken: CancellationToken, ?optB: int)

📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI changed the title [WIP] [WIP] Address feedback on CancellationToken support in OpenApiClientProvider methods Fix CancellationToken parameter ordering and name collision in v3 OperationCompiler Mar 23, 2026
Copilot AI requested a review from sergey-tihon March 23, 2026 22:27
@sergey-tihon
Copy link
Member

@copilot fix build

Copy link
Contributor Author

Copilot AI commented Mar 24, 2026

@copilot fix build

Fixed in commit 6e5ba8a.

Root cause: dotnet build SwaggerProvider.sln triggers inner-build restores via Paket.Restore.targets that overwrite project.assets.json with only a single TFM (the last one processed — either netstandard2.0 or net10.0). When BuildTests then ran dotnet build SwaggerProvider.TestsAndDocs.sln, it hit NETSDK1005 because the assets file was missing the other TFM.

Fix: BuildTests now first runs dotnet restore SwaggerProvider.TestsAndDocs.sln (which correctly populates the assets file with both netstandard2.0 and net10.0), then builds with --no-restore to prevent the inner-build restore from corrupting the assets file again.

@sergey-tihon
Copy link
Member

/repo-assist merge target branch into this one and actualize changes

@github-actions
Copy link
Contributor

github-actions bot commented Mar 25, 2026

Repo Assist completed successfully!

@sergey-tihon
Copy link
Member

@copilot merge target repo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95 branch into this one, resolve conflicts and actualize changes

…dering + uniqueness improvements from this PR

Both branches fixed CT name uniqueness independently. This branch additionally
fixes the parameter ordering bug (CT must precede optional params). The merge
keeps the more complete implementation (buildProvidedParameters helper, two-pass
required/optional split, ctArgIndex insertion) and discards the simpler
base-branch approach that still appended CT after optional params.
Copy link
Contributor Author

Copilot AI commented Mar 25, 2026

@copilot merge target repo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95 branch into this one, resolve conflicts and actualize changes

Done in commit 4888df8. Merged repo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95 into this branch. The base branch had independently added CT name-uniqueness (via Seq.initInfinite), while this branch already had that plus the parameter ordering fix. Conflict in OperationCompiler.fs resolved by keeping our more complete implementation (two-pass buildProvidedParameters, ctArgIndex insertion between required and optional params).

@sergey-tihon sergey-tihon marked this pull request as ready for review March 25, 2026 08:46
Copilot AI review requested due to automatic review settings March 25, 2026 08:46
@sergey-tihon sergey-tihon merged commit c56be43 into repo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95 Mar 25, 2026
2 checks passed
@sergey-tihon sergey-tihon deleted the copilot/sub-pr-336-another-one branch March 25, 2026 08:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes v3 client-method generation in OperationCompiler so CancellationToken is emitted as a required parameter between required and optional OpenAPI parameters (valid .NET ordering), and ensures the generated CT parameter name cannot collide with OpenAPI parameter names. It also adjusts the build script to explicitly restore before building the test solution to avoid multi-TFM assets issues.

Changes:

  • Reworked v3 parameter construction into required/optional passes and inserted CancellationToken at ctArgIndex = requiredParamCount.
  • Added unique-name generation for the CT parameter (cancellationToken, cancellationToken1, …) based on already-generated parameter names.
  • Updated invokeCode to extract CT by ctArgIndex rather than assuming it is the last argument; updated BuildTests to restore then build --no-restore.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs Fixes CT parameter ordering and name-collision handling; updates invokeCode arg extraction accordingly.
build.fsx Adds an explicit restore step before building the tests solution to stabilize multi-TFM restore/build behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +195 to 200
let ctName = findUniqueName "cancellationToken" 1
let ctParam = ProvidedParameter(ctName, typeof<Threading.CancellationToken>)
// CT is inserted after required params so it never follows optional params
let ctArgIndex = List.length requiredProvidedParams
ctArgIndex, requiredProvidedParams @ [ ctParam ] @ optionalProvidedParams
else
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The CT ordering/name-collision fixes are not covered by tests. Consider adding a v3 unit test that compiles a minimal schema with both required and optional parameters and asserts (via reflection over the provided method) that the CancellationToken parameter is positioned after required params and before optional params, and another schema containing an OpenAPI parameter named cancellationToken to assert the generated CT parameter name is suffixed (e.g., cancellationToken1).

Copilot uses AI. Check for mistakes.
sergey-tihon added a commit that referenced this pull request Mar 25, 2026
…vider generated methods (closes #212) (#336)

* feat: add CancellationToken support to OpenApiClientProvider generated methods (closes #212)

- Add CallAsync overload with CancellationToken to ProvidedApiClientBase
- Thread CancellationToken from generated methods through to HttpClient.SendAsync
- Each generated method gains an optional cancellationToken parameter (defaults to CancellationToken.None)
- Backward-compatible: existing call sites without CT continue to work unchanged
- Add unit tests: success with CancellationToken.None, cancellation propagation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ci: trigger checks

* fix: replace optional struct CancellationToken parameter with method overloads (#337)

* Initial plan

* Fix: revert global.json and address CancellationToken build failures

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/1861c3cb-6a0a-438a-aa31-f65b8c809f88

* fix: use method overloading for CancellationToken support instead of optional struct parameter

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/1861c3cb-6a0a-438a-aa31-f65b8c809f88

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>

* Add type provider integration tests for CancellationToken-overloaded methods (#338)

* Fix: CT parameter name uniqueness in CancellationToken overload generation (#339)

* Initial plan

* fix: generate unique CT parameter name to avoid collision with OpenAPI params named 'cancellationToken'

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/7d588ec7-c4df-4a6c-89f8-9c13c2472d29

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>

* Fix CancellationToken parameter ordering and name collision in v3 OperationCompiler (#341)

* Initial plan

* fix: insert CT between required and optional params; generate unique CT name

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/b0c519de-0186-40ca-8174-42ed67a5316a

* fix: add explicit restore + --no-restore to BuildTests to fix NETSDK1005

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/565d6633-576d-4587-b924-a29b0ea53c2c

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>

* refactor: use UniqueNameGenerator for CT param name uniqueness

Replace hand-coded recursive findUniqueName function with the existing
UniqueNameGenerator utility (already used in DefinitionCompiler and
for method name deduplication in OperationCompiler).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: add optional occupiedNames parameter to UniqueNameGenerator constructor

Allows callers to pre-seed the generator with names that are already taken,
so MakeUnique will never return any of those names without a numeric suffix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: single CallAsync overload + single generated method with optional CancellationToken

- Remove no-CT CallAsync overload from ProvidedApiClientBase; keep only the
  version with explicit CancellationToken (quotation code always supplies it)
- Remove double-compilation in OperationCompiler: one method per operation with
  optional cancellationToken (null default = default(CancellationToken).None)
- Update RuntimeHelpersTests to pass CancellationToken.None explicitly

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* cleanup: simplify OperationCompiler and add default/async CT tests

- Use List.map instead of List.collect since compileOperation returns a single method
- Clean up comments in OperationCompiler
- Add test for calling generated method without CancellationToken (default token)
- Add test for async (PreferAsync=true) generated method without CancellationToken

* feat: propagate CancellationToken through ReadAsStringAsync/ReadAsStreamAsync via RuntimeHelpers

Add readContentAsString and readContentAsStream wrappers to RuntimeHelpers
with #if NET5_0_OR_GREATER guards, enabling CancellationToken propagation
in generated quotation code that must compile against netstandard2.0.
Also add explicit CancellationToken integration tests and conditional
CT support in ProvidedApiClientBase error path.

* test: add CT coverage for stream, text/plain, async cancellation, and async POST paths

* fix: async tests

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Sergey Tihon <sergey.tihon@gmail.com>
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.

3 participants