This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] Implement an include-cleaner check.
ClosedPublic

Authored by VitaNuo on Apr 20 2023, 4:43 AM.

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Eugene.Zelenko added inline comments.Apr 20 2023, 7:36 AM
clang-tools-extra/test/clang-tidy/checkers/google/Inputs/bar.h
5

Please add. Same in other files.

VitaNuo retitled this revision from [clang-tidy] Implement an include-cleaner check. to [WIP][clang-tidy] Implement an include-cleaner check..Apr 20 2023, 7:39 AM

I believe this should be discussed in an RFC. We already have the standalone include-cleaner tool, why is that not sufficient? Can it be extended instead? There's also the include-what-you-use tool out there.

I believe this should be discussed in an RFC. We already have the standalone include-cleaner tool, why is that not sufficient? Can it be extended instead? There's also the include-what-you-use tool out there.

On my understanding, include-cleaner is re-implementation of IWYU. Awhile ago IWYU maintainers tried to merge their project to LLVM. Discussion should be in old mailing lists.

Hi folks, the rationale for a clang-tidy check is enabling include-cleaner findings to be applied at scale and integrations into existing workflows (e.g. a lot of people run cleanups using clang-tidy findings hence there's somewhat existing infra for that).

current include-cleaner tool has some downsides when it comes to cleaning-up a whole codebase (e.g. it applies changes as it goes, which might result in breaking builds as it goes if not applied carefully), surely these shortcomings can be addressed by introducing more logic into the standalone tool, but it'd still lack the ecosystem & integrations clang-tidy has, which would be really unfortunate.
Could you give some details about how you're using the existing include-cleaner tool so that we better understand use cases around that one too?

As for the existing IWYU tool, include-cleaner library used in this check has a different interpretation of "use", so despite looking similar they have different implementations and might even disagree at certain cases due to these differences.

VitaNuo updated this revision to Diff 520307.May 8 2023, 2:35 AM
VitaNuo marked 2 inline comments as done.

Move the check from "google" to "misc".

thanks, mostly LG at the high level i think one thing we're missing is the ability to suppress analysis for certain headers, similar to what we have in clangd config options. can you add a check option for that?

clang-tools-extra/clang-tidy/CMakeLists.txt
10

can you move this into clang-tools-extra/clang-tidy/misc/CMakeLists.txt ? in theory it isn't needed by all the checks

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
49 ↗(On Diff #520307)

rather than storing state in ::check and running logic in ::onEndOfTranslationUnit, you can just trigger analysis & finding generation here as we already match only on the translation unit decl.

57 ↗(On Diff #520307)

TUDecl can have children that are not part of the main file and we'll just run analysis on these decls for nothing as we discard references spelled outside mainfile inside walkUsed.

so can you go over the children of TUDecl, and only pick the ones that are spelled inside the mainfile and feed them as analysis roots?

93 ↗(On Diff #520307)

you can use ClangTidyCheck::getCurrentMainFile() instead of Mainfile->tryGetRealPathName()

104 ↗(On Diff #520307)

you can have: diag(Inc->HashLocation, "unused include %0") << Inc->quote() << FixItHint::CreateRemoval(..) instead of the string concat above.
(also convention is to have diagnostic messages that start with lower case letters)

106–109 ↗(On Diff #520307)

you can use tooling::HeaderIncludes not only for insertions, but also for removals.

113 ↗(On Diff #520307)

again you can use ClangTidyCheck::getCurrentMainFile()

114 ↗(On Diff #520307)

nit: i'd extract SM->getBufferData(SM->getMainFileID()) into a llvm::StringRef Code and use it in both places

116 ↗(On Diff #520307)

no need for braces

123–124 ↗(On Diff #520307)

again you can use ClangTidyCheck::getCurrentMainFile() and drop the check

125 ↗(On Diff #520307)

constructor of tooling::HeaderIncludes parses the code, so let's create it once outside the loop?

139 ↗(On Diff #520307)

you can use the substitution alternative mentioned above here as well

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
25 ↗(On Diff #520307)

can you move this struct into the implementation file? as it isn't exposed in the interfaces

clang-tools-extra/docs/ReleaseNotes.rst
174

Enforces include-what-you-use on headers using include-cleaner. ?

clang-tools-extra/docs/clang-tidy/checks/list.rst
293

could you revert this change?

481

could you revert this change?

clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
6 ↗(On Diff #520307)

can you also mention that this only generates findings for the main file of a translation unit?

clang-tools-extra/include-cleaner/lib/Record.cpp
153

can you change this constructor to : RecordPragma(CI.getSourceManager(), CI.getPreprocessor(), Out) ?

157

no need to pass SM seperately, you can get it out of pre-processor via PP.getSourceManager()

351

same here, no need to pass SM separately

VitaNuo updated this revision to Diff 524336.May 22 2023, 8:45 AM
VitaNuo marked 19 inline comments as done.

Address review comments.

kadircet added inline comments.May 25 2023, 12:36 AM
clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
61 ↗(On Diff #524336)

after storing them in the class state on construction, we should provide them with current values here:

/// Should store all options supported by this check with their
/// current values or default values for options that haven't been overridden.
66 ↗(On Diff #524336)

as mentioned above, rather than doing this on demand, we should run the logic on construction and store in the class state

66 ↗(On Diff #524336)

s/Suppress/IgnoreHeader

to be consistent with option in clangd

81 ↗(On Diff #524336)

we should actually use FileLoc of the decl location here (i.e. map it back to spelling location) as the decl might be introduced by a macro expansion, but if the spelling of "primary" location belongs to the main file we should still analyze it (e.g. decls introduced via TEST macros)

also SourceManager::isInMainFile will follow #line directives, which can create confusion (a declaration from some other file will create a diagnostic in the main file), so let's use isWrittenInMainFile instead?

101 ↗(On Diff #524336)

we should also respect IgnoreHeaders here

126 ↗(On Diff #524336)

sorry i guess i wasn't explicit about this one, but it should actually be a suffix match based regex (e.g. foo/.* disables analysis for all sources under a directory called foo) on the resolved path of the include (similar to what we do in clangd).

as headers can be spelled differently based on the translation unit and this option is mentioned for the whole codebase.

131 ↗(On Diff #524336)

FileData makes it sound like this is some other data about the file rather than its contents. maybe just Code?

144 ↗(On Diff #524336)

sorry I know I suggested using HeaderIncludes for removals too, but this seems to cause more trouble actually;

we should actually go over rest of the replacements too, HeaderIncludes will generate removals for all the includes that match this (spelling, quoting). hence we can't just apply the first removal.
but that'll imply we'll remove them multiple times (as analysis above will treat each of them as a separate unused include). hence we'll need some deduplicaiton.

it might be easier to just use line number we have in Inc and use SourceManager:: translateFileLineCol to drop the whole line (up until start of the next line).

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
43 ↗(On Diff #524336)

the convention is to rather store options in check's state on construction. see documentation on ClangTidyCheck::ClangTidyCheck:

/// Initializes the check with \p CheckName and \p Context.
///
/// Derived classes must implement the constructor with this signature or
/// delegate it. If a check needs to read options, it can do this in the
/// constructor using the Options.get() methods below.
clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
8 ↗(On Diff #524336)

can you also mention the check options for ignoring analysis?

clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
18 ↗(On Diff #524336)

can you have the includes multiple times and make sure all of them are being dropped?

37 ↗(On Diff #524336)

can you also test for insertion suppressions ?

Drive-by nit: want add this to the disableUnusableChecks() blocklist in clangd/TidyProvider.cpp?
Since inside clangd we want to use the direct feature instead.

VitaNuo updated this revision to Diff 526064.May 26 2023, 7:57 AM
VitaNuo marked 13 inline comments as done.

Rework according to the review comments.

kadircet added inline comments.May 26 2023, 2:31 PM
clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
42 ↗(On Diff #526064)

let's put this struct into anon namespace

48 ↗(On Diff #526064)

in theory this is not enough to disable the check for objc++, it'll have both objc and cplusplus set. so we should check ObjC is false (there's no need to disable it for c, right?)

nit: we can also override isLanguageVersionSupported to disable check for non-c++.

67 ↗(On Diff #526064)

let's make sure we're only going to match suffixes by appending $ to the Header here.

also before pushing into IgnoreHeadersRegex can you verify the regex isValid and report a diagnostic via configurationDiag if regex is invalid.

72 ↗(On Diff #526064)

check is performed only once for this check so it doesn't matter, but it might be better to perform this in constructor and store as class state to make sure we're only parsing options and creating the regexes once (also possibly reporting diagnostics once). as regex creation is actually expensive

83 ↗(On Diff #526064)

instead of const_cast here, you can just drop the const in const auto *D in for loop variable

107 ↗(On Diff #526064)

we're running this against spelled include of the file, not resolved path.

we should instead use the spelling + trimmed version for verbatim/standard headers and use FileEntry::tryGetRealPathName for phyiscal headers. we can even do the filtering before spelling the header to not spell redundantly.

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
48 ↗(On Diff #526064)

let's make a full copy here and store a std::string instead, as reference from options might become dangling. also the copy is cheap, we do that once per check instantiation.

clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
15 ↗(On Diff #526064)
VitaNuo marked 8 inline comments as done.May 30 2023, 2:51 AM

Thanks for the comments! I'll re-assign the review to Haojian for now, so that we can hopefully make more progress this week.

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
48 ↗(On Diff #526064)

Ah thanks for the tip with isLanguageVersionSupported, doing that now.

67 ↗(On Diff #526064)

Thanks, good ideas!

clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
15 ↗(On Diff #526064)

You probably wanted to say insertion/removal instead of inclusion/insertion.

VitaNuo marked 3 inline comments as done.May 30 2023, 2:51 AM
VitaNuo updated this revision to Diff 526558.May 30 2023, 2:54 AM

Address review comments.

VitaNuo retitled this revision from [WIP][clang-tidy] Implement an include-cleaner check. to [clang-tidy] Implement an include-cleaner check..May 30 2023, 3:03 AM

Not too bad, you on a right road. Tests failed on windows, and clang-format failed.

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
86 ↗(On Diff #526558)

auto -> const Decl*

170–175 ↗(On Diff #526558)

Use IncludeInserter::createIncludeInsertion if possible...
So include style would be properly handled.

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
28–29 ↗(On Diff #526558)

this description does not match first sentence in check documentation.

42 ↗(On Diff #526558)

many check uses ; as separator, and uses parseStringList function to do that from OptionsUtils.

44 ↗(On Diff #526558)

if someone will put empty string like this ,, you will end up with $ as regex, wont it match "everything" ?

47 ↗(On Diff #526558)

llvm::Regex got constructor, use emplace_back

55 ↗(On Diff #526558)

Missing "void storeOptions(ClangTidyOptions::OptionMap &Opts) override;"
to store IgnoreHeaders option. --export-config may not work correctly without this.

clang-tools-extra/docs/ReleaseNotes.rst
174

is this actually a "include-what-you-use" ?
Simply if I use iwyu tool, will it give same output and result in same behaviour ?
If this is different tool, then change this to avoid confusion.

174

This description does not match first sentence in check documentation.

clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
6 ↗(On Diff #526558)

avoid "The check"

9 ↗(On Diff #526558)

Try adding some very simple "example"

17 ↗(On Diff #526558)

add some info about default value, is there any ?

clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
9 ↗(On Diff #526558)

this test file is fine, but there is no validation of output warning.

PiotrZSL added inline comments.May 30 2023, 5:57 AM
clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
9 ↗(On Diff #526558)

nwm, somehow I missed one file.

19 ↗(On Diff #526558)

make sure that those tests does not depend on actual system headers (check what headers are included here).

hokein added inline comments.May 31 2023, 2:00 AM
clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
45 ↗(On Diff #526558)

storing a string instance for header per system reference is expensive (we might have many missing-include symbols, and we might have many duplications). We can store the a clang::include_cleaner::Header in the struct here, and call the spellHeader when generating the FixIts.

88 ↗(On Diff #526558)

We should use the getExpansionLoc rather than the SpellingLoc here, otherwise we might miss the case like TEST_F(WalkUsedTest, MultipleProviders) {... } where the decl location is spelled in another file, but the function body is spelled in main-file.

89 ↗(On Diff #526558)

and we probably need the same logic to filtering out implicit template specialization, similar to https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/include-cleaner/lib/Record.cpp#L408-L410.

It is fine to leave it in this patch, but please add a FIXME.

130 ↗(On Diff #526558)

nit: the prefix ClangTidyCheck:: qualifier is not needed, you can remove it.

149 ↗(On Diff #526558)

The diagnostic message "unused include ..." seems too short for users to understand what's the issue here, it would be better to make it more descriptive, in clangd, we emit included header ... is not used directly, we can follow this pattern.

The same for the missing-includes.

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
36 ↗(On Diff #526558)

nit: the function body is long, consider moving it to .cpp file.

clang-tools-extra/clangd/TidyProvider.cpp
220 ↗(On Diff #526558)

nit: the entry here is under the Crashing Checks category, this doesn't seem a good fit. Maybe move it right after the above empty string "".

clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
13 ↗(On Diff #526558)

IgnoreHeader => IgnoreHeaders, in the implementation, we use the plural form.

hokein added inline comments.May 31 2023, 2:00 AM
clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
1 ↗(On Diff #526558)

nit: missing the license file comment.

hokein added inline comments.May 31 2023, 2:29 AM
clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
170–175 ↗(On Diff #526558)

tooling::HeaderIncludes already handles the include style well (and more). In general, we recommend to use tooling::headerIncludes -- it was built for generic toolings that need to perform #include manipulations, it has been heavily used in clangd/clang-format/clang-include-cleaner etc.

IncludeInserter::createIncludeInsertion is an old implementation in clang-tidy, and it was built before tooling::HeaderIncludes is thing. In the long-term, I think we should probably consider deprecating it and replacing it with tooling::HeaderIncludes.

VitaNuo updated this revision to Diff 527001.May 31 2023, 5:01 AM
VitaNuo marked 25 inline comments as done.

Address review comments.

Thanks for the comments!

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
86 ↗(On Diff #526558)

const will not work, since the container I need to populate has to have llvm::SmallVector<Decl *> type due to the include-cleaner library interfaces.

88 ↗(On Diff #526558)

we should actually use FileLoc of the decl location here (i.e. map it back to spelling location) as the decl might be introduced by a macro expansion, but if the spelling of "primary" location belongs to the main file we should still analyze it (e.g. decls introduced via TEST macros)

we actually want spelling location, not fileloc
sorry for the confusion
basically, spelling location will always map to where a name is spelled in a > physical file, even if it's part of macro body
whereas getFileLoc, will map tokens from macro body to their expansion locations (i.e. place in a physical file where the macro is invoked)

These are earlier comments from Kadir on this topic. AFAIU we want the spelling location for TEST_F(WalkUsedTest, MultipleProviders) {... } because:

  • for Decls introduced by the TEST_F expansion, we would like to analyze them only if they are spelled in the main file.
  • for arguments, their transitive spelling location is where they're written. So if TEST_F(WalkUsedTest, MultipleProviders) {... } is written in the main file, the argument locations will be counted.
81 ↗(On Diff #524336)

we should actually use FileLoc of the decl location here (i.e. map it back to spelling location) as the decl might be introduced by a macro expansion, but if the spelling of "primary" location belongs to the main file we should still analyze it (e.g. decls introduced via TEST macros)

also SourceManager::isInMainFile will follow #line directives, which can create confusion (a declaration from some other file will create a diagnostic in the main file), so let's use isWrittenInMainFile instead?

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
42 ↗(On Diff #526558)

Thanks for the tip, I can use the OptionsUtils infrastructure indeed.

clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
19 ↗(On Diff #526558)

they don't, adding a declaration to the custom "vector.h" and then using it in the main file makes vector disappear from the list of unused includes.
On top of that, even if the test did somehow depend on the actual system headers, this wouldn't change anything in the test result, since the vector header would remain unused.

Eugene.Zelenko added inline comments.May 31 2023, 7:06 AM
clang-tools-extra/docs/ReleaseNotes.rst
171

Please keep alphabetical order (by check name) in this section.

174

One sentence is enough.

The only thing is the source location, see my comments for details, otherwise looks good in general.

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
88 ↗(On Diff #526558)

I think I'm not convinced, see my comments below.

for Decls introduced by the TEST_F expansion, we would like to analyze them only if they are spelled in the main file.

I think it is true for some cases. For example, a function decl has different parts (declarator, and function body), if the declarator is introduced by a macro which defined in a header, and the function body is spelled in the main-file, we still want to analyze the function body, see a simple example below:

// foo.h
#define DECLARE(X) void X()

// main.cpp
#include "foo.h"

DECLARE(myfunc) {
   int a;
   ...
}

Moreover, we have use ExpansionLoc pattern in other places when we collect the main-file top-level decl (include-cleaner, clangd), and I don't see a special reason to change the pattern in the clang-tidy check.

116 ↗(On Diff #527001)

it is sad that we duplicate the include-cleaner analyse implementation (the only difference is that here we record the references for missing-includes), I think we should find a way to share the code in the future.

No action required in this patch, can you add a FIXME?

194 ↗(On Diff #527001)

I think we should use the spelling location -- Inc.SymRefLocation can be a macro location which can points to another file, we intend to show diagnostics for main-file only, and walkUsed has some internal logic which guarantees that the spelling loc of returned refs is main-file.

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.h
21 ↗(On Diff #527001)

can you cleanup the includes here? looks like there are some unused-includes now, at least Syntax/Tokens.h from the first glance.

clang-tools-extra/docs/clang-tidy/checks/misc/include-cleaner.rst
4 ↗(On Diff #527001)

nit: remove extra trailing ==

clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
1 ↗(On Diff #527001)

nit: IncludeCleanerTest.cpp

VitaNuo updated this revision to Diff 527317.Jun 1 2023, 1:08 AM

Update release notes.

VitaNuo marked 2 inline comments as done.Jun 1 2023, 1:08 AM
VitaNuo updated this revision to Diff 527329.Jun 1 2023, 2:30 AM
VitaNuo marked 6 inline comments as done.

Address review comments.

Thanks for the comments!

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
88 ↗(On Diff #526558)

Is the expansion location of myfunc in the main file? If that's the case, we need the expansion location indeed.
Otherwise, we need getFileLoc to map file locations from macro arguments to their spelling (in the main file above) and locations from macro bodies to the expansion location.

hokein accepted this revision.Jun 1 2023, 4:03 AM

Thanks, just a few nits. I think it is in a good shape, let's ship it!

Please make sure the premerge-test is happy before landing the patch, the windows premerge test is failing now (https://buildkite.com/llvm-project/premerge-checks/builds/155660#0188764d-dc9f-4e8f-b489-c7b8f8b0c52a)

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp
189–193 ↗(On Diff #527329)

nit: we can simplify it like

if (auto Replacement = HeaderIncludes.insert(...))
   diag(...);
88 ↗(On Diff #526558)

Is the expansion location of myfunc in the main file? If that's the case, we need the expansion location indeed.

Right. The expansion location here is a file location which points to the main-file.

Would be nice if you add the above test into the unittest.

clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp
27 ↗(On Diff #527329)

wrap all things into an anonymous namespace to avoid potential ODR violations.

70 ↗(On Diff #527329)

Instead of using 4 += statements, I think it is a bit clearer to use llvm::formatv("bar.h;{1};{2};...", appendPathSystemIndependent(...), ....);

This revision is now accepted and ready to land.Jun 1 2023, 4:03 AM
VitaNuo updated this revision to Diff 527359.Jun 1 2023, 4:24 AM

Try to fix windows test.

VitaNuo updated this revision to Diff 527369.Jun 1 2023, 4:54 AM
VitaNuo marked an inline comment as done.

Remove system-independent handling to see behavior on Windows.

VitaNuo updated this revision to Diff 527376.Jun 1 2023, 5:42 AM
VitaNuo marked an inline comment as done.

Remove the regular expression.

VitaNuo updated this revision to Diff 527384.Jun 1 2023, 6:05 AM
VitaNuo marked an inline comment as done.

Remove regex and path handling.

VitaNuo updated this revision to Diff 527385.Jun 1 2023, 6:05 AM

Remove regex and path handling.

VitaNuo updated this revision to Diff 527392.Jun 1 2023, 6:24 AM
VitaNuo marked an inline comment as done.

Add debug statements for windows debugging.

VitaNuo updated this revision to Diff 527402.Jun 1 2023, 6:47 AM

Remove debug statements.

VitaNuo updated this revision to Diff 527457.Jun 1 2023, 9:28 AM

Re-introduce special path handling.

VitaNuo updated this revision to Diff 527473.Jun 1 2023, 10:02 AM

Try to fix windows build.

VitaNuo updated this revision to Diff 527509.Jun 1 2023, 10:59 AM

Try ignoring verbatim spelling.

VitaNuo updated this revision to Diff 527543.Jun 1 2023, 11:44 AM

Fix last test.

VitaNuo updated this revision to Diff 527554.Jun 1 2023, 12:07 PM

Experiment with paths.

VitaNuo updated this revision to Diff 527812.Jun 2 2023, 5:15 AM

Escape the slashes in regex.

VitaNuo updated this revision to Diff 527822.Jun 2 2023, 5:37 AM

Remove path handling.

VitaNuo updated this revision to Diff 527839.Jun 2 2023, 6:36 AM

Re-introduce path handling.

This revision was automatically updated to reflect the committed changes.
mgorny added a subscriber: mgorny.Jun 3 2023, 3:06 AM

I'm getting completely incomprehensible build errors on Gentoo from this, as of aa7eace8431ba213c5431638b894b0e1b4b481c7:

samu: job failed: : && /usr/lib/ccache/bin/x86_64-pc-linux-gnu-g++ -march=znver2 --param=l1-cache-size=32 --param=l1-cache-line-size=64 -O2 -pipe -frecord-gcc-switches -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -Wimplicit-fallthrough -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -pedantic -Wno-long-long -Wl,-O1 -Wl,--as-needed -Wl,--hash-style=gnu   -Wl,--export-dynamic -rdynamic  -Wl,-rpath-link,/var/tmp/portage/sys-devel/clang-17.0.0_pre20230603/work/x/y/clang-abi_x86_64.amd64/./lib64 tools/extra/clang-tidy/tool/CMakeFiles/clang-tidy.dir/ClangTidyToolMain.cpp.o -o bin/clang-tidy -L/usr/lib/llvm/17/lib64 -Wl,-rpath,"\$ORIGIN/../lib64:/usr/lib/llvm/17/lib64:/var/tmp/portage/sys-devel/clang-17.0.0_pre20230603/work/x/y/clang-abi_x86_64.amd64/lib64:"  lib64/libclangTidy.a  lib64/libclangTidyMain.a  lib64/libclangTidyAndroidModule.a  lib64/libclangTidyAbseilModule.a  lib64/libclangTidyAlteraModule.a  lib64/libclangTidyBoostModule.a  lib64/libclangTidyBugproneModule.a  lib64/libclangTidyCERTModule.a  lib64/libclangTidyConcurrencyModule.a  lib64/libclangTidyCppCoreGuidelinesModule.a  lib64/libclangTidyDarwinModule.a  lib64/libclangTidyFuchsiaModule.a  lib64/libclangTidyGoogleModule.a  lib64/libclangTidyHICPPModule.a  lib64/libclangTidyLinuxKernelModule.a  lib64/libclangTidyLLVMModule.a  lib64/libclangTidyLLVMLibcModule.a  lib64/libclangTidyMiscModule.a  lib64/libclangTidyModernizeModule.a  lib64/libclangTidyObjCModule.a  lib64/libclangTidyOpenMPModule.a  lib64/libclangTidyPerformanceModule.a  lib64/libclangTidyPortabilityModule.a  lib64/libclangTidyReadabilityModule.a  lib64/libclangTidyZirconModule.a  lib64/libclangTidyMPIModule.a  lib64/libclangTidyBugproneModule.a  lib64/libclangTidyCppCoreGuidelinesModule.a  lib64/libclangTidyGoogleModule.a  lib64/libclangTidyMiscModule.a  lib64/libclangAnalysis.a  lib64/libclangASTMatchers.a  lib64/libclangAST.a  lib64/libclangLex.a  lib64/libclangBasic.a  lib64/libclangTidyModernizeModule.a  lib64/libclangTidyReadabilityModule.a  lib64/libclangTidyUtils.a  lib64/libclangTidy.a  lib64/libclang-cpp.so.17gitaa7eace8  /usr/lib/llvm/17/lib64/libLLVM-17gitaa7eace8.so && :
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: lib64/libclangTidyMiscModule.a(IncludeCleanerCheck.cpp.o): in function `clang::tidy::misc::IncludeCleanerCheck::registerPPCallbacks(clang::SourceManager const&, clang::Preprocessor*, clang::Preprocessor*) [clone .localalias]':
IncludeCleanerCheck.cpp:(.text._ZN5clang4tidy4misc19IncludeCleanerCheck19registerPPCallbacksERKNS_13SourceManagerEPNS_12PreprocessorES7_+0x24): undefined reference to `clang::include_cleaner::RecordedPP::record(clang::Preprocessor const&)'
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: IncludeCleanerCheck.cpp:(.text._ZN5clang4tidy4misc19IncludeCleanerCheck19registerPPCallbacksERKNS_13SourceManagerEPNS_12PreprocessorES7_+0x94): undefined reference to `clang::include_cleaner::PragmaIncludes::record(clang::Preprocessor&)'
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: lib64/libclangTidyMiscModule.a(IncludeCleanerCheck.cpp.o): in function `clang::tidy::misc::IncludeCleanerCheck::check(clang::ast_matchers::MatchFinder::MatchResult const&) [clone .localalias]':
IncludeCleanerCheck.cpp:(.text._ZN5clang4tidy4misc19IncludeCleanerCheck5checkERKNS_12ast_matchers11MatchFinder11MatchResultE+0x508): undefined reference to `clang::include_cleaner::walkUsed(llvm::ArrayRef<clang::Decl*>, llvm::ArrayRef<clang::include_cleaner::SymbolReference>, clang::include_cleaner::PragmaIncludes const*, clang::SourceManager const&, llvm::function_ref<void (clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cleaner::Header>)>)'
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: IncludeCleanerCheck.cpp:(.text._ZN5clang4tidy4misc19IncludeCleanerCheck5checkERKNS_12ast_matchers11MatchFinder11MatchResultE+0x604): undefined reference to `clang::include_cleaner::PragmaIncludes::getPublic(clang::FileEntry const*) const'
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: IncludeCleanerCheck.cpp:(.text._ZN5clang4tidy4misc19IncludeCleanerCheck5checkERKNS_12ast_matchers11MatchFinder11MatchResultE+0x9e8): undefined reference to `clang::include_cleaner::Include::quote[abi:cxx11]() const'
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: IncludeCleanerCheck.cpp:(.text._ZN5clang4tidy4misc19IncludeCleanerCheck5checkERKNS_12ast_matchers11MatchFinder11MatchResultE+0xd15): undefined reference to `clang::include_cleaner::spellHeader[abi:cxx11](clang::include_cleaner::Header const&, clang::HeaderSearch const&, clang::FileEntry const*)'
/usr/lib/gcc/x86_64-pc-linux-gnu/13/../../../../x86_64-pc-linux-gnu/bin/ld: lib64/libclangTidyMiscModule.a(IncludeCleanerCheck.cpp.o): in function `void llvm::function_ref<void (clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cleaner::Header>)>::callback_fn<clang::tidy::misc::IncludeCleanerCheck::check(clang::ast_matchers::MatchFinder::MatchResult const&)::{lambda(clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cleaner::Header>)#1}>(long, clang::include_cleaner::SymbolReference const&, llvm::ArrayRef<clang::include_cleaner::Header>)':
IncludeCleanerCheck.cpp:(.text._ZN4llvm12function_refIFvRKN5clang15include_cleaner15SymbolReferenceENS_8ArrayRefINS2_6HeaderEEEEE11callback_fnIZNS1_4tidy4misc19IncludeCleanerCheck5checkERKNS1_12ast_matchers11MatchFinder11MatchResultEEUlS5_S8_E_EEvlS5_S8_+0xd1): undefined reference to `clang::include_cleaner::Includes::match(clang::include_cleaner::Header) const'
collect2: error: ld returned 1 exit status

I'm getting completely incomprehensible build errors on Gentoo from this

I'm also hitting this; it only seems to happen if building with -DLLVM_LINK_LLVM_DYLIB=ON.

My educated guess would be that clangIncludeCleaner is being linked via clang_target_link_libraries while it's not part of libclang-cpp, so it should go into regular target_link_libraries.

My educated guess would be that clangIncludeCleaner is being linked via clang_target_link_libraries while it's not part of libclang-cpp, so it should go into regular target_link_libraries.

Yes, that does seem to do the trick. LGTM from my PoV if you can push such a fix, otherwise I can try to do it in a little while...

My educated guess would be that clangIncludeCleaner is being linked via clang_target_link_libraries while it's not part of libclang-cpp, so it should go into regular target_link_libraries.

Yes, that does seem to do the trick. LGTM from my PoV if you can push such a fix, otherwise I can try to do it in a little while...

Looks like it is fixed in https://github.com/llvm/llvm-project/commit/ac0ea7555ee4ae872bcd153e04513ba0b88b8985, thanks!