feat: add tooling for parameter migration#2684
Conversation
krlmlr
left a comment
There was a problem hiding this comment.
Nice, thanks for driving this!
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>
|
And if we think that a little further: what if our code generator patches the code in place, delineated by |
Interesting idea, I will iterate a bit more |
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>
|
Iterated one more time |
krlmlr
left a comment
There was a problem hiding this comment.
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.
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>
|
Switched the inline closure for a plain named helper |
krlmlr
left a comment
There was a problem hiding this comment.
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.
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>
|
If this is ok we could merge to main and apply in #2677? |
krlmlr
left a comment
There was a problem hiding this comment.
Thanks, great! Would you like to do a follow-up PR for refining this?
| # matches both `weight` and `weights`). Consumed by | ||
| # tests/testthat/test-migration-fixture.R. | ||
| migration_fixture = list( | ||
| old = function(graph, n, weight = weights, type, directed) {}, |
There was a problem hiding this comment.
Playing around the following axis would be great in a follow-up PR, including calls with g = ... or gr = ... instead of graph = ...:
| old = function(graph, n, weight = weights, type, directed) {}, | |
| old = function(graph, n, weight = weights, kind = type, directed) {}, |
@krlmlr a suggestion derived from the discussions in #2677. This would be a way to generate arg migration functions.