Skip to content

Migrate workspace to Rust 2024 edition - #1207

Open
isPANN wants to merge 6 commits into
mainfrom
rust-2024-edition
Open

isPANN wants to merge 6 commits into
mainfrom
rust-2024-edition

Conversation

@isPANN

@isPANN isPANN commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Moves all four workspace crates from Rust 2021 to Rust 2024, following the official transition guide. Each commit maps to steps of the guide:

Commit Guide step What changed
81c2e1be 2–4: cargo fix --edition, set edition, test edition = "2024" in all crates and rustfmt. Closure patterns rewritten for 2024 binding modes. Nested ifs collapsed into let-chains (the clippy follow-up the guide asks for). Illustrative doc snippet fenced as text, because make test --include-ignored was already failing on main.
16ee25fe 5: cargo fmt (separate commit) Formatting only: 2024 style edition, mostly version-sorted imports. Safe to add to .git-blame-ignore-revs.
57ebb235 review follow-up rust-version = "1.88" and one indentation fix.
8368ccaf codecov follow-up Tests for error branches the let-chain rewrite reflowed (test code only).
5a02d669 unrelated tooling fix scripts/pipeline_pr.py wait-ci crashed (AttributeError: ... 'format') because it lacked --format. Added the option and a regression test.
4152d3ec unrelated tooling fix test_create_pr_uses_gh_pr_create_and_returns_current_context was stale: b17d57de made create_pr look up the PR by head branch without updating the test. Test-only.

Step 1 (cargo update): Cargo.lock is untracked, so this step produces no commit. make check passes on freshly updated dependencies.

Manual review the guide calls for:

  • Macros: the tool rewrote expr to expr_2021; this PR reverts that. In 2024, expr also accepts _ and const {}, but in none of the 27 :expr fragments could that input fall into a different macro arm.
  • Drop order: the migration flagged 6 sites. The workspace has no Drop impls and no lock or RefCell guards, so the only effect is when memory is freed.
  • Doctests: they pass under 2024.
  • Platform-specific code: there is no cfg-gated platform code.
  • unsafe: the existing unsafe already uses explicit blocks.

MSRV: 1.88. The let-chains this migration introduces need 1.88, and so does the dev-dependency proptest 1.12. Verified with cargo +1.88 check --workspace --all-targets --all-features. On 1.88, the MSRV-aware resolver picks uuid 1.26 for rmcp, since uuid 1.28 needs 1.89.

Test plan

  • make check (fmt-check, clippy --all-features -D warnings, full test suite, --include-ignored) after each commit
  • make paper
  • cargo +1.88 check --workspace --all-targets --all-features

🤖 Generated with Claude Code

- Set edition = "2024" for all four crates and rustfmt; pin
  style_edition = "2021" so this change carries no formatting churn.
- Apply cargo fix --edition (closure pattern binding modes); keep plain
  `expr` macro fragments since no caller passes `const {}` or `_`.
- Collapse nested ifs into let-chains per clippy::collapsible_if.
- Mark the illustrative #[reduction] syntax snippet as `text` so
  `make test` (--include-ignored) no longer tries to compile it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@isPANN

isPANN commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

Agentic Review Report

PR #1207: Migrate workspace to Rust 2024 edition (head 81c2e1be). This is a generic, toolchain-only PR: 54 files, +255/−266, with no models or rules added or removed.

Structural and semantic

  • Hard fails: none. The diff has no generated exports, no unrelated edits, and no trait or macro behavior changes.
  • Edition bump: all four crates (problemreductions, -cli, -expr, -macros) and rustfmt.toml are set to 2024. style_edition = "2021" keeps formatting unchanged, and make fmt-check passes.
  • Closure patterns (|(_, &v)| → |&(_, &v)|): these are the mechanical cargo fix rewrites required by the 2024 match-ergonomics rules. Every call site is .iter().enumerate().filter(...) over Copy items (bool, i64, tuples of usize), so the binding types stay the same.
  • Let-chain collapses (all 39 hunks checked by hand):
    • Each rewrite merges an outer if / if let that has no else with an inner if / if let that also has no else. The new if A && let B = … && C { body } evaluates A, then B, then C, left to right with short-circuiting. That is the same order as the nested form. No else branch was added, removed or moved.
    • Statements after the inner block stayed outside the chain: for example, return; in cli.rs:392ff and normalized.push(...) after continue in ilp.rs:585ff/625ff.
    • ? inside a condition (rules/graph.rs:1581 aggregate_witness_evaluation(..)?, and solvers/decision_search.rs:39,79 is_satisfiable(..)?) runs at the same point and under the same guard as before. The mutable borrow from normalized.last_mut() is still scoped to the body.
    • The parenthesized || in mcp/tools.rs:254 keeps the original grouping.
  • Edition-2024 drop order (if_let_rescope, tail_expr_drop_order), including sites the lint did not flag:
    • I searched the whole workspace (src, the three sub-crates, tests, examples). It has no Drop impls, no RefCell / Mutex / RwLock / .lock() / .borrow_mut() guards, and no ManuallyDrop / mem::forget or tempfile guards.
    • The only interior mutability is Cell counters (unit_tests/solvers/brute_force.rs:238, unit_tests/rules/traits.rs:513), the OnceLock registries, and the allocation-counting GlobalAlloc test (unit_tests/models/graph/bounded_component_spanning_forest.rs). That test counts only alloc and realloc, not dealloc, so earlier drops cannot change its count.
    • So the rescoping changes only when memory is freed. No observable behavior changes.
  • Reverting expr_2021 to expr: this is safe. In 2024, $x:expr also accepts const { … } and _. That can change behavior only when an earlier arm with expr would now capture input meant for a later arm.
    • I checked all 27 :expr fragments across the 35 macro_rules!. No arm order lets _ or const {} reach a different arm.
    • impl_variant_param! arms are distinguished by , k:. The @declare arms of register_decision_variant! are distinguished by a trailing random. impl_variant_reduction!, impl_sat_to_ksat! and the test macros have one arm each.
    • Fragment semantics follow the edition of the crate that defines the macro, so the exported macros now accept slightly more input downstream. They never accept less.
  • Other 2024 changes:
    • Box<[T]>::into_iter: the Box<[Plan]> / Box<[Expr]> fields are only iterated with .iter(), and SerializedNode uses Vec.
    • The gen keyword appears only as a string in the expr reserved-word list.
    • unsafe extern and set_var changes don't apply. The unsafe {} blocks in the global allocator were already there.
    • Merged doctests and never-type fallback compile and pass under -D warnings.
  • Doc fence ```ignore → ```text in problemreductions-macros/src/lib.rs:211: this is correct, because the block is illustrative syntax and cannot compile under --include-ignored.
  • Issue compliance: there is no linked issue. The PR body matches the diff.

Quality

  • No production logic changed, so no new tests are needed. The existing suite exercises every touched model, rule and solver path.
  • Local gates on the PR head (rustc 1.99.0):
    • make fmt-check: OK
    • make clippy (--all-targets --all-features -D warnings): OK
    • make test (workspace, --include-ignored): all pass, including 6241 lib unit tests, 344 integration tests and the MCP integration tests, with 0 failures.
  • cargo doc prints 4 rustdoc warnings (URL is not a hyperlink, unclosed HTML tag, unresolved link). They are in doc comments this PR does not touch.

Feature test (make cli, scratch directory)

  • pred show MIS prints the schema description line (commands/graph.rs let-chain).
  • pred create --example MaximumMatching → pred solve returns Max(2) via ILP (maximum_matching.rs let-chains).
  • pred create --example MinimumSetCovering → pred solve --solver brute-force returns Min(2) (covered_elements let-chain).
  • pred path MIS QUBO → pred reduce --via → pred solve returns Max(4) with QUBO intermediate Min(-394). This recovers through recover_completed_result (rules/graph.rs let-chain).
  • pred solve with a missing argument gives the usual clap error and usage text.

Findings

Critical: none.

Important: none.

Minor

  1. No declared MSRV (Cargo.toml:12, problemreductions-{cli,expr,macros}/Cargo.toml:4). Edition 2024 requires Rust ≥ 1.85, and the PR body says so, but no crate sets rust-version. Without it, crates.io users on an older toolchain get a less helpful error, and the implicit resolver = "3" that 2024 brings has no MSRV to resolve against. Fix: add rust-version = "1.85" to each [package], or to a [workspace.package] that the crates inherit.
  2. Misaligned indentation after the collapse. At problemreductions-cli/src/mcp/tools.rs:261-265, the anyhow::bail! arguments still sit at the old, deeper nesting level. At src/models/algebraic/ilp.rs:597, the format! string in normalize_constraint no longer lines up with its format!(. rustfmt skips these blocks because the string literal is longer than max_width, so fmt-check doesn't catch it. Fix: dedent them by hand, or split the long strings so rustfmt can format the blocks. This is cosmetic only.
  3. PR body rationale for reverting expr_2021. The body says "no caller passes const {} or _", but the stronger reason is that no macro has an arm that such input could be redirected to. That also covers future downstream callers of the exported macros. Fix: optionally reword the body; no code change is needed.

Verdict: the change preserves behavior. Every let-chain is equivalent to the nested form, the drop-order changes have no observable effect because the workspace has no guard-type destructors, and reverting to expr is safe. It can be merged once a human approves; adding rust-version is recommended.

Drop the style_edition = "2021" pin and reformat. Formatting only:
2024 version-sorts imports and adjusts a few layout rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.00000% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.99%. Comparing base (f11b2a4) to head (4152d3e).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/unit_tests/example_db.rs 82.35% 6 Missing ⚠️
...odels/misc/minimum_code_generation_one_register.rs 55.55% 4 Missing ⚠️
src/rules/ksatisfiability_cyclicordering.rs 50.00% 4 Missing ⚠️
.../rules/consistencyofdatabasefrequencytables_ilp.rs 0.00% 2 Missing ⚠️
src/rules/stringtostringcorrection_ilp.rs 0.00% 2 Missing ⚠️
...s/decisionminimumvertexcover_hamiltoniancircuit.rs 0.00% 1 Missing ⚠️
src/rules/hamiltoniancircuit_hamiltonianpath.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1207      +/-   ##
==========================================
+ Coverage   96.90%   96.99%   +0.08%     
==========================================
  Files        1095     1095              
  Lines      145109   145468     +359     
==========================================
+ Hits       140621   141096     +475     
+ Misses       4488     4372     -116     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Edition 2024 needs Rust >= 1.85, but the let-chains this migration
introduces need 1.88 (dev-dependency proptest 1.12 needs 1.88 too).
Verified with `cargo +1.88 check --workspace --all-targets --all-features`.
The MSRV-aware resolver (default for edition 2024) picks uuid 1.26 for
rmcp on 1.88, since uuid 1.28 needs 1.89.

Also realign a format! string in ilp.rs that rustfmt cannot reflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@isPANN

isPANN commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the Minor findings of the Agentic Review Report in 57ebb23:

  1. MSRV: declared rust-version = "1.88", not 1.85. The let-chains this migration introduces need 1.88. Verified with a real 1.88 toolchain across the whole workspace.
  2. Indentation: tools.rs was already fixed by the 2024 style reformat in 16ee25f. ilp.rs:597 was realigned by hand.
  3. PR body: now gives the stronger reason the expr revert is safe (no macro arm can be re-matched by the new input) and maps each commit to a step of the edition guide.

isPANN and others added 3 commits October 14, 2026 05:05
The edition migration reflowed several error branches that no test
reached, failing the codecov patch target. Add tests asserting each
exact error: undersized num_vertices on graph construction, weight and
setup-time overflow in evaluate, ILP objective overflow, malformed
targets in rule extraction and gadget tapes, CLI after-help, and two
proc-macro rejection paths. Test code only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wait-ci reached emit_result(result, args.format) without defining
--format, so every run crashed with AttributeError after CI finished.
Add the option with the same choices and default as the other
subcommands, plus a parser regression test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b17d57d made create_pr look up the new PR by its head branch (so it
works from worktrees) but left the test asserting the old call without
head=. Assert the current call; production code is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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