Summary
Two first-party sources end with a self-include of their own generated .moc
file. The clang-tidy-diff job configures but deliberately never builds, so
that .moc does not exist when clang-tidy runs — and the resulting
clang-diagnostic-error is not filtered out by clang-tidy's -line-filter.
The consequence: the first PR that changes any line in either file gets a red
clang-tidy-diff naming an error that has nothing to do with the change, and
there is no line the contributor can edit to make it go away.
The two files:
$ grep -rn '\.moc"' examples src tests include
examples/common/testkit/test_qml_surface.cpp:756:#include "test_qml_surface.moc"
src/qt/forms/tests/tst_main.cpp:69:#include "tst_main.moc"
Verification status: reproduced
On a020e69c, clang-tidy 22.1.8, against a build/clang-debug configured with
the CI clang-tidy job's exact option set (.github/workflows/ci.yml, step
"Configure (generates compile_commands.json over every optional feature)") and
not built — the same state that job is in when it lints.
Run with the job's own extra-args, plus a -line-filter that restricts analysis
to a single line the diagnostic is nowhere near:
$ clang-tidy -p build/clang-debug --quiet \
--extra-arg=-std=c++23 --extra-arg=-Wno-missing-include-dirs \
-line-filter='[{"name":"test_qml_surface.cpp","lines":[[10,10]]}]' \
examples/common/testkit/test_qml_surface.cpp
Error while processing .../examples/common/testkit/test_qml_surface.cpp.
.../examples/common/testkit/test_qml_surface.cpp:756:10: error: 'test_qml_surface.moc' file not found [clang-diagnostic-error]
756 | #include "test_qml_surface.moc"
| ^~~~~~~~~~~~~~~~~~~~~~
exit=1
The line filter does not suppress it and clang-tidy exits 1. That is the
whole finding: clang-tidy-diff.py passes exactly such a filter and propagates
exactly that exit code (which a6f4e27c / #482 deliberately made fatal), so a
one-character change to line 10 of that file fails the gate.
Found during a full-database clang-tidy sweep for #580. Across all 695
translation units in that database, ten produced 'X' file not found; eight are
generated QML plugin stubs under build/ that no diff can ever name, and these
two are first-party sources under examples/ and src/.
Why -Wno-missing-include-dirs does not cover it
The Configure step's comment explains that flag as handling AUTOMOC's
<target>_autogen/include directories not existing after a configure-only run.
That is correct as far as it goes — it suppresses the missing directory
warning. It does not make the file appear, and a #include "x.moc" still
fails.
The same comment then asserts:
and no source under examples/, include/ or src/ includes a generated moc_/ui_
header (the "Application ladder" job's own "Check no generated moc include
ascends" step is the standing guard on that).
That claim is about the moc_*.h / ui_*.h prefix form, which
scripts/check_automoc_includes.sh does guard. The AUTOMOC bottom-of-file
#include "<basename>.moc" form is a different spelling and is not covered by
that script — so the comment's conclusion ("suppressing it is sufficient, not
merely convenient") is true for the directories and false for these two files.
Why nobody has hit it
Neither file has been touched since the gate acquired the database that reaches
them:
$ git log --oneline -2 -- examples/common/testkit/test_qml_surface.cpp
4ee7b819 style: clang-format the two new testkit test files
b54854e8 ladder: audit examples/common's coverage number, and assert what it was counting
$ git log --oneline -2 -- src/qt/forms/tests/tst_main.cpp
dd5b57b9 forms: pin the two x-rules evaluators to one shared corpus, and let the renderer honour a decorated instance range (#292)
d2cb3ae1 Production-hardening + GUI enhancement program (28 items) + 2 security fixes (#19)
All four predate 9f8b0bc0 ("ci: give the clang-tidy gate the application
ladder it claims to lint (#483)"). The trap is latent, not firing — which is
why it is worth recording now rather than after it costs someone an afternoon.
Not verified
- Not reproduced in CI. I did not push a branch touching either file to
watch the job go red. The reproduction above is local, on a tree in the same
configure-only state, with the job's own flags and a representative
-line-filter; I did not drive clang-tidy-diff.py itself end to end.
- Not established which fix is right. At least four are plausible and I did
not choose between them:
- run AUTOMOC for the two targets before the lint step (partial build,
costs the job its "no build" property);
- add
-regex / a skip list to clang-tidy-diff.py for these two paths
(cheap, but re-introduces exactly the exclusion #483 deleted, and is
silent when a third such file appears);
- extend
scripts/check_automoc_includes.sh to also forbid the
#include "x.moc" form and restructure the two files to a separate moc'd
header (fixes it by construction, makes the guard true as written);
- accept it and document it.
- Not checked on Windows or macOS. AUTOMOC behaves the same in principle;
the clang-tidy job is Linux-only, so this is the configuration that matters.
What would change the verdict
Close this when a PR that edits a line in either file passes clang-tidy-diff
— demonstrated by the job going green on such a PR, not by reasoning about the
fix. Re-open if a third source acquires a #include "*.moc", which nothing
currently prevents.
Related
Found while measuring #580 (full-tree clang-tidy census). Distinct mechanism:
#580 is about header findings the gate cannot see; this is about a gate
failure with no relationship to the diff at all. Deliberately not folded into
that measurement.
Summary
Two first-party sources end with a self-include of their own generated
.mocfile. The
clang-tidy-diffjob configures but deliberately never builds, sothat
.mocdoes not exist when clang-tidy runs — and the resultingclang-diagnostic-erroris not filtered out by clang-tidy's-line-filter.The consequence: the first PR that changes any line in either file gets a red
clang-tidy-diffnaming an error that has nothing to do with the change, andthere is no line the contributor can edit to make it go away.
The two files:
Verification status: reproduced
On
a020e69c, clang-tidy 22.1.8, against abuild/clang-debugconfigured withthe CI clang-tidy job's exact option set (
.github/workflows/ci.yml, step"Configure (generates compile_commands.json over every optional feature)") and
not built — the same state that job is in when it lints.
Run with the job's own extra-args, plus a
-line-filterthat restricts analysisto a single line the diagnostic is nowhere near:
The line filter does not suppress it and clang-tidy exits 1. That is the
whole finding:
clang-tidy-diff.pypasses exactly such a filter and propagatesexactly that exit code (which
a6f4e27c/ #482 deliberately made fatal), so aone-character change to line 10 of that file fails the gate.
Found during a full-database clang-tidy sweep for #580. Across all 695
translation units in that database, ten produced
'X' file not found; eight aregenerated QML plugin stubs under
build/that no diff can ever name, and thesetwo are first-party sources under
examples/andsrc/.Why
-Wno-missing-include-dirsdoes not cover itThe Configure step's comment explains that flag as handling AUTOMOC's
<target>_autogen/includedirectories not existing after a configure-only run.That is correct as far as it goes — it suppresses the missing directory
warning. It does not make the file appear, and a
#include "x.moc"stillfails.
The same comment then asserts:
That claim is about the
moc_*.h/ui_*.hprefix form, whichscripts/check_automoc_includes.shdoes guard. The AUTOMOC bottom-of-file#include "<basename>.moc"form is a different spelling and is not covered bythat script — so the comment's conclusion ("suppressing it is sufficient, not
merely convenient") is true for the directories and false for these two files.
Why nobody has hit it
Neither file has been touched since the gate acquired the database that reaches
them:
All four predate
9f8b0bc0("ci: give the clang-tidy gate the applicationladder it claims to lint (#483)"). The trap is latent, not firing — which is
why it is worth recording now rather than after it costs someone an afternoon.
Not verified
watch the job go red. The reproduction above is local, on a tree in the same
configure-only state, with the job's own flags and a representative
-line-filter; I did not driveclang-tidy-diff.pyitself end to end.not choose between them:
costs the job its "no build" property);
-regex/ a skip list toclang-tidy-diff.pyfor these two paths(cheap, but re-introduces exactly the exclusion
#483deleted, and issilent when a third such file appears);
scripts/check_automoc_includes.shto also forbid the#include "x.moc"form and restructure the two files to a separate moc'dheader (fixes it by construction, makes the guard true as written);
the clang-tidy job is Linux-only, so this is the configuration that matters.
What would change the verdict
Close this when a PR that edits a line in either file passes
clang-tidy-diff— demonstrated by the job going green on such a PR, not by reasoning about the
fix. Re-open if a third source acquires a
#include "*.moc", which nothingcurrently prevents.
Related
Found while measuring #580 (full-tree clang-tidy census). Distinct mechanism:
#580 is about header findings the gate cannot see; this is about a gate
failure with no relationship to the diff at all. Deliberately not folded into
that measurement.