gates: a real scanner, so a continued string is not read as code - #3262
Merged
Merged
Conversation
Two defects, same family, both measured with the tool against master's binary. `masked()` is deliberately language-agnostic -- it runs on a Python oracle, a Verilog header and a YAML workflow. But `#` opens a comment two rules down, so `r#"` was read as `r` followed by a comment to end of line, and the raw string's CONTENTS came back marked as code. A probe holding `pub const FIXTURE: &str = r#"\n threshold = 12345\n other = 6789\n"#;` was reported as "4 literal(s)", offering both fixture numbers as constants. `test_module_lines` ended a `#[cfg(test)]` module at a line that is exactly `}`, and competitors.rs has one inside the raw string `const TWO` (opens 681, the brace at 692). Everything after it was reported as production: 36 offered sites sat below that file's own `#[cfg(test)]` at 567. Perturbing fixture text and reading the red as "the checker noticed" is the tautology `drop_test_module_sites` exists to prevent, through another door. Measured, master's binary vs this one, same eleven files: competitors.rs offered 114 -> 45 skipped 14 -> 77 in-module 36 -> 0 fpga.rs offered 376 -> 376 skipped 637 -> 617 nine others unchanged Four mutants, all previously green, now red: the rule never firing, the hash count ignored, the close searched from the wrong offset, and an unterminated raw string stopping at the quote instead of end of file. Two branches were REMOVED after mutation showed them unexercisable and a probe showed why: a `!prev_is_word` guard and a `br#"` prefix branch, both in `masked()`, where the ordinary-string rule reaches those bytes first and masks the same span. The same guard IS load-bearing in `raw_string_opens`, which has no such rule -- without it `"cannot occur"` reads as an unclosed raw-string opener. Only measurement tells the two apart. Not fixed, reported in the issue: types_dup.rs uses an ORDINARY string continued with a backslash whose fixture has a column-0 brace at 1115, leaving 29 test-only sites offered. A quote-parity rule was written and reverted after measurement -- it took fpga.rs from 376 offered / 637 skipped to 993 / 0. Refs #3255 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ring in the next Records the raw-string pass. The bug is small; three things around it are not. A `!prev_is_word` guard was written into two functions. In `masked()` mutation removed it and everything stayed green -- the ordinary-string rule reaches those bytes first -- so it was deleted. In `raw_string_opens`, a per-line scanner with no such rule ahead of it, the identical check carries the whole case. The same code one function apart is dead in one place and structural in the other, and only measurement separates them. A plausible fix for the ordinary multi-line string -- an odd number of unescaped quotes opens one -- was written and reverted after measurement took fpga.rs from 376 offered / 637 skipped to 993 / 0. And the baseline has to come from the other binary: I recorded gates.rs at 213 from a partially-modified build, then read 221 and chased a regression that did not exist. Master reads 221 too. Refs #3255 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # .claude/skills/ci-gates/SKILL.md
`test_module_lines` used per-line rules, and no line rule can express an ordinary string continued with a trailing backslash. `types_dup.rs` holds `const THREE_SPELLINGS: &str = "\` whose fixture has a column-0 `}` at line 1115: that closed the file's test module and handed 29 test-only literals to the mutator as production constants. Replaces the line rules with `code_mask`, one character pass over line and nested block comments, ordinary strings honouring escapes -- which is what lets them span lines -- raw strings with hash-counted closes, and char literals as distinct from lifetimes. Measured against master's binary, same eleven files: types_dup.rs offered 71 -> 40 skipped 11 -> 42 in-module 29 -> 0 competitors.rs offered 114 -> 45 skipped 14 -> 77 in-module 36 -> 0 nine others unchanged Five mutants killed: the ordinary-string rule never firing, escapes ignored, block comments not nested, the raw close ignoring its hash count, and the caller ignoring the mask. Two controls had to be SHARPENED first -- the originals passed under their mutants because `/* /* 888 */ */` has 888 inside either way and the continuation fixture held no escaped quote. A control that cannot fail is not one. The `!prev_is_word` guard is removed. It was load-bearing one revision ago, in the per-line scanner that had no string rule ahead of it; here the ordinary-string rule consumes `"cannot occur"` whole and the scan never lands on the `r`. Measured across eleven files plus the probe: zero differ. Necessity is a property of the scanner's shape, not of the check. Refs #3255 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # cli/tri/src/gates.rs
gHashTag
enabled auto-merge (squash)
September 5, 2026 09:09
Replacing a line scanner with a character pass, mutation killed only some branches -- and two of the controls were the reason, not the code. `/* /* 888 */ */` cannot prove nesting: 888 is inside either way. It had to become `/* /* x */ 999 */`, digit after the inner close. The escape control named `\` handling but its fixture held no escaped quote, so the string closed at the same byte with or without escapes. Both passed. Both looked like they exercised the branch they named. Also records that the `!prev_is_word` guard, load-bearing one revision ago, is a decoration again now that the per-line scanner it protected is gone -- and that a baseline regenerated from a moving `origin/master` is not a baseline, which cost a diagnosis of a regression that did not exist. Refs #3255 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
PR DashboardGenerated at: 2026-09-05 09:15:00 UTC
Summary
Seal Status
|
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Contributor
PR DashboardGenerated at: 2026-09-05 09:35:34 UTC
Summary
Seal Status
|
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.
Finishes the case #3255 reported and deliberately left unfixed.
test_module_linesused per-line rules, and no line rule can express anordinary string continued with a trailing backslash.
types_dup.rsholdswhose fixture has a column-0
}at line 1115. That closed the file's testmodule, and everything after it was reported as production: 29 test-only
literals handed to the mutator as constants.
What replaces them
code_mask— one character pass over line and nested block comments, ordinarystrings honouring escapes (which is exactly what lets them span lines), raw
strings with hash-counted closes, and char literals kept distinct from
lifetimes.
test_module_linesnow asks it whether a line's first byte is codebefore treating a
}as a close.types_dup.rscompetitors.rsgates.rsreads 251 rather than 221 because this change editsgates.rs—it adds ~120 lines of scanner with 42 literals of its own. Master's own copy of
gates.rs, run under this binary, is unchanged at 221/218.Mutation
Five killed: the ordinary-string rule never firing · escapes ignored · block
comments not nested · the raw close ignoring its hash count · the caller
ignoring the mask.
Two controls had to be sharpened before they could kill anything.
/* /* 888 */ */has 888 inside whether or not nesting is honoured, and thecontinuation fixture contained no escaped quote. Both passed under their
mutants until the digit was moved after the inner close and an escaped quote
added. A control that cannot fail is not a control.
The guard, one last time
#3256 recorded that a
!prev_is_wordcheck was a decoration inmasked()andload-bearing in
raw_string_opens. This change deletesraw_string_opens—
code_masksupersedes it — so the guard is a decoration again and is gone.Measured across eleven files plus the probe: zero differ.
Necessity was never a property of the check; it was a property of the scanner's
shape.
tri gates warningsflagged the orphanedraw_string_opensimmediatelyafter the merge, which is what that command exists for.
cargo test -p tri --bin tri: 779 passed, 0 failed.Refs #3255