Skip to content

feat: add tooling for parameter migration#2684

Merged
krlmlr merged 12 commits into
mainfrom
feat/migration-helper-codegen
Jun 3, 2026
Merged

feat: add tooling for parameter migration#2684
krlmlr merged 12 commits into
mainfrom
feat/migration-helper-codegen

Conversation

@schochastics
Copy link
Copy Markdown
Contributor

@krlmlr a suggestion derived from the discussions in #2677. This would be a way to generate arg migration functions.

Comment thread tools/README.md Outdated
Copy link
Copy Markdown
Contributor

@krlmlr krlmlr left a comment

Choose a reason for hiding this comment

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

Nice, thanks for driving this!

Comment thread tools/README.md Outdated
Comment thread tools/README.md Outdated
Comment thread tools/README.md Outdated
Comment thread tools/migrations.R Outdated
Comment thread .github/workflows/check-migrations.yaml Outdated
Comment thread tools/generate-migrations.R Outdated
Comment thread tools/generate-migrations.R Outdated
schochastics and others added 3 commits June 2, 2026 07:24
Rework the migration tooling per review on #2684.

- Registry entries now declare `old`/`new` as literal function signatures;
  renames are inferred from bare-symbol defaults (`c = c_renamed`) and
  defaults are read off the `new` formals. Less typing, no parallel
  renames/defaults bookkeeping.
- Generated helpers now recover legacy calls by position *and* by (partial)
  name: a renamed-away old name (`attr =`) or an abbreviation of a new arg
  (`weig =`) is matched via charmatch(); ambiguous prefixes error.
- Deprecation message shows the detected vs. requested signature side by
  side and drops the "pre-3.0.0" phrasing; build it with paste0().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the standalone check-migrations workflow with two cheaper hooks:

- A testthat helper regenerates R/handle-args-*.R whenever tools/migrations.R
  (or the generator) is newer, so editing the registry and running the tests
  refreshes the helpers automatically.
- The rcc smoke job regenerates from the source checkout and fails on any
  resulting git diff, before the auto-commit steps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Reframe "removed/reordered args" as a supported workaround: place the
  affected argument after `...` so it is recovered by name, not position.
- Explain why `.user_env` is threaded: deprecate_soft() gates on
  is_direct(user_env) before the verbosity override, so the helper's extra
  call frame would otherwise suppress the warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@krlmlr krlmlr left a comment

Choose a reason for hiding this comment

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

Beautiful!

Comment thread .github/workflows/R-CMD-check.yaml Outdated
Comment thread tools/migrations.R Outdated
Comment thread tools/README.md Outdated
Comment thread tools/README.md Outdated
@krlmlr
Copy link
Copy Markdown
Contributor

krlmlr commented Jun 2, 2026

And if we think that a little further: what if our code generator patches the code in place, delineated by # BEGIN ARG_HANDLE and # END ARG_HANDLE comments? No handler function, no magic, no caller env crap.

@schochastics
Copy link
Copy Markdown
Contributor Author

And if we think that a little further: what if our code generator patches the code in place, delineated by # BEGIN ARG_HANDLE and # END ARG_HANDLE comments? No handler function, no magic, no caller env crap.

Interesting idea, I will iterate a bit more

schochastics and others added 3 commits June 2, 2026 18:39
Per review on #2684, pivot from generated handler functions to in-place
codegen. The generator now rewrites the code between
`# BEGIN/END GENERATED ARG_HANDLE` markers inside each migrated function body
instead of emitting a standalone `handle_args_<fn>()`.

- The recovery runs inline: an immediately-invoked function does the pure
  matching (temps stay scoped, no environment access), the host frame reassigns
  its own locals, and a single `lifecycle::deprecate_soft()` is emitted directly
  in the function. Its default `user_env` (caller_env(2)) resolves to the user,
  so the `.user_env` parameter and all the env threading are gone.
- Output is laid out exactly as `air` formats it, so the host files stay clean
  (no air.toml exclusion needed; the old one is dropped).
- Registry: document that old-side default values are ignored (default changes
  live in `new`); note the bare-symbol-default edge.
- Fixture moves to R/migration-fixture.R (a marker-carrying function); the old
  R/handle-args-migration_fixture.R is removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the regenerate + `git diff` drift guard out of the rcc smoke job into the
`custom/after-install` composite action (R is available there, and it is the
natural place per review). The testthat helper still regenerates locally when
the registry is newer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@schochastics
Copy link
Copy Markdown
Contributor Author

Iterated one more time

Comment thread .github/workflows/custom/after-install/action.yml
Copy link
Copy Markdown
Contributor

@krlmlr krlmlr left a comment

Choose a reason for hiding this comment

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

Sweet! We're getting there. I really like the new inline checkers, really looking forward to rolling it out.

I wonder if we could also get a series of snapshot tests that exercise the migration fixture with lifecycle warnings enabled.

Comment thread R/migration-fixture.R Outdated
Comment thread R/migration-fixture.R Outdated
Address review on #2684:

- Drop the inline IIFE in the generated ARG_HANDLE block in favour of a plain,
  named, hand-written helper `migrate_recover_args()` (R/migrate-args.R) that
  takes the per-function maps and returns the recovered values plus the
  deprecation message parts, or NULL. It is pure (no environment access) and
  easy to step through in a debugger. The generated block now just supplies the
  configuration, assigns the results, and emits the inline deprecate_soft().
- Collapse the three-line generated header comment onto the BEGIN marker, which
  may now carry a trailing note; the generator's marker regex captures the
  function name up to the first non-identifier character.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@schochastics
Copy link
Copy Markdown
Contributor Author

Switched the inline closure for a plain named helper migrate_recover_args() in R/migrate-args.R. The generated block just passes config and assigns results, with deprecate_soft() still inline. Also collapsed the generated comment onto the marker. Left the after-install drift check for now

Copy link
Copy Markdown
Contributor

@krlmlr krlmlr left a comment

Choose a reason for hiding this comment

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

Wonderful!

I'd still like to see a test-migration-fixture.R with many snapshot tests. May need to extend the arg names to also cover the "partial matching" case.

Alternatively (or in addition), we can start applying this to a few functions.

Comment thread R/migration-fixture.R Outdated
schochastics and others added 3 commits June 3, 2026 07:53
Address review on #2684:

- Wrap the generated ARG_HANDLE block in `if (...length() > 0L)` so a correct
  new-API call (nothing in `...`) skips the `migrate_recover_args()` call
  entirely; the now-redundant `if (!is.null())` check is dropped.
- Extend the test fixture to `migration_fixture(graph, n, weight = weights,
  type, directed)` so it exercises a rename (`weight -> weights`), unique
  abbreviations (`ty`, `dir`) and an ambiguous one (`weig` matches both
  `weight` and `weights`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename test-handle-args.R to test-migration-fixture.R and add snapshot tests
(run under lifecycle_verbosity = "warning") covering positional recovery, the
renamed-away old name, unique/ambiguous abbreviations, and the unknown /
conflict / too-many error messages, alongside the migrate_recover_args() unit
tests and the generator in-sync checks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a fixture case where a positional value and a named abbreviation are
supplied together, pinning down the interleaved recovery and the
positional-then-named ordering in the "Detected call" message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@schochastics
Copy link
Copy Markdown
Contributor Author

If this is ok we could merge to main and apply in #2677?

@schochastics schochastics marked this pull request as ready for review June 3, 2026 06:32
Copy link
Copy Markdown
Contributor

@krlmlr krlmlr left a comment

Choose a reason for hiding this comment

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

Thanks, great! Would you like to do a follow-up PR for refining this?

Comment thread tools/migrations.R
# matches both `weight` and `weights`). Consumed by
# tests/testthat/test-migration-fixture.R.
migration_fixture = list(
old = function(graph, n, weight = weights, type, directed) {},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Playing around the following axis would be great in a follow-up PR, including calls with g = ... or gr = ... instead of graph = ...:

Suggested change
old = function(graph, n, weight = weights, type, directed) {},
old = function(graph, n, weight = weights, kind = type, directed) {},

@krlmlr krlmlr merged commit 49d18ae into main Jun 3, 2026
6 checks passed
@krlmlr krlmlr deleted the feat/migration-helper-codegen branch June 3, 2026 17:36
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.

2 participants