Skip to content

fix(rust): correct the declared MSRV to 1.96 and enforce it in CI - #987

Merged
kucherenko merged 3 commits into
masterfrom
fix/rust-msrv-1.96
Aug 31, 2026
Merged

kucherenko merged 3 commits into
masterfrom
fix/rust-msrv-1.96

Conversation

@kucherenko

@kucherenko kucherenko commented Aug 31, 2026 •

Copy link
Copy Markdown
Owner

The problem

The crates.io page for jscpd advertises Rust 1.87 as the minimum supported version. The crate cannot build on it:

$ cargo +1.93 check -p jscpd
error: rustc 1.93.1 is not supported by the following packages:
  oxc_allocator@0.147.0 requires rustc 1.96.0
  oxc_ast@0.147.0 requires rustc 1.96.0
  ...

crates.io renders [workspace.package] rust-version verbatim, so anyone trusting the badge and installing 1.87 hits a hard resolution error rather than a build.

Why it's wrong

The real floor is 1.96.0. These are not optional or dev-only deps — oxc_parser → cpd-tokenizer → jscpd is a mandatory chain:

Requires Crates Path to published crate
1.96.0 13 × oxc_* 0.147.0 cpd-tokenizer → jscpd
1.88 ignore, globset cpd-finder → jscpd
1.88 askama 0.16 + macros cpd-reporter → jscpd

git log -L 13,13:rust/Cargo.toml shows one commit ever touched the line — 0ddf611 (2026-06-04), when the Rust workspace was added. It was likely accurate then (oxc was pinned to 0.133) and drifted as oxc raised its own MSRV across later bumps.

Why nothing caught it

Every workflow pins the toolchain to 1.97.1 — rust.yml:29, release.yml:130, crates-publish.yml:77, npm-rust-publish.yml:151. No job has ever built at the declared MSRV, so the field was free to rot.

Changes

  • rust/Cargo.toml — rust-version 1.87 → 1.96.
  • .github/workflows/rust.yml — new msrv job. It reads rust-version straight out of Cargo.toml rather than hardcoding it, so the workflow can't drift out of sync with the manifest, then runs cargo check --workspace --all-targets --locked against exactly that toolchain.
  • rust/README.md — said "Requires Rust 1.87+ (see rust-toolchain.toml)", but that file pins 1.97.1; the two never agreed. Now states the MSRV and explains what the pin is for.

The job invokes cargo +<version> explicitly instead of relying on the toolchain the action installs — rust-toolchain.toml pins 1.97.1 for development and would otherwise take precedence, silently testing the wrong compiler.

Verification

$ cargo +1.96 check --workspace --all-targets --locked
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 20.95s

Passes with --all-targets, so 1.96 is achievable including dev-dependencies. --locked confirms it holds against the committed lockfile.

Metadata and CI only — no source or behaviour changes.


This change is Reviewable

The workspace declared rust-version = "1.87", set once when the Rust
workspace was added (0ddf611, June 2026) and never revisited. crates.io
publishes that field verbatim, so the jscpd crate page advertised a floor
the crate cannot actually build on:

  $ cargo +1.93 check -p jscpd
  error: rustc 1.93.1 is not supported by the following packages:
    oxc_allocator@0.147.0 requires rustc 1.96.0
    ...

The real floor is 1.96.0, forced by the 13 oxc_* crates that reach the
published crate through the mandatory oxc_parser -> cpd-tokenizer -> jscpd
chain. ignore, globset and askama independently require 1.88. The number
was probably accurate at the time — oxc was pinned to 0.133 then — and
drifted silently as oxc raised its own MSRV across later bumps.

Nothing caught the drift because every workflow pins the toolchain to
1.97.1, so no job ever built at the declared MSRV. This adds an `msrv` job
that reads rust-version straight out of Cargo.toml and runs cargo check
against exactly that toolchain, so the next oxc bump fails loudly instead
of quietly falsifying published metadata.

The check uses `cargo +<version>` rather than relying on the toolchain
installed by the action: rust-toolchain.toml pins 1.97.1 for development
and would otherwise take precedence, making the job test the wrong
compiler.

rust/README.md claimed 1.87+ and pointed at rust-toolchain.toml, which
actually pins 1.97.1 — the two never agreed. It now states the MSRV and
explains what the pin is for.

Verified: cargo +1.96 check --workspace --all-targets --locked passes, so
1.96 is achievable including dev-dependencies.
Raising rust-version to 1.96 turned on 11 clippy::collapsible_if errors
across four crates, failing the Clippy step with -D warnings.

The lint is MSRV-gated: its suggested fix is a let-chain, stabilised in
Rust 1.88, so clippy stayed silent while the workspace claimed 1.87. The
violations were always there — the wrong MSRV was suppressing them.

Applied with `cargo clippy --fix` (machine-applicable suggestions only),
then `cargo fmt` to repair the indentation the fixer leaves behind. Every
site is a plain nested if with no else branch, so each collapse is a pure
rewrite with no change in control flow.

  cpd-tokenizer  javascript.rs, razor.rs, sfc.rs, ts_strip.rs (1 each)
  cpd-finder     walker.rs (5)
  cpd-reporter   sarif.rs (2)
  jscpd          main.rs (3)

Verified: clippy --workspace -- -D warnings clean, cargo fmt --check
clean, 847 tests pass, and cargo +1.96 check --workspace --all-targets
--locked still passes (let-chains predate the MSRV by eight releases).
@kucherenko
kucherenko merged commit 73147ac into master Aug 31, 2026
16 checks passed
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.

1 participant