The suppression asked whether a depfile flag appeared anywhere in the rule's
command text. It was consulted per rule, before the scans were built, so any
word spelling a depfile flag silenced the report for every object the rule
declared — including a word in an invocation that no scan would ever have
been built from.
That made the diagnostic defeatable by placement rather than by meaning, and
the failure was double-silent: no scan, no depfile, and no report that either
was missing. The tool whose one job is to say when headers go unrecorded had a
blind spot a stray word could open.
Both scanners now ask the question over the invocations build_dep_scans draws
from, gated the way build_dep_scans gates them: the invocation must be the
compile itself. Reading only the scannable prefix is not sufficient on its own
— the prefix deliberately continues past scan-transparent invocations so the
walk can reach a later compile, and build_dep_scans then skips exactly those
for want of a compiler index. Without the compiler gate,
echo -MD && gcc -c a.c -o a.o && cp a.o b.o
still answered true: a program that cannot write a depfile suppressing a
compile that carries no flag, leaving b.o uncovered and unreported. The two
predicates now read the identical set of invocations, so they cannot disagree.
The word test moves with it. Matching -M inside the command text needed a
manual check that the preceding character was a space; over tokenized words
that check is the tokenizer's job and is gone, which is what retires the
string_utils include here.
The requirement said "carries no depfile flag anywhere in its text" and now
says "no invocation a scan would have been built from carries a depfile flag",
and the two classes that escaped the report are down to one.
Verified: RED quoted for every leg, including the three second-round tests that
failed against the first fix. The E2E leg authored after the fix was proved
discriminating by restoring the pre-tightening scanner, observing the failure,
and putting it back, verified by sha256. build, format, tidy, iwyu, spec-check
all exit 0; make test is green when carried onto the #397 base and fails on a
main base only at the pre-existing read-only-/tmp failure in test_builder.cpp,
which this change does not touch.
Fixes #357
What was wrong
The unscanned-compile diagnostic exists to say when a compile's headers go unrecorded. Its
suppression — "this command writes its own depfile, so it needs no scan" — asked whether a depfile
flag appeared anywhere in the rule's command text, and was consulted per rule, before any scan
was built.
So the diagnostic was defeatable by placement rather than meaning. Any word spelling a depfile
flag silenced the report for every object the rule declared, and the failure was double-silent: no
scan, no depfile, and no report that either was missing.
The part the issue did not anticipate
Re-spelling the predicate over the scannable prefix closes the trailing and empty-prefix vectors,
but leaves the bug alive in miniature.
scannable_prefixdeliberately continues pastscan-transparent invocations (
echo,true,:) so the walk can reach a later compile;build_dep_scansthen skips exactly those, for want of a compiler index. Reading the prefix alonetherefore still counts a flag that no scan could ever have been built from:
answered "carries a depfile flag" — a program that cannot write a depfile suppressing a compile that
carries no flag.
b.ois left uncovered and unreported.Both predicates now gate on the same condition
build_dep_scansgates on: the invocation must bethe compile itself. The two read the identical set of invocations, so they cannot disagree.
Also in this change
The word test moves from the command text to the tokenized words. Matching
-Minside the textrequired a manual check that the preceding character was a space; over words that is the
tokenizer's job, and dropping it is what retires the
string_utilsinclude here.clang_clgetsthe same shape, so the two scanners state the rule the same way rather than in two spellings.
Spec
REQ-SCAN-REPORT-UNSCANNEDnow reads "no invocation a scan would have been built from carries adepfile flag" in place of "carries no depfile flag anywhere in its text", and the section's "two
classes escape the report" is down to one — the compile-and-link case, which is unrelated and
still open.
Three discharge tests, pinning both directions: a flag outside the scannable prefix suppresses
nothing, a flag inside it still suppresses, and an E2E scenario for the object left uncovered.
Verification
RED quoted for every leg, including the three second-round tests that failed against the first fix.
The E2E leg was authored after that fix, so it was proved discriminating by restoring the
pre-tightening scanner, observing the failure, and putting it back — verified by sha256, with no
git checkoutand no stash.make testOn a
mainbasemake testreports the pre-existingtest_builder.cppfailure from a read-only/tmpon the development machine — the #397 defect, fixed by PR #406, in a file this change doesnot touch. CI runners have a writable
/tmp.Fixes #357