Skip to content

mutate: string content was being offered as constants to perturb - #3256

Merged
gHashTag merged 4 commits into
masterfrom
w107-string-content-as-constants
Sep 5, 2026
Merged

mutate: string content was being offered as constants to perturb#3256
gHashTag merged 4 commits into
masterfrom
w107-string-content-as-constants

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Two defects in the same family. Both measured with the tool itself, against a
binary built from master's own mutate.rs and gates.rs, so the before column
is measured rather than remembered.

1. masked() did not know Rust raw strings

It is deliberately language-agnostic — it runs on a Python oracle, a Verilog
header and a YAML workflow "without knowing which is which". But # opens a
comment two rules down, so r#" read as r followed by a comment to end of
line, and the string's contents came back marked as code:

pub fn real() -> u32 { 7 }
pub const FIXTURE: &str = r#"
    threshold = 12345
    other = 6789
"#;
pub fn also_real() -> u32 { 42 }

4 literal(s) in probe.rs
  probe.rs:1:24   7 -> 8
  probe.rs:4:17   12345 -> 12346      <-- string content
  probe.rs:5:13   6789 -> 6790        <-- string content
  probe.rs:8:29   42 -> 43

Now 2 literal(s): 7 and 42.

2. A column-0 } inside a fixture ended the test module

test_module_lines closes a #[cfg(test)] module at a line that is exactly
}. competitors.rs has one at line 692, inside the raw string const TWO
which opens at 681. Everything after was reported as production — 36 offered
sites below that file's own #[cfg(test)] at line 567
.

Perturbing a test's own arithmetic fails that test, and the red is then read as
"the checker noticed": the tautology drop_test_module_sites exists to
prevent, reached through another door.

Measured

file offered skipped as test sites inside its own test module
competitors.rs 114 → 45 14 → 77 36 → 0
fpga.rs 376 → 376 637 → 617
nine others unchanged unchanged

Mutation

Four mutants, all previously green, now red: the rule never firing · the hash
count ignored · the close searched from the wrong offset · 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. Run on let _ = xr"junk 55 junk"; and br#"bytes 4242 here"#
the tool reports identical mutants either way.

The same guard is load-bearing in raw_string_opens, which has no
ordinary-string rule: without it "cannot occur" reads as an unclosed
raw-string opener and swallows every line after it, #[cfg(test)] included.
Only measurement told the two apart.

Not fixed, and why

types_dup.rs holds const THREE_SPELLINGS: &str = "\ — an ordinary
string continued with a backslash — whose fixture has a column-0 } at line
1115. 29 test-only sites are still offered there.

A parity rule ("an odd number of unescaped quotes opens a string") was written
and reverted after measurement: one unbalanced quote puts the scanner into
string mode permanently, so #[cfg(test)] is never seen again. Across eleven
files it took fpga.rs from 376 offered / 637 skipped to 993 / 0,
prcheck.rs 43 → 122, and made competitors.rs worse (36 → 43 sites inside its
own test module). Left for a real scanner rather than guessed at.

Prior art

cargo-mutants parses with syn and applies edits textually. The documented
failure mode of naive textual mutators is exactly this — a literal "1 + 2"
mutated to "1 - 2", failing a test for the wrong reason — and the stated
minimum is a tokenizer-based skip mask. This tool has one; it was not
Rust-aware.

cargo test -p tri --bin tri: 777 passed, 0 failed.

Refs #3255

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>
@gHashTag
gHashTag enabled auto-merge (squash) September 5, 2026 08:23
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-05 08:23:43 UTC

Summary

Status Count
Total Open PRs 13
PRs with Failing Checks 11
PRs with All Checks Green 2
READY 0
FAILING 11
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=b52fccebfa30 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

…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
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-05 08:32:43 UTC

Summary

Status Count
Total Open PRs 12
PRs with Failing Checks 12
PRs with All Checks Green 0
READY 0
FAILING 12
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=b52fccebfa30 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-09-05 08:56:32 UTC

Summary

Status Count
Total Open PRs 12
PRs with Failing Checks 11
PRs with All Checks Green 1
READY 0
FAILING 11
PENDING 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=b52fccebfa30 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

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