Skip to content

Match hyphen in multi-revision comment matchers - #124137

Merged
bors merged 1 commit into
rust-lang:masterfrom
tgross35:testsuite-multi-rev-regex
Apr 20, 2024
Merged

Match hyphen in multi-revision comment matchers#124137
bors merged 1 commit into
rust-lang:masterfrom
tgross35:testsuite-multi-rev-regex

Conversation

@tgross35

@tgross35tgross35 commented Apr 18, 2024

Copy link
Copy Markdown
Member

Currently, the matcher //[rev-foo,rev-bar]~ does not get selected by the regex. Change the matcher to include -.

@rustbot

Copy link
Copy Markdown
Collaborator

r? @compiler-errors

rustbot has assigned @compiler-errors.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbotrustbot added A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels Apr 18, 2024
@rustbot

Copy link
Copy Markdown
Collaborator

Some changes occurred in src/tools/compiletest

cc @jieyouxu

@tgross35

Copy link
Copy Markdown
MemberAuthor

Reasoning: https://github.com/rust-lang/rust/pull/123816/files#diff-0238262528c07a864b8aa49d6d3aa989c49b045a012b1a8e2c491c84e3ccac3b wasn't matching anything labeled //[legacy,verbose-legacy]~^, @michaelwoerister suggested fixing the regex.

@compiler-errors

Copy link
Copy Markdown
Contributor

r? @jieyouxu

@michaelwoerister

Copy link
Copy Markdown
Member

The new regex looks good, @tgross35. Would you mind adding a unit test for the parse_expected function that covers the basics? See this for examples of other unit tests in compiletest:
https://github.com/rust-lang/rust/blob/master/src/tools/compiletest/src/header/tests.rs

@jieyouxu

Copy link
Copy Markdown
Member

Making revision names accept - makes sense to me, but I don't remember if the dash messes with crate names generated by revisions, could you double check if something like

//@ revisions: hello-world
//@[hello-world] run-pass
fn main() {}

isn't borked?

@jieyouxu

Copy link
Copy Markdown
Member

IIRC revision names are used in crate names for artifact names and output, I don't immediately recall if we normalized it to become underscore.

Comment threadsrc/tools/compiletest/src/errors.rs Outdated
// //[rev1,rev2]~^^
static RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"//(?:\[(?P<revs>[\w,]+)])?~(?P<adjust>\||\^*)").unwrap());
Lazy::new(|| Regex::new(r"//(?:\[(?P<revs>.+)])?~(?P<adjust>\||\^*)").unwrap());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this regex mean test.name is accepted, along other non-alphanumeric characters? If so, that will break anything using revision name as crate names unless you also perform "name mangling" to normalize the non-alphanumeric characters

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another case of "silently failing" in compiletest where it really really needs to loudly report that your directives don't work and won't work as expected...

@michaelwoerister

michaelwoerister commented Apr 19, 2024

Copy link
Copy Markdown
Member

#123816 is modifying a test case that already uses - in a revision name. It seems to work except for when used in error annotations.

However, generally restricting revision names to valid ASCII-only identifiers seems reasonable to me (as long as we give a good error message when compiletest encounters an invalid revision name 🙂)

@jieyouxu

jieyouxu commented Apr 19, 2024

Copy link
Copy Markdown
Member

#123816 is modifying a test case that already uses - in a revision name. It seems to work except for when used in error annotations.

However, generally restricting revision names to valid ASCII-only identifiers seems reasonable to me (as long as we give a good error message when compiletest encounters an invalid revision name 🙂)

I think that only recently works because I added some explicit crate name mangling for revisioned run-rustfix: #123601, which modifying the accepted revision name regex luckily hits. But that only handles . and - I want to say? So if the revision name contains a $ it probably breaks?

@jieyouxu

Copy link
Copy Markdown
Member

However, generally restricting revision names to valid ASCII-only identifiers seems reasonable to me (as long as we give a good error message when compiletest encounters an invalid revision name 🙂)

I think we could just only accept revision names that are crate-name-like (that is, restrict revision names to only consist of alphanumeric + underscore characters), and report an error otherwise. I don't think we want to perform any weird crate name mangling, because e.g. if you naively mangle rev-name as rev_name, then the corresponding crate name will conflict with another revision rev_name. People usually don't write revision names like this, but yeah...

@michaelwoerister

Copy link
Copy Markdown
Member

I agree, the simpler the better.

@tgross35

Copy link
Copy Markdown
MemberAuthor

Having - in revision names seems reasonably common, just based on a quick search https://github.com/search?q=repo%3Arust-lang%2Frust+%2F%40.*revisions%3A.*-%2F&type=code, so it seems like that should work without any special mangling. I just changed the regex to match everything so if all revision names get matched even if invalid, and let them fail elsewhere rather than silently be skipped (doesn’t help if the revision doesn’t exist, but that is a different problem…).

What would you prefer for this PR? I can leave it as-is and add a test, or change back to the original pattern and just add -.

@jieyouxu

Copy link
Copy Markdown
Member

Having - in revision names seems reasonably common, just based on a quick search https://github.com/search?q=repo%3Arust-lang%2Frust+%2F%40.*revisions%3A.*-%2F&type=code, so it seems like that should work without any special mangling. I just changed the regex to match everything so if all revision names get matched even if invalid, and let them fail elsewhere rather than silently be skipped (doesn’t help if the revision doesn’t exist, but that is a different problem…).

What would you prefer for this PR? I can leave it as-is and add a test, or change back to the original pattern and just add -.

Huh, I suppose then it does work with -s. In that case, let's respect the existing accepted revision names (alphanumeric + _ + -), but just update the error annotation regex to accept _ and - in addition to alphanumeric characters only (and not any character). Could you also enforce that revision names can only contain alphanumeric + _ + -, and report an error if revision names don't match the regex? A test is also desirable to verify our behavior here.

@jieyouxu

Copy link
Copy Markdown
Member

But otherwise it seems like an oversight that revisions accept - but not the error annotations (lol). What we really should do in compiletest (in the long term) is to have the revisions parsing be strict about what revision names it accept. Thanks for the fix, helping compiletest be more self-consistent is very helpful.

@tgross35
tgross35force-pushed the testsuite-multi-rev-regex branch from 4b4673c to 23fea67CompareApril 19, 2024 18:57
@tgross35

Copy link
Copy Markdown
MemberAuthor

I changed this PR to just do the simplest thing and match - in addition to what was there before. I plan on refactoring #123882, which I think would be a better place to check that the name is valid.

Since there is no new rejecting behavior, is this okay as-is or do you want a test still? (if so, what would that look like - just a dummy UI test?)

@tgross35tgross35 changed the title Match any characters in multi-revision comment matchersMatch hyphen in multi-revision comment matchersApr 19, 2024
@jieyouxu

jieyouxu commented Apr 19, 2024

Copy link
Copy Markdown
Member

Since there is no new rejecting behavior, is this okay as-is or do you want a test still? (if so, what would that look like - just a dummy UI test?)

Could you still add a unit test for parse_expected checking that a revision with a name containing - is accepted? I realize that uhh the error reporting here is less than ideal, but for the time being having a regression test might remind future reworks in terms of what revision names can be accepted.

@tgross35

Copy link
Copy Markdown
MemberAuthor

Alrightie I added a simple test, let me know if that looks fine.

@tgross35
tgross35force-pushed the testsuite-multi-rev-regex branch from 23fea67 to feec066CompareApril 19, 2024 19:45
@tgross35

Copy link
Copy Markdown
MemberAuthor

Tidy failed so it didn't push before, should be up to date now

@rust-log-analyzer

This comment has been minimized.

Currently, the matcher `//[rev-foo,rev-bar]~` does not get selected by
the regex. Change the matcher to also match strings that contain a `-`.h
@tgross35
tgross35force-pushed the testsuite-multi-rev-regex branch from feec066 to 282488cCompareApril 19, 2024 20:03
@jieyouxu

Copy link
Copy Markdown
Member

Looks good to me, thanks for the patch!

@bors r+ rollup

@bors

bors commented Apr 19, 2024

Copy link
Copy Markdown
Collaborator

📌 Commit 282488c has been approved by jieyouxu

It is now in the queue for this repository.

@borsbors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 19, 2024
@tgross35

Copy link
Copy Markdown
MemberAuthor

Thanks!

bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 20, 2024
…iaskrgr
Rollup of 3 pull requests
Successful merges:
- rust-lang#123409 (Implement Modified Condition/Decision Coverage)
- rust-lang#124104 (Fix capturing duplicated lifetimes via parent in `precise_captures` (`impl use<'...>`))
- rust-lang#124137 (Match hyphen in multi-revision comment matchers)
r? `@ghost`
`@rustbot` modify labels: rollup
@bors
bors merged commit f58ef08 into rust-lang:masterApr 20, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Apr 20, 2024
Rollup merge of rust-lang#124137 - tgross35:testsuite-multi-rev-regex, r=jieyouxu
Match hyphen in multi-revision comment matchers
Currently, the matcher `//[rev-foo,rev-bar]~` does not get selected by the regex. Change the matcher to include `-`.
@rustbotrustbot added this to the 1.79.0 milestone Apr 20, 2024
@michaelwoerister

Copy link
Copy Markdown
Member

Thanks for getting this fixed, everyone!

@tgross35
tgross35 deleted the testsuite-multi-rev-regex branch July 21, 2024 07:14
cmbays added a commit to breezy-bays-labs/mokumo that referenced this pull request May 4, 2026
Adds the CI job that runs the per-handler branch-coverage producer
introduced in earlier waves and feeds its artifact into
scorecard-aggregate.
Side-car design (data layer + workflow stay in lockstep):
- Pinned nightly channel from rust-nightly-coverage-toolchain.toml
via scripts/nightly-coverage-channel.sh — the rest of the repo
stays on stable. Removal path is one PR (toml + script + this job
+ the moon task) when rust-lang/rust#124137 stabilizes.
- continue-on-error: true mirrors quality.toml's
coverage_handler.report_only = true: nightly outage surfaces in
the CI UI and the scorecard pending-note, never blocks the merge
queue. Promotion to a hard gate is two paired edits.
- scorecard-aggregate consumes the artifact via download-artifact
with continue-on-error so a missing file falls through to the
renderer's "producer pending" pathway.
Reuses existing tools.toml pins (cargo-nextest@0.9.133,
cargo-llvm-cov@0.8.5) — no new pins needed. coverage-handlers is
intentionally NOT in verdict's needs[]: matches the existing
convention for continue-on-error jobs (desktop-e2e, crap-delta).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmbays added a commit to breezy-bays-labs/mokumo that referenced this pull request May 6, 2026
…tly side-car (#805)
* chore(coverage): #583 — pin nightly-only side car for branch-coverage producer
Branch-level coverage instrumentation (-Zcoverage-options=branch surfaced via
cargo llvm-cov --branch) is nightly-only as of rustc 1.97 (rust-lang/rust
#124137). The repo's stable 1.95.0 pin makes per-handler branch coverage —
the signal Row::CoverageDelta.breakouts.handlers[].branch_coverage_pct names
in the V4 schema — unobtainable without a nightly side channel.
Rather than override rust-toolchain.toml (which would bleed nightly into
every other task), this commit lands the date-pinned channel in a separate
file consumed only by:
- the new shop:coverage-branches moon task (--branch on nightly, output
coverage-branches.json — separate artifact from coverage.json so the
CRAP gate path stays on stable),
- the coverage-handlers CI job that will follow.
Single source of truth: rust-nightly-coverage-toolchain.toml. Both consumers
read it through scripts/nightly-coverage-channel.sh, so a pin bump is a
single-file edit. The rationale and the four-step removal path (delete file
+ task + script + CI job; add --branch to the existing coverage task) are
documented in the toolchain file's prologue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(coverage): #583 — coverage-breakouts producer (route walker + LLVM JSON join)
Lifts per-handler branch coverage out of "(producer pending — see #583)"
sentinel territory into a real artifact the scorecard aggregator can
consume. New `coverage-breakouts` bin under `tools/docs-gen/`:
- syn-based route walker (tools/docs-gen/src/coverage/route_walker.rs)
resolves `(method, url_path, handler_rust_path)` from `.route()` and
`.nest()` calls. Handles fully-qualified handler refs (`crate::x::y`),
bare idents resolved via in-file `use` items (with alias support), and
inline-anonymous nested routers. Records unresolvable findings rather
than silently dropping them.
- LLVM coverage JSON parser (tools/docs-gen/src/coverage/llvm_cov.rs)
decodes the `cargo llvm-cov --branch --json` payload (3.x schema as
emitted on nightly), demangles function names via `rustc-demangle`,
and computes branch coverage per LLVM convention: count = 2 ×
branches.len(), covered = sum of (true>0) + (false>0). Partially-
covered branches (one side hit) register as 1-of-2 — exactly the
negative-path signal Quinn's blind-spot 3 wants surfaced.
- Producer (tools/docs-gen/src/coverage/producer.rs) discovers crates
from crates/*/Cargo.toml + apps/*/Cargo.toml, applies the same
exclusion ledger as crap4rs.toml, joins routes with coverage on Rust
path equality, and emits CoverageBreakoutArtifact JSON. Diagnostics
surface every unresolved-handler and unresolvable-route with file +
line so the operator can fix root cause; the bin exits with code 2
when any are present (artifact still written for inspection).
- Bin shim (tools/docs-gen/src/bin/coverage-breakouts.rs) is CC=1 by
the same logic as lcov-dedup and adr-validate: coverage credit lands
on the library, the bin is a thin arg parser.
Wire schema unchanged. The producer-internal HandlerArtifactEntry
carries rust_path, branch counts, and function_count for diagnostics;
the aggregator (next commit) translates these into the already-defined
HandlerBreakout wire shape (handler + branch_coverage_pct).
Smoke-tested end-to-end against a partial coverage payload (scorecard
crate only): walker discovers 55 routes across kikan + mokumo-shop,
all resolve to coherent Rust paths, and the producer correctly reports
all 55 as unresolved (no kikan/mokumo-shop coverage in the partial
payload) — the exact failure mode CI will hit if a handler is renamed
without the coverage rerun.
Tests: 34 unit tests across artifact, llvm_cov, route_walker,
crap_exclusions, producer (epoch math + crate discovery). Real-fixture
test in llvm_cov is opt-in (skipped when fixture absent so a fresh
checkout still passes).
Also tightens .gitignore — re-includes tools/docs-gen/src/coverage/
since the global `coverage/` rule (for runtime artifacts) was hiding
the producer source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(scorecard): #583 — wire per-handler breakouts producer artifact into aggregator + threshold gate
Closes the second leg of #583: the aggregator now reads the
`coverage-breakouts.json` producer artifact, populates
`Row::CoverageDelta.breakouts.by_crate[].handlers[]` with `(route,
branch_coverage_pct)` pairs, and runs them through a configurable
threshold gate.
Producer artifact reader: new `crates/scorecard/src/coverage_breakouts.rs`
mirrors the wire types from the producer (under `tools/docs-gen/src/
coverage/artifact.rs`) so the aggregator doesn't take a build-time dep
on docs-gen. Versioned (`ARTIFACT_VERSION_MAX = 1`); a higher-version
artifact is a hard parse error so a producer/consumer skew surfaces
loudly. The wire translation drops producer-internal fields
(`rust_path`, branch counts, `function_count`) — the renderer only
needs route + percentage. Uses `Result<T, String>` rather than `anyhow`
so the scorecard lib's deps-zero-by-default invariant holds.
CLI: `scorecard aggregate` gains `--coverage-breakouts-json <PATH>`.
Optional — when omitted (or the file is missing), the drill-down stays
empty and the existing `(per-handler producer pending — see #583)`
note path still applies.
Threshold gate: new `[rows.coverage_handler]` table in `quality.toml`.
- `warn_pct_below` (default 60.0): handler at-or-below resolves Yellow.
- `fail_pct_below` (default 40.0): handler at-or-below resolves Red.
- `report_only` (default true): when true, gate verdict is informational
only — does not escalate row status. Defaults to true so a nightly-
toolchain outage on the producer side cannot block the merge queue.
Operators flip to false in `quality.toml` once the side-channel's
reliability is established.
Verdict logic: handler-floor verdict = worst-of across all handlers
(one Red handler drives the row Red). Final row status = max(delta-
driven status, handler verdict) when `report_only = false`. When Red
fires from the handler path, `failure_detail_md` names up to 5
offending handlers + their pcts so the operator sees the actionable
signal without expanding the drill-down.
Wire schema: unchanged (the existing `Breakouts.by_crate[].handlers[]`
shape is what we populate). Operator-config schema bumps the
`additionalProperties:false` count from 7 to 8 (new
`CoverageHandlerThresholds`). Regenerated
`.config/scorecard/quality.config.schema.json` via
`scorecard emit-schema`.
Tests: 5 new aggregate-level tests pin breakouts population, the
report_only-true default no-escalation path, the enforce-mode Red,
the warn-floor Yellow, and the missing-input empty-drill-down path.
6 new coverage_breakouts unit tests pin parse/round-trip, version
rejection, wire translation, iter_handler_pcts, and diagnostics
classification. 256 lib tests pass total.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(scorecard): #583 — render per-handler branch coverage drill-down
Closes the third leg of #583. The sticky-comment renderer now surfaces
the breakouts the aggregator populates (Wave C) inside a collapsible
`<details>` block beneath the `Row::CoverageDelta` row.
Layout:
- Handlers grouped by crate (matches the wire shape's `by_crate[]`).
- Within each crate, sorted ascending by `branch_coverage_pct` so the
most-uncovered handlers float to the top — operator's eye is drawn
to the actionable end first.
- Each handler row carries a small icon (🔴 ≤40%, 🟡 ≤60%, 🟢 above)
matching the `[rows.coverage_handler]` quality.toml defaults. The
numbers live in named constants (`HANDLER_FAIL_PCT`,
`HANDLER_WARN_PCT`) so a future "render thresholds from artifact"
upgrade has one grep target. Operators tuning the gate threshold
will see slight cosmetic drift between icons and verdict — that's
acceptable; the row's status is the authoritative signal.
Producer-pending fallback: when the artifact carries no handlers (no
`--coverage-breakouts-json` supplied to the aggregator, or producer
artifact is empty), the renderer emits an inline italic note linking
to issue #583 — wires the existing `(per-handler producer pending —
see #583)` documented behavior that types.d.ts already promised but
the renderer hadn't implemented yet.
Tests: 13 new vitest cases pin (a) the threshold constants matching
quality.toml defaults, (b) the empty/undefined breakouts → pending
note path, (c) the populated → details block path, (d) ascending
sort by pct, (e) icon mapping at the inclusive boundaries (40.0 →
🔴, 60.0 → 🟡), (f) per-crate grouping with multiple crates, (g)
singular nouns at count=1, (h) integration through
renderScorecardMarkdown. 78 vitest cases pass total across the
scorecard renderer suite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(scorecard): #583 — coverage-handlers nightly side-car job
Adds the CI job that runs the per-handler branch-coverage producer
introduced in earlier waves and feeds its artifact into
scorecard-aggregate.
Side-car design (data layer + workflow stay in lockstep):
- Pinned nightly channel from rust-nightly-coverage-toolchain.toml
via scripts/nightly-coverage-channel.sh — the rest of the repo
stays on stable. Removal path is one PR (toml + script + this job
+ the moon task) when rust-lang/rust#124137 stabilizes.
- continue-on-error: true mirrors quality.toml's
coverage_handler.report_only = true: nightly outage surfaces in
the CI UI and the scorecard pending-note, never blocks the merge
queue. Promotion to a hard gate is two paired edits.
- scorecard-aggregate consumes the artifact via download-artifact
with continue-on-error so a missing file falls through to the
renderer's "producer pending" pathway.
Reuses existing tools.toml pins (cargo-nextest@0.9.133,
cargo-llvm-cov@0.8.5) — no new pins needed. coverage-handlers is
intentionally NOT in verdict's needs[]: matches the existing
convention for continue-on-error jobs (desktop-e2e, crap-delta).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(scorecard): #583 — handler-coverage BDD specs + nightly side-car notes
Wave 6 (BDD): four @future scenarios under a new "Per-handler
drill-down" rule in scorecard_display.feature documenting the V4.1
contract — populated breakouts render a per-crate <details> block
sorted ascending, empty breakouts render a producer-pending note,
worst-of-handler thresholds escalate the row only when report_only
is disabled, and the report-only default keeps the gate purely
informational. @future-tagged so bdd-lint --exclude-tags treats them
as documented placeholders ahead of step-def implementation.
Wave 7 (docs):
- COVERAGE.md gains a "Per-handler branch coverage (mokumo#583)"
section walking the three pipeline stages (capture → producer →
aggregator + threshold gate), the non-blocking design, the two
paired edits to promote to a hard gate, and the one-PR removal
path when branch coverage stabilizes on rustc.
- CHANGELOG.md "Unreleased / Added" entry for V4.1 — schema
additive (no schema_version bump), new operator surface
([rows.coverage_handler] in quality.toml), new CI job
(coverage-handlers consuming pinned nightly).
- AGENTS.md §Dep-graph and verdict assertions gains a "Nightly
toolchain side-cars" callout codifying the side-car convention
used by mokumo#583 so future nightly-only features follow the
same registry pattern.
- quality.toml gains a documented [rows.coverage_handler] section
with the M00 60% / 40% defaults so operators can see and tune the
handler thresholds without diving into Rust source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(coverage-breakouts): #583 — resolve sibling-module handler refs
The route walker mis-resolved multi-segment handler refs whose head
named a sibling module declared in the same file. Smoke-running the
producer against the real workspace surfaced six kikan auth handlers
collapsing to literal `login::login` / `me::me` / `logout::logout` /
`recover::recover_*` instead of the fully-qualified
`kikan::platform::v1::auth::<sub>::<fn>` paths the LLVM coverage
artifact carries — the join in producer::run() then logged them as
`unresolved_handlers` and dropped them from the wire breakouts.
Root cause: `FileVisitor::resolve_path()` had three branches —
`use_map` hit, single-ident module-local, and multi-segment "external
crate". The third branch fired for `pub mod login; ... .post(login::login)`
patterns because `login` matched neither a `use` import nor was a
single-ident.
Fix: track sibling module declarations via a new `module_decls:
HashSet<String>` field populated from `visit_item_mod` (handles both
`mod foo;` and inline `mod foo { ... }`). In `resolve_path()`, a
multi-segment head whose value is in `module_decls` now resolves under
`<file_module_path>::<head>::<tail>` — the same shape as the
single-ident module-local branch, just with a tail.
Pinning test: `resolves_sibling_module_handler_via_mod_decl` writes a
two-file fixture mimicking the real auth/mod.rs + auth/login.rs layout
and asserts the resolved path is `demo::auth::login::login`. Re-running
the producer against the workspace post-fix shows the auth handlers
correctly resolved (still on the unresolved list because the synthetic
LLVM JSON only has one function — the path is now correct, the join
key just has nothing to join against until real coverage data lands).
The "external-crate qualified" fallback branch is preserved for
genuinely external multi-segment references (e.g. `axum::Router`); no
existing walker test relied on the previous behavior for sibling-mod
patterns.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* quality(coverage): #583 — bring crap-rust gate green for coverage-breakouts surface
CI surfaced 7 functions over CRAP threshold 15 (worst 272.0) — all in
the new producer surface added by this PR. Two compounding causes:
1. `bin/coverage-breakouts::main` and `coverage::producer::run` reported
0% coverage because cargo-llvm-cov nextest doesn't credit `src/bin/`
code, and `run()` had no end-to-end test exercising it through the
library — the CRAP formula `CC + CC²·(1-cov)³` exploded.
2. Five route-walker / producer functions had high CC (22..31) from
tightly-packed match arms and let-else early returns.
Fixes:
- New `docs_gen::coverage_breakouts` lib module mirroring `validate.rs`:
`parse_args` / `Args` / `execute` with 13 unit tests covering CLI
parse + happy/error execute paths. Bin shrinks to a 4-line shim.
- Three end-to-end `run()` tests in `producer.rs` building tempdir
workspaces with synthetic LLVM JSON: happy join, unresolved-handler
diagnostic, and crap4rs.toml exclusion path. Coverage on `run` jumps
0% to 93%, dropping CRAP 272 to 16.
- `route_walker::collect_method_router` (CC 31) split into
`head_call_method_handler` + `chain_call_method_handler` per chain
position.
- `FileVisitor::resolve_path` (CC 22) dispatches keyword heads
(`crate` / `self` / `super`) to a `join_path(prefix, tail)` helper
and routes the non-keyword case to `resolve_other_head`.
- `route_walker::walk` (CC 29) decomposes into `parse_crate_sources` /
`scan_src_tree` / `visit_one_file` / `emit_routes` over a
`ParsedSources` accumulator.
- `route_walker::file_module_path` (CC 15) delegates per-component
classification to `file_path_segment`.
- `producer::discover_crates` (CC 23) splits into
`scan_subdir_for_packages` + `read_package_name`.
- `producer::run` (CC 16) decomposes into `gather_walker_inputs` /
`join_routes_with_coverage` / `join_one_crate` / `lift_unresolvable`.
Local crap4rs against tools/docs-gen drops 7 to 0 above threshold; worst
14.4 (PASS). All 143 docs-gen lib tests pass.
The coverage-handlers job's separate failure (LLVM SIGSEGV in
`getInstantiationGroups` on nightly-2026-04-15) is upstream nightly LLVM
and already correctly soft-gated (`continue-on-error: true` + not in
verdict's `needs:`), so it doesn't block the rollup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(coverage): #583 — narrow coverage-branches scope + bump nightly to dodge llvm-cov SIGSEGV
The `coverage-handlers` job's previous run crashed during the post-test
merge step: `llvm-cov export` SIGSEGV in
`llvm::coverage::CoverageMapping::getInstantiationGroups` (LLVM 22.1
shipped with `nightly-2026-04-15`). All 968 tests passed; the bug is
upstream in the instrumentation pipeline, not in our code.
Two cheap mitigations:
1. Bump the pinned nightly to `nightly-2026-05-01`. Rust's bundled LLVM
rolls weekly and the bug may be already fixed in a newer rev.
2. Replace `--workspace --exclude {mokumo-desktop,kikan-tauri,kikan-admin-ui}`
with `-p kikan -p kikan-cli -p kikan-spa-sveltekit -p mokumo-shop` —
only crates that register `Router::route(...)` / `.nest(...)` need
instrumentation for the producer's per-handler join. Drops the count
of `-object` files llvm-cov merges from 11 to 4, narrowing the surface
for the buggy code path. The route walker still walks every crate
(it's a source-level scan, independent of coverage); coverage-less
crates simply contribute zero handler lookups, which the producer
already handles cleanly.
If both fail to clear the bug, `coverage-handlers` stays soft-gated
(`continue-on-error: true`, not in verdict's `needs:`) and the rollup
remains green — same containment we already have.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* review(coverage): #583 — address CodeRabbit feedback on PR #805
Addresses 10 of the 12 review comments. Skips:
- moon.yml --exclude annotation request: stale, the prior commit
(33d71a3) replaced --exclude flags with positive -p selection.
- route_walker multi-mount prefix fan-out: real bug but heavy lift
(HashMap value to Vec, DFS over parent_of, multi-prefix emit). Mokumo's
current Axum topology mounts each builder fn at most once so the bug
is latent; deferred to a follow-up issue rather than expanding this
PR's scope further.
Fixes:
- Branchless handlers report 100.0% (coverage::llvm_cov,
coverage::artifact). A handler with no conditionals is vacuously
fully covered; encoding the empty case as 0.0 would trip the
worst-of [rows.coverage_handler] gate against handlers with
nothing to cover.
- Producer exit_code follows the documented 0/2 contract
(coverage::producer::run). Previously returned 0/1 internally and
coverage_breakouts::execute re-coerced 1->2; library callers now get
the right value directly without the translation layer.
- Cargo package names preserved in artifact (coverage::producer,
coverage::route_walker). The walker now carries both the package
name (e.g. mokumo-shop, used in RouteEntry.crate_name and the
artifact's by_crate[].crate_name) and the Rust ident form
(mokumo_shop, used for path resolution against demangled symbols).
- Inline .nest() targets walked once
(coverage::route_walker::FileVisitor::visit_expr_method_call).
handle_nest_call already recurses into target_expr with the prefix
on the inline-prefix stack; falling through to the default visitor
walked it a second time without the prefix and emitted duplicate
routes. Now we descend into the receiver chain explicitly and skip
the default args walk for the nest case.
- Yellow handler downgrade carries actionable detail
(Row::coverage_delta_yellow, aggregate::handler_yellow_failure_detail).
Yellow constructor now takes Option<String>; when the per-handler
gate causes the downgrade (!report_only and handler_status == Yellow),
the row carries a detail naming the offending handlers in the
(warn, fail] band. Plain delta-driven Yellow stays detail-less.
- Offender count corrected after take(5)
(aggregate::coverage_red_failure_detail). Previously counted
post-take so any breakout >5 reported "5 handler(s)"; now totals
before truncation and appends "and N more" when applicable.
- coverage_handler thresholds validated at config-load
(aggregate::validate_threshold_config). NaN/Inf, out-of-range
([0, 100]), and inverted (fail > warn, making Yellow unreachable)
configs now fail loud with structured error messages — same
treatment the other threshold rows already get.
- JSON schema constrains pct_below thresholds to [0, 100]
(threshold::CoverageHandlerThresholds schemars annotation +
regenerated quality.config.schema.json). The config validator above
enforces it at load; the JSON schema enforces it for ajv at the
schema-drift gate.
- Nightly side-car continue-on-error: true carries the required
tracked: annotation (.github/workflows/quality.yml). Per the
exclusion-tracking contract added in #740, deliberate exclusion
sites need an inline tracked: <repo>#<n> — <reason> marker.
- awk field separator includes single quotes
(scripts/nightly-coverage-channel.sh). Inline doc claimed
quote-tolerance for both ' and " but the awk -F only included ";
a single-quoted channel would have leaked the surrounding quotes.
Local crap4rs against tools/docs-gen stays at 0 above threshold; all
404 docs-gen + scorecard tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* quality(scorecard): #583 — decompose validate_threshold_config
Extracted four per-section helpers — validate_coverage_section,
validate_count_sections, validate_ci_wall_clock_section,
validate_coverage_handler_section — so the entry function is a
linear orchestrator (CC=5, CRAP=5.0) instead of carrying every
section's branch fan-out (CC=15, CRAP=17.15 on the previous
revision after the coverage_handler validation block was added).
Largest helper post-split is CC=6 / CRAP=13.15, comfortably under
the threshold of 15. Behaviour is identical; the same error
strings are returned for the same inputs, validated by 256 lib
tests still passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* review(coverage): #583 — address CodeRabbit second-pass feedback
Six fixes on revision e387ad9 — five correctness-leaning rustdoc /
behaviour gaps and one boundary test set that pins the new resolver:
1. route_walker.rs: visit_one_file no longer silently skips files whose
`syn::parse_file` errors. Every `.rs` under a crate's `src/` is
expected to be valid Rust; a silent skip would let the producer
emit a partial artifact under exit-0 cover — exactly the drift the
producer's loud-by-design diagnostics exist to surface.
2. llvm_cov.rs: module docs updated to (a) note `parse_str` accepts
schema 2.x and 3.x, not just 2.x, and (b) document the 100%
branchless rule on `branch_coverage_pct` so callers reading the
rustdoc don't trip on the old "0.0% when total == 0" claim.
3. artifact.rs: doc on `CrateHandlerSet::crate_name` now states the
field carries the Cargo package name (`mokumo-shop`) — matching
the producer's pass-through behaviour after the previous round —
instead of the stale Rust-ident form (`mokumo_shop`).
4. nightly-coverage-channel.sh: `awk` regex now tolerates indented
` channel = "..."` keys and matches the value by `nightly-`
prefix rather than fixed field index, so a TOML formatter that
indents under `[toolchain]` doesn't silently fail the side-car.
5. aggregate.rs: `read_coverage_breakouts` no longer returns
`Ok(None)` for an explicit `--coverage-breakouts-json <missing>`.
Operator-supplied paths now fail loud, matching the contract of
`read_pr_meta`, `read_ci_wall_clock_json`, and the rest of the
reader family. A path typo on a CI invocation will surface
instead of silently degrading the verdict to a pending stub.
Pinned by two new regression tests.
6. threshold.rs: 12 boundary tests for `resolve_coverage_handler`,
the only resolver here that lacked them. Covers empty input,
each side of warn / fail (using `f64::next_up()` for the
smallest representable step at the boundary magnitude), the
worst-of fold's promotion of one-Red over Greens, and the
short-circuit-on-Red contract via a NaN-after-Red probe.
Test deltas: scorecard lib 256 → 270 passing; clippy clean across
both crates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Christopher Bays <cmbays@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-testsuiteArea: The testsuite used to check the correctness of rustcS-waiting-on-borsStatus: Waiting on bors to run and complete tests. Bors will change the label on completion.T-bootstrapRelevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@tgross35@rustbot@compiler-errors@michaelwoerister@jieyouxu@rust-log-analyzer@bors