Repository navigation
chore: migrate to Rust edition 2024 and adopt let-chains - #3782
Merged
Merged
Conversation
KuSh
force-pushed
the
chore/rust-edition-2024
branch
4 times, most recently
from
August 31, 2026 00:18
325e794 to
24f505a
Compare
4 tasks done
KuSh
force-pushed
the
chore/rust-edition-2024
branch
3 times, most recently
from
September 2, 2026 16:55
6cdab99 to
4b9e0c2
Compare
KuSh
force-pushed
the
chore/rust-edition-2024
branch
from
September 4, 2026 22:35
4b9e0c2 to
9124395
Compare
This was referenced Sep 5, 2026
KuSh
force-pushed
the
chore/rust-edition-2024
branch
from
September 13, 2026 01:10
9124395 to
7ce7668
Compare
Contributor
|
Hey @KuSh lgtm once conflict resolved and ci green |
KuSh
force-pushed
the
chore/rust-edition-2024
branch
from
September 13, 2026 18:50
7ce7668 to
3e83aba
Compare
Pin style_edition = "2024" in rustfmt.toml and reformat, ahead of the edition 2024 bump. Setting style_edition explicitly decouples the reformat from the crate edition, so this lands on edition 2021 and CI's `cargo fmt --all -- --check` still passes. Run through rustfmt directly rather than `cargo fmt`. Every module under src/cmds/ is currently declared by automod::dir!, and rustfmt only follows literal `mod` items, so `cargo fmt` cannot see those files -- the next commit makes them reachable. Roughly two thirds of the diff is therefore in src/cmds/, mostly the version-sorted imports style edition 2024 introduces. No logic changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every ecosystem mod.rs under src/cmds/ generated its module list with
automod::dir!(). rustfmt only follows literal `mod` items and cannot expand
macros, so `cargo fmt` never reached any of those files -- and neither did
CI's `cargo fmt --all -- --check` gate, which has been silently passing
over half the crate.
Demonstrated by appending `fn __probe( )->u8{let x=1;x}` to a file
under src/cmds/: with automod, `cargo fmt --all -- --check` reports no
diff; with an explicit `pub mod`, it reports the diff and `cargo fmt --all`
fixes it. The preceding commit had to invoke rustfmt directly for the same
reason; from here `cargo fmt` covers the crate on its own.
Listing modules by hand trades one failure mode for another, so build.rs
now guards the new one: automod compiled any stray .rs file, whereas an
explicit list silently drops a file whose `pub mod` line is forgotten --
never compiled, never linted, tests never run, and check-test-presence.sh
still reporting PASS because it only greps the file for #[cfg(test)]. The
build now fails with the missing declaration named.
Declaring the modules explicitly also exposed clippy::module_inception on
cmds::git::git, which clippy skips inside macro-generated code. git.rs is
renamed to git_cmd.rs, which resolves the lint and matches the convention
its siblings already follow -- diff_cmd, gh_cmd, glab_cmd, gt_cmd. Docs
that point at the module are updated; sample command output that merely
shows a "git.rs" path is left alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bump edition 2021 -> 2024. rust-version stays at 1.91, already well above
the 1.85 floor the edition needs; docs/guide/resources/troubleshooting.md
still claimed 1.70+, which is where a failed `cargo install --git` lands.
Four things the edition forces:
- std::env::{set_var,remove_var} are unsafe in 2024 with no safe std
replacement. Rather than wrap the test call sites in unsafe -- which the
crate denies and .semgrep.yml flags -- route them through temp-env, a
dev-only dependency whose closure API is safe and which restores the
previous value even when the body panics. The hand-rolled CLAUDE_DIR_LOCK
and PI_DIR_LOCK guards existed only to serialise those mutations and are
now redundant; CWD_LOCK and TEST_ENV_LOCK stay, they order more than the
env var itself.
- unsafe_op_in_unsafe_fn is on by default, so the libc calls in the proxy
signal handler and in stream.rs's relay handler need explicit unsafe
blocks, scoped to the libc calls themselves.
- `gen` is a reserved keyword, so the closure by that name in diff_cmd.rs
becomes make_lines.
- Tightened tail-expression temporary scopes let clippy prove the binding in
setup_test_env is inlinable, so let_and_return now fires there.
if_let_rescope changes when the scrutinee temporary drops in an if let/else.
The two sites in show_claude_config take cargo fix --edition's match rewrite,
which keeps the 2021 drop timing.
rustfmt.toml is kept rather than dropped: cargo fmt passes --edition from
Cargo.toml, but a bare rustfmt invocation has no crate context and falls
back to edition 2015, which cannot parse the let-chains the next commit
introduces. Pinning it there keeps format-on-save and pre-commit hooks in
agreement with CI.
clippy::collapsible_if is allowed crate-wide for now; the follow-up commit
adopts let-chains and removes the allow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Let-chains are edition 2024 syntax, so clippy::collapsible_if now fires on
every nested `if let` / `if` pair, and with warnings = "deny" they are build
errors rather than suggestions. Applied with cargo clippy --fix, which
handles all of them in one pass, then cargo fmt to re-indent the collapsed
bodies.
The deepest cases flatten four levels of nesting into one condition:
if let Some(cutoff_time) = cutoff
&& let Ok(meta) = fs::metadata(file_path)
&& let Ok(mtime) = meta.modified()
&& mtime < cutoff_time
{
continue;
}
Drops the crate-wide clippy::collapsible_if allow added in the previous
commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
show_claude_config read the Cursor hook file only to compute _is_thin, which nothing reads. The `?` on that read meant an unreadable hook file aborted `rtk init --status` before it printed the rest of the report -- a status command failing on the thing it is meant to report about. The executable-bit check above it is what the branch actually uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The style-edition reformat touched 69 files without changing behaviour, which buries the real authorship of those lines. GitHub's web blame applies .git-blame-ignore-revs automatically; locally it needs blame.ignoreRevsFile set, so CONTRIBUTING now says so where contributors first clone. Only the reformat is listed. The let-chain commit also touches many files but changes control flow and drops a crate-wide lint allow, so its authorship is real and should stay visible in blame. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
KuSh
force-pushed
the
chore/rust-edition-2024
branch
from
September 13, 2026 23:34
3e83aba to
bf2f7c7
Compare
aeppling
approved these changes
Sep 14, 2026
Merged
This was referenced Sep 17, 2026
This was referenced Sep 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrates the crate from Rust edition 2021 to 2024, and adopts the readability win
the edition unlocks: let-chains.
rust-versionstays at1.91, already well above the1.85floor edition 2024needs, so this costs no MSRV bump. No CI workflow changes — every job already uses
dtolnay/rust-toolchain@stable.Typed
chore:rather thanfeat:: there is no new user-facing capability here, andrelease-please would otherwise publish "migrate to Rust edition 2024" under
Features in the changelog.
Six commits, each independently green (
cargo fmt --checkandcargo clippy --all-targetsverified at every one), reviewable a commit at a time:chore: reformat under rustfmt style edition 2024style_edition, reformat (60 files)chore: list command modules explicitly instead of via automodchore: migrate to Rust edition 2024refactor: adopt let-chainscargo clippy --fixfix(hooks): don't abort init --status on an unreadable Cursor hookchore: ignore the style-edition reformat in git blame.git-blame-ignore-revsA pre-existing bug this surfaced:
cargo fmtwas checking half the crateEvery ecosystem
mod.rsundersrc/cmds/generated its module list withautomod::dir!(). rustfmt only follows literalmoditems and cannot expand macros,so
cargo fmtnever reached any of those files — and neither did CI'scargo fmt --all -- --checkgate.Reproduce on
developby appending this to any file undersrc/cmds/:cargo fmt --all -- --checkreports no diff. With an explicitpub mod, itreports the diff and
cargo fmt --allfixes it.Module lists are now written out by hand, which drops the
automoddependency. Thattrades one failure mode for another, so
build.rsnow guards the new one: automodcompiled any stray
.rsfile, whereas an explicit list silently drops a file whosepub modline is forgotten — never compiled, never linted, tests never run, withcheck-test-presence.shstill reporting PASS because it only greps the file for#[cfg(test)]. The build now fails and names the missing declaration:Declaring the modules explicitly also exposed
clippy::module_inceptiononcmds::git::git, which clippy skips inside macro-generated code — soautomodhadbeen suppressing two separate tools.
src/cmds/git/git.rsis renamed togit_cmd.rs, resolving the lint rather than allowing it and matching the conventionits siblings already follow (
diff_cmd,gh_cmd,glab_cmd,gt_cmd). Git recordsit as a rename. Docs pointing at the module are updated; sample command output that
merely shows a
git.rspath is left alone.What the edition forced
1.
std::env::{set_var,remove_var}areunsafein 2024, with no safestdreplacement. Wrapping the test call sites in
unsafewould collide withunsafe_code = "deny"and theunsafe-blockrule in.semgrep.yml, so they routethrough
temp-env, a dev-only dependencywhose closure API is safe.
Beyond avoiding unsafe, this is a correctness improvement:
temp-envrestores theprevious value even when the body panics, which the hand-rolled helpers did not.
CLAUDE_DIR_LOCKandPI_DIR_LOCKexisted only to serialise env mutation and arenow redundant;
CWD_LOCKstays (it guards the working directory) andtracking.rskeeps
ENV_LOCK, which orders more than one test.2.
unsafe_op_in_unsafe_fnis deny-by-default, so thelibccalls in the proxysignal handler need explicit blocks. This is the only new
unsafein themigration, scoped to the
libc::calls themselves with no safe code inside, andcarrying the repo's existing
// nosemgrep: unsafe-blockannotation.3.
clippy::collapsible_iffires on every nestedif let/ifpair. Withwarnings = "deny"these are build errors, not suggestions — adopting let-chains ismandatory here, not optional polish.
rustfmt.tomlis kept, not dropped with the style-edition pin:cargo fmtpasses--editionfromCargo.toml, but a barerustfmtinvocation — format-on-save,rust-analyzer.rustfmt.overrideCommand, a pre-commit hook — has no crate context andfalls back to edition 2015, which cannot even parse the let-chains this PR
introduces.
Two smaller items: the two
if_let_rescopesites inshow_claude_configtakecargo fix --edition'smatchrewrite, preserving the 2021 drop timing; and edition2024's tighter temporary scopes let clippy prove the binding in
setup_test_envinlinable, so
let_and_returnnow fires there.Verification
Rebased onto
developat d402152. Because this PR replacesautomod::dir!()withexplicit module lists and reformats the crate, it is rebuilt on each rebase rather than
replayed — a plain rebase would carry forward stale
mod.rsfiles and drop newly mergedcommand modules without any conflict marker.
Behavioural equivalence (the point of most of this work — nothing here should change
what rtk prints):
rtk pipecases, same comparisonscripts/benchmark.sh, both binaries, same working directory2,167,209 → 151,955 (-92%)scripts/test-all.sh, both binariesscripts/test-tracking.sh, both binarieshyperfine -m 200after warmupCorrectness and gates:
cargo fmt --checkandcargo clippy --all-targetsclean at every one of the sixcommits, not just at the head
--test-threads=1cargo test --all -- --ignored(real-process integration tests): 7 passed, 0 failedsemgrep scan --config .semgrep.yml --baseline-commit <base> --error(exactly what CIruns): 0 new findings — the
// nosemgrep: unsafe-blockannotations on the newunsafe blocks are correctly placed
scripts/check-test-presence.shandscripts/validate-docs.sh: passrust-2024-compatibilitylints remainbuild.rsguard re-verified: a.rsfile undersrc/cmds/with nopub modline failsthe build with exit 101, naming the missing declaration
Reproducing the suite
Everything above is scripted.
BASEis the merge-base withdevelop; build both binariesinto directories where each is named
rtk, so clap'sargv[0]does not show up as aspurious diff:
Per-commit gates — each of the six commits, not just the head:
Tests, including the real-process ones and a serialised run (
temp-envchanges howenv-touching tests interleave, so that ordering is worth checking explicitly):
Behavioural equivalence — run any command through both binaries and compare
stdout+stderr+exit code, normalising only timings and temp paths:
Project validation scripts. Benchmark and smoke both resolve the binary from the working
directory or
PATH, so swap the binary rather than the directory to keep inputs identical:Security scan, the same invocation CI uses, and startup:
The
build.rsguard, which should fail the build rather than silently skip the file:Not verified locally: the Windows target —
cargo check --target x86_64-pc-windows-msvccannot run the
ring/libsqlite3-sysbuild scripts without an MSVC C toolchain. TheWindows-gated code was audited by hand (its only
unsafeis a block inside a safe fn, sounsafe_op_in_unsafe_fndoes not apply) andtest (windows-latest)passes in CI.cargo-auditis not installed locally; CI's Security Scan job covers it.Notes for reviewers
temp-envis a dev-dependency, so the shipped binary is unaffected(
cargo tree --edges normal -i temp-envfinds nothing).automodis removed, sothis is net-neutral on non-dev dependencies.
Cargo.lockis already v4 and is unchangedapart from the
temp-env/automodswap; resolver 3 affects futurecargo updateruns, which become MSRV-aware against
rust-version = "1.91".tail_expr_drop_ordersites were reviewed and are benign (plainString/Resulttemporaries, no locks or file handles). Note the lint isallow-by-default after the bump, so it cannot flag them again — they were checked
while it still could.
.git-blame-ignore-revslists only the reformat. The let-chain commit touchesmany files but changes control flow and drops a crate-wide lint allow, so its
authorship should stay visible. CONTRIBUTING now documents the one-time
git config blame.ignoreRevsFileopt-in, since it is otherwise inert outsideGitHub's web UI.
🤖 Generated with Claude Code