pm4ai

Lintmax (Rust)

Gotchas

  • The gate's completeness is itself audited: a maximum gate runs EVERY check at full scope — gate doctests not just unit tests (a discarded cargo test --doc result lets a broken doc example pass), pass --workspace to every per-package command (a workspace with a root package otherwise lints only the root), and deny yanked + unmaintained deps (cargo-deny defaults them to warn). A stage that runs but discards its result is a silent hole.
  • Max-strict rustfmt is not just style options — it includes the GATING levers error_on_line_overflow + error_on_unformatted, which turn "silently leaves a >max_width line / unformattable comment" into a hard failure; without them a long inline json!/macro payload or raw-string literal sails through. Enabling them surfaces real overflow lines (fix the code: split a json! body multi-line, concat!-join a long raw string), never disable the lever.
  • A fix stage that DISCARDS its formatter result false-greens: cargo fmt in write mode returns non-zero when error_on_line_overflow hits a line it cannot fit, so run_fmt_all must RETURN that result (gate it), exactly like the check path — otherwise fix passes while check/CI fails on the same tree.
  • The configs the gate writes into the project dir (rustfmt.toml, etc.) are themselves walked by the formatter stages, so any block the gate appends (e.g. the computed vendored ignore list) must already be in the downstream formatter's canonical style (dprint TOML = 2-space array indent) or check flags the gate's own written config as unformatted while fix silently reformats it.
  • Maximum strictness spans EVERY child tool, not just rustfmt: shellcheck runs --severity=style --enable=all (style severity + optional checks are off by default), shfmt runs -s (simplify, a lint-grade normalizer) plus -ci -sr -bn for one canonical form, and cargo-deny adds multiple-versions-include-dev (dev-dep version dups slip the default check) + required-git-spec="rev" (force git deps pinned). A stage left at its tool default is a silent strictness gap.
  • The rustc forbid list is audited by diffing rustc -W help allow-by-default lints against the list — but the gate runs STABLE, so a nightly-feature-gated lint (fuzzy_provenance_casts, must_not_suspend, unqualified_local_imports, …) passed as -F breaks the build with "unknown lint"; test each candidate with rustc -F <lint> on stable first, add only the ones it accepts (e.g. linker_info, linker_messages).
  • A per-crate gate (cargo lintmax in the crate dir) does NOT reach shell/config files outside the crate (repo-root scripts/), so a launcher script accretes ungated debt; lint repo-root shell with the same shellcheck --severity=style --enable=all + shfmt -i=2 -ci -sr -bn -s flags via a root-level gate invocation, never trust the nested per-crate run to cover it.
  • A lint that fundamentally conflicts with a required language pattern (clippy arbitrary_source_item_ordering vs serde #[serde(other)]-must-be-last) is fixed in the CONSUMER code (rename the catch-all variant to sort last, or collapse the enum), never by disabling the lint for that item-kind — disabling it for one conflict turns it off everywhere.
  • Vendored external crates (a committed path-dependency under the repo) are not first-party: exclude them from file-walking stages (dprint, typos, machete via a temp .ignore) the same way registry deps are never linted — detect them via cargo metadata (null source + non-workspace-member), never by directory name.
  • A [workspace] exclude does NOT stop cargo fmt --all reformatting a vendored crate under a strict root rustfmt config — the formatter still visits it; the rustfmt-native fix is an ignore = [...] list computed from the vendored dirs (cargo metadata) and appended to the written rustfmt.toml, and clean_configs must recognize the appended-ignore form as tool-owned so the consumer repo is left clean.
  • Strict nightly-only rustfmt options (import grouping, comment normalization) require the nightly rustfmt binary, forced via the RUSTFMT env var on cargo fmtrustup run nightly cargo fmt fails when the PATH cargo is not the rustup proxy (its fmt still finds stable rustfmt); the gate self-installs nightly + the rustfmt component and fails closed in check when absent.
  • A --profile minimal toolchain (the common CI install: rustup toolchain install stable --profile minimal) OMITS the rustfmt and clippy rustup components, so the gate's own cargo fmt / cargo clippy stages die with "'cargo-fmt'/'cargo-clippy' is not installed for the toolchain" on a fresh runner — the gate must self-install BOTH components on the ACTIVE toolchain (rustup component add rustfmt/clippy, gated on a cargo fmt --version / cargo clippy --version probe), not only the nightly rustfmt it forces via RUSTFMT. This is the toolchain-COMPONENT twin of the child-binary self-install: a stage that shells out to a cargo <component> subcommand owns ensuring that component exists, and the failure cascades one stage at a time (fmt green only reveals the clippy gap), so sweep the WHOLE component set the gate uses in one pass. The local machine passes only because its toolchain was installed full-profile.
  • Incremental clippy can false-green: a warm cargo clippy cache skips a newly-strict lint on a just-edited item, so a local fix reads clean while the fresh pre-commit/CI clippy fails — the fresh gate is the authoritative backstop, never the warm local cache.
  • lintmax fix (the TS gate) on a MIXED-language repo WITHOUT scoping ignores mangles every non-TS file it walks: run blind on a rust+TS repo it reformatted a vendored rust crate, a sourced prompt .txt, generated drizzle migration metadata, and Cargo.toml, and eslint errored (Oops! Something went wrong). Before running fix on a mixed repo, SCOPE the gate to the hand-written TS-family source only (exclude the whole rust crate dir, node_modules, dist, vendored deps, and codegen output) — the rust is gated separately by lintmax-rs. Recover with git checkout -- . from the clean pre-fix checkpoint (always commit before any autofix).
  • A corrupt cached build artifact masks the WHOLE gate: a malformed target/doc/crates.js (or any stale doc/incremental artifact) makes the doc/compile stage exit before clippy runs, so the gate prints a generic stage error while every real restriction-group finding behind it stays hidden — reading clean is the false-green. On any unexplained stage error, blow away the cached artifact (rm -rf target/doc) and re-run from cold before trusting a pass; never let a never-stale toolchain bump's new lints sit behind a masked stage.
  • Passing the Rust gate's restriction group shapes the code: no ? operator (use let Ok(x) = e else {…} / match), no let _ = (a fn discard<T>(_v: T){} helper), no unwrap/expect/panic-paths, no indexing (.get/.pointer), no raw arithmetic (wrapping_add/checked_*), no as casts (try_from), explicit return everywhere (implicit_return is active because needless_return is allowed), alphabetized struct fields + enum variants, a bool fn-param becomes a 2-variant enum, a >2KB async future is Box::pin-ed, and only //////! doc comments survive — every other // is stripped.

On this page