diff --git a/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp b/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp index fd2de6a17ad4a..7b28d1c252d71 100644 --- a/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp +++ b/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp @@ -275,6 +275,12 @@ llvm::SmallVector
headersForSymbol(const Symbol &S, // are already ranked in the stdlib mapping. if (H.kind() == Header::Standard) continue; + // Don't apply name match hints to exporting headers. As they usually have + // names similar to the original header, e.g. foo_wrapper/foo.h vs + // foo/foo.h, but shouldn't be preferred (unless marked as the public + // interface). + if ((H.Hint & Hints::OriginHeader) == Hints::None) + continue; if (nameMatch(SymbolName, H)) H.Hint |= Hints::PreferredHeader; } diff --git a/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp b/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp index 5a2a41b2d99bd..07302142a13e3 100644 --- a/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp @@ -628,5 +628,24 @@ TEST_F(HeadersForSymbolTest, StandardHeaders) { tooling::stdlib::Header::named(""))); } +TEST_F(HeadersForSymbolTest, ExporterNoNameMatch) { + Inputs.Code = R"cpp( + #include "exporter/foo.h" + #include "foo_public.h" + )cpp"; + Inputs.ExtraArgs.emplace_back("-I."); + // Deliberately named as foo_public to make sure it doesn't get name-match + // boost and also gets lexicographically bigger order than "exporter/foo.h". + Inputs.ExtraFiles["foo_public.h"] = guard(R"cpp( + struct foo {}; + )cpp"); + Inputs.ExtraFiles["exporter/foo.h"] = guard(R"cpp( + #include "foo_public.h" // IWYU pragma: export + )cpp"); + buildAST(); + EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("foo_public.h"), + physicalHeader("exporter/foo.h"))); +} + } // namespace } // namespace clang::include_cleaner