Skip to content

Add per-header test files to fix clangd/clang-tidy false diagnostics on untested headers #156

Description

@sean-parent

Summary

Many headers under adobe/ show spurious "file not found" / cascading parse errors in the editor (clangd) when opened directly, even though the project builds cleanly. This also affects running clang-tidy's misc-include-cleaner check per-header to find truly unused includes (a separate but related investigation). Both issues trace back to the same root cause: headers with no dedicated test file have no reliable "donor" compile command, so clangd/clang-tidy fall back to a heuristic that can pick an unrelated file.

Root cause

compile_commands.json only contains entries for real translation units (.cpp files) — headers aren't compiled on their own, so they have no entry. When you open a header directly, clangd (and clang-tidy, and clangd --check) borrows compile flags from another file in the database, chosen by clang::tooling::InterpolatingCompilationDatabase (clang/lib/Tooling/InterpolatingCompilationDatabase.cpp). Per that file's own documentation, it's a point-scoring heuristic:

  • +1 if a candidate's filename stem starts with the query header's stem; +1 more if it's an exact stem match (2 total)
  • +1 for each of the last 2 parent-directory names of the query that also appears among the candidate's last 4 parent directories
  • +1 if the candidate's whole path is a prefix match with the query's directory prefix
  • Ties break on matching language, then longest path-string prefix, then a fixed deterministic order
  • If literally nothing scores any points, it falls back to whichever file has the longest raw path-string prefix (score 0)

With ~330 of the ~420 entries in our compile_commands.json being third-party Boost sources (pulled in via CPM), headers whose names happen to share a stem prefix with some unrelated Boost internal file can get matched to it instead of a real project file:

  • adobe/algorithm/find.hpp → inferred command from boost/.../libs/atomic/src/find_address_sse2.cpp (1 point: stem "find_address_sse2" starts with "find"; 0 directory overlap) — that command's include paths know nothing about adobe/, so #include <adobe/config.hpp> fails, cascading into dozens of bogus errors ending in fatal_too_many_errors.
  • adobe/algorithm/unique.hpp → same story, matched to boost/.../libs/filesystem/src/unique_path.cpp.
  • adobe/algorithm/append.hpp → matched test/algorithm/clamp/clamp_test.cpp purely on shared algorithm directory name (1 point), since no append_test.cpp exists.

Checking test/algorithm/, only 4 of the ~48 adobe/algorithm/*.hpp headers have a dedicated test file (clamp, median, minmax, select). Every other header in that directory (and likely elsewhere in adobe/) is one coincidental Boost filename away from this failure mode.

Proposed fix

Add a minimal test .cpp for every header that lacks one, following the existing convention (test/<subdir>/<name>/<name>_test.cpp). This reliably wins the heuristic: e.g. for find.hpp, a test/algorithm/find/find_test.cpp scores 1 (stem prefix) + 1 (shared algorithm directory) = 2 points, beating any accidental 1-point Boost cross-match — and no Boost file would ever also match on the algorithm directory segment, so there's no ambiguity.

This is a good fix to pursue independent of any compile-command filtering, because:

  • It doesn't touch compile_commands.json, so Boost/Boost-symbol navigation in the editor is unaffected.
  • It closes a real test-coverage gap (most of adobe/algorithm/*.hpp currently has no dedicated test).
  • It's local and incremental — can be done header-by-header.

Next steps

  • Enumerate all adobe/**/*.hpp headers lacking a same-stem test file.
  • Add minimal test stubs (at least #include the header, to guarantee a real compile-command match) for each, prioritizing the ones observed to actually misfire (find.hpp, unique.hpp, append.hpp, and others in adobe/algorithm/).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions