Skip to content

clang-tidy-diff fails with a clang-diagnostic-error on any edit to the two sources that self-include their .moc #624

Description

@Yaraslaut

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:
    1. run AUTOMOC for the two targets before the lint step (partial build,
      costs the job its "no build" property);
    2. 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);
    3. 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);
    4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: ciSubsystem: ciarea: qtSubsystem: qtbugSomething isn't workingtriage: validWell-framed; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions