1 //===--- FindHeadersTest.cpp ----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "AnalysisInternal.h"
10 #include "TypesInternal.h"
11 #include "clang-include-cleaner/Analysis.h"
12 #include "clang-include-cleaner/Record.h"
13 #include "clang-include-cleaner/Types.h"
14 #include "clang/AST/Expr.h"
15 #include "clang/AST/RecursiveASTVisitor.h"
16 #include "clang/Basic/FileEntry.h"
17 #include "clang/Basic/FileManager.h"
18 #include "clang/Basic/LLVM.h"
19 #include "clang/Frontend/FrontendActions.h"
20 #include "clang/Testing/TestAST.h"
21 #include "clang/Tooling/Inclusions/StandardLibrary.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
29 namespace clang::include_cleaner
{
31 using testing::ElementsAre
;
32 using testing::UnorderedElementsAre
;
34 std::string
guard(llvm::StringRef Code
) {
35 return "#pragma once\n" + Code
.str();
38 class FindHeadersTest
: public testing::Test
{
42 std::unique_ptr
<TestAST
> AST
;
44 Inputs
.MakeAction
= [this] {
45 struct Hook
: public SyntaxOnlyAction
{
47 Hook(PragmaIncludes
*Out
) : Out(Out
) {}
48 bool BeginSourceFileAction(clang::CompilerInstance
&CI
) override
{
55 return std::make_unique
<Hook
>(&PI
);
58 void buildAST() { AST
= std::make_unique
<TestAST
>(Inputs
); }
60 llvm::SmallVector
<Hinted
<Header
>> findHeaders(llvm::StringRef FileName
) {
61 return include_cleaner::findHeaders(
62 AST
->sourceManager().translateFileLineCol(
63 AST
->fileManager().getFile(FileName
).get(),
64 /*Line=*/1, /*Col=*/1),
65 AST
->sourceManager(), &PI
);
67 FileEntryRef
physicalHeader(llvm::StringRef FileName
) {
68 return *AST
->fileManager().getOptionalFileRef(FileName
);
72 TEST_F(FindHeadersTest
, IWYUPrivateToPublic
) {
76 Inputs
.ExtraFiles
["private.h"] = guard(R
"cpp(
77 // IWYU pragma: private, include "path
/public.h
"
80 EXPECT_THAT(findHeaders("private.h"),
81 UnorderedElementsAre(physicalHeader("private.h"),
82 Header("\"path/public.h\"")));
85 TEST_F(FindHeadersTest
, IWYUExport
) {
89 Inputs
.ExtraFiles
["exporter.h"] = guard(R
"cpp(
90 #include "exported1
.h
" // IWYU pragma: export
92 // IWYU pragma: begin_exports
93 #include "exported2
.h
"
94 // IWYU pragma: end_exports
98 Inputs
.ExtraFiles
["exported1.h"] = guard("");
99 Inputs
.ExtraFiles
["exported2.h"] = guard("");
100 Inputs
.ExtraFiles
["normal.h"] = guard("");
103 EXPECT_THAT(findHeaders("exported1.h"),
104 UnorderedElementsAre(physicalHeader("exported1.h"),
105 physicalHeader("exporter.h")));
106 EXPECT_THAT(findHeaders("exported2.h"),
107 UnorderedElementsAre(physicalHeader("exported2.h"),
108 physicalHeader("exporter.h")));
109 EXPECT_THAT(findHeaders("normal.h"),
110 UnorderedElementsAre(physicalHeader("normal.h")));
111 EXPECT_THAT(findHeaders("exporter.h"),
112 UnorderedElementsAre(physicalHeader("exporter.h")));
115 TEST_F(FindHeadersTest
, IWYUExportForStandardHeaders
) {
117 #include "exporter
.h
"
119 Inputs
.ExtraFiles
["exporter.h"] = guard(R
"cpp(
120 #include <string> // IWYU pragma: export
122 Inputs
.ExtraFiles
["string"] = guard("");
123 Inputs
.ExtraArgs
.push_back("-isystem.");
125 tooling::stdlib::Symbol StdString
=
126 *tooling::stdlib::Symbol::named("std::", "string");
128 include_cleaner::findHeaders(StdString
, AST
->sourceManager(), &PI
),
129 UnorderedElementsAre(physicalHeader("exporter.h"), StdString
.header()));
132 TEST_F(FindHeadersTest
, SelfContained
) {
136 Inputs
.ExtraFiles
["header.h"] = guard(R
"cpp(
137 #include "fragment
.inc
"
139 Inputs
.ExtraFiles
["fragment.inc"] = "";
141 EXPECT_THAT(findHeaders("fragment.inc"),
142 UnorderedElementsAre(physicalHeader("fragment.inc"),
143 physicalHeader("header.h")));
146 TEST_F(FindHeadersTest
, NonSelfContainedTraversePrivate
) {
150 Inputs
.ExtraFiles
["header.h"] = guard(R
"cpp(
151 #include "fragment
.inc
"
153 Inputs
.ExtraFiles
["fragment.inc"] = R
"cpp(
154 // IWYU pragma: private, include "public.h
"
158 // There is a IWYU private mapping in the non self-contained header, verify
159 // that we don't emit its includer.
160 EXPECT_THAT(findHeaders("fragment.inc"),
161 UnorderedElementsAre(physicalHeader("fragment.inc"),
162 Header("\"public.h\"")));
165 TEST_F(FindHeadersTest
, NonSelfContainedTraverseExporter
) {
167 #include "exporter
.h
"
169 Inputs
.ExtraFiles
["exporter.h"] = guard(R
"cpp(
170 #include "exported
.h
" // IWYU pragma: export
172 Inputs
.ExtraFiles
["exported.h"] = guard(R
"cpp(
173 #include "fragment
.inc
"
175 Inputs
.ExtraFiles
["fragment.inc"] = "";
177 // Verify that we emit exporters for each header on the path.
178 EXPECT_THAT(findHeaders("fragment.inc"),
179 UnorderedElementsAre(physicalHeader("fragment.inc"),
180 physicalHeader("exported.h"),
181 physicalHeader("exporter.h")));
184 TEST_F(FindHeadersTest
, TargetIsExpandedFromMacroInHeader
) {
185 struct CustomVisitor
: RecursiveASTVisitor
<CustomVisitor
> {
186 const Decl
*Out
= nullptr;
187 bool VisitNamedDecl(const NamedDecl
*ND
) {
188 if (ND
->getName() == "FLAG_foo" || ND
->getName() == "Foo") {
189 EXPECT_TRUE(Out
== nullptr);
197 llvm::StringRef MacroHeader
;
198 llvm::StringRef DeclareHeader
;
200 {/*MacroHeader=*/R
"cpp(
201 #define DEFINE_CLASS(name) class name {};
203 /*DeclareHeader=*/R
"cpp(
207 {/*MacroHeader=*/R
"cpp(
208 #define DEFINE_Foo class Foo {};
210 /*DeclareHeader=*/R
"cpp(
214 {/*MacroHeader=*/R
"cpp(
215 #define DECLARE_FLAGS(name) extern int FLAG_##name
217 /*DeclareHeader=*/R
"cpp(
223 for (const auto &T
: TestCases
) {
224 Inputs
.Code
= R
"cpp(#include "declare
.h
")cpp";
225 Inputs
.ExtraFiles
["declare.h"] = guard(T
.DeclareHeader
);
226 Inputs
.ExtraFiles
["macro.h"] = guard(T
.MacroHeader
);
229 CustomVisitor Visitor
;
230 Visitor
.TraverseDecl(AST
->context().getTranslationUnitDecl());
232 auto Headers
= clang::include_cleaner::findHeaders(
233 Visitor
.Out
->getLocation(), AST
->sourceManager(),
234 /*PragmaIncludes=*/nullptr);
235 EXPECT_THAT(Headers
, UnorderedElementsAre(physicalHeader("declare.h")));
239 MATCHER_P2(HintedHeader
, Header
, Hint
, "") {
240 return std::tie(arg
.Hint
, arg
) == std::tie(Hint
, Header
);
243 TEST_F(FindHeadersTest
, PublicHeaderHint
) {
247 Inputs
.ExtraFiles
["public.h"] = guard(R
"cpp(
249 #include "private.inc
"
251 Inputs
.ExtraFiles
["private.h"] = guard(R
"cpp(
252 // IWYU pragma: private
254 Inputs
.ExtraFiles
["private.inc"] = "";
256 // Non self-contained files and headers marked with IWYU private pragma
257 // shouldn't have PublicHeader hint.
259 findHeaders("private.inc"),
260 UnorderedElementsAre(
261 HintedHeader(physicalHeader("private.inc"), Hints::OriginHeader
),
262 HintedHeader(physicalHeader("public.h"), Hints::PublicHeader
)));
263 EXPECT_THAT(findHeaders("private.h"),
264 UnorderedElementsAre(HintedHeader(physicalHeader("private.h"),
265 Hints::OriginHeader
)));
268 TEST_F(FindHeadersTest
, PreferredHeaderHint
) {
272 Inputs
.ExtraFiles
["private.h"] = guard(R
"cpp(
273 // IWYU pragma: private, include "public.h
"
276 // Headers explicitly marked should've preferred signal.
278 findHeaders("private.h"),
279 UnorderedElementsAre(
280 HintedHeader(physicalHeader("private.h"), Hints::OriginHeader
),
281 HintedHeader(Header("\"public.h\""),
282 Hints::PreferredHeader
| Hints::PublicHeader
)));
285 class HeadersForSymbolTest
: public FindHeadersTest
{
287 llvm::SmallVector
<Header
> headersFor(llvm::StringRef Name
) {
288 struct Visitor
: public RecursiveASTVisitor
<Visitor
> {
289 const NamedDecl
*Out
= nullptr;
290 llvm::StringRef Name
;
291 Visitor(llvm::StringRef Name
) : Name(Name
) {}
292 bool VisitNamedDecl(const NamedDecl
*ND
) {
293 if (auto *TD
= ND
->getDescribedTemplate())
296 if (ND
->getName() == Name
) {
297 EXPECT_TRUE(Out
== nullptr || Out
== ND
->getCanonicalDecl())
298 << "Found multiple matches for " << Name
<< ".";
299 Out
= cast
<NamedDecl
>(ND
->getCanonicalDecl());
305 V
.TraverseDecl(AST
->context().getTranslationUnitDecl());
307 ADD_FAILURE() << "Couldn't find any decls named " << Name
<< ".";
309 return headersForSymbol(*V
.Out
, AST
->sourceManager(), &PI
);
311 llvm::SmallVector
<Header
> headersForFoo() { return headersFor("foo"); }
314 TEST_F(HeadersForSymbolTest
, Deduplicates
) {
318 Inputs
.ExtraFiles
["foo.h"] = guard(R
"cpp(
319 // IWYU pragma: private, include "foo
.h
"
326 UnorderedElementsAre(physicalHeader("foo.h"),
327 // FIXME: de-duplicate across different kinds.
328 Header("\"foo.h\"")));
331 TEST_F(HeadersForSymbolTest
, RankByName
) {
336 Inputs
.ExtraFiles
["fox.h"] = guard(R
"cpp(
339 Inputs
.ExtraFiles
["bar.h"] = guard(R
"cpp(
343 EXPECT_THAT(headersForFoo(),
344 ElementsAre(physicalHeader("bar.h"), physicalHeader("fox.h")));
347 TEST_F(HeadersForSymbolTest
, Ranking
) {
348 // Sorting is done over (public, complete, canonical, origin)-tuple.
352 #include "public_complete
.h
"
353 #include "exporter
.h
"
355 Inputs
.ExtraFiles
["public.h"] = guard(R
"cpp(
358 Inputs
.ExtraFiles
["private.h"] = guard(R
"cpp(
359 // IWYU pragma: private, include "canonical
.h
"
362 Inputs
.ExtraFiles
["exporter.h"] = guard(R
"cpp(
363 #include "private.h
" // IWYU pragma: export
365 Inputs
.ExtraFiles
["public_complete.h"] = guard("struct foo {};");
367 EXPECT_THAT(headersForFoo(),
368 ElementsAre(physicalHeader("public_complete.h"),
369 Header("\"canonical.h\""), physicalHeader("public.h"),
370 physicalHeader("exporter.h"),
371 physicalHeader("private.h")));
374 TEST_F(HeadersForSymbolTest
, PreferPublicOverComplete
) {
376 #include "complete_private
.h
"
379 Inputs
.ExtraFiles
["complete_private.h"] = guard(R
"cpp(
380 // IWYU pragma: private
383 Inputs
.ExtraFiles
["public.h"] = guard("struct foo;");
385 EXPECT_THAT(headersForFoo(),
386 ElementsAre(physicalHeader("public.h"),
387 physicalHeader("complete_private.h")));
390 TEST_F(HeadersForSymbolTest
, PreferNameMatch
) {
392 #include "public_complete
.h
"
393 #include "test
/foo
.fwd
.h
"
395 Inputs
.ExtraFiles
["public_complete.h"] = guard("struct foo {};");
396 Inputs
.ExtraFiles
["test/foo.fwd.h"] = guard("struct foo;");
398 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("public_complete.h"),
399 physicalHeader("test/foo.fwd.h")));
402 TEST_F(HeadersForSymbolTest
, MainFile
) {
404 #include "public_complete
.h
"
407 Inputs
.ExtraFiles
["public_complete.h"] = guard(R
"cpp(
411 auto &SM
= AST
->sourceManager();
412 // FIXME: Symbols provided by main file should be treated specially.
415 ElementsAre(physicalHeader("public_complete.h"),
416 Header(*SM
.getFileEntryRefForID(SM
.getMainFileID()))));
419 TEST_F(HeadersForSymbolTest
, PreferExporterOfPrivate
) {
422 #include "exporter
.h
"
424 Inputs
.ExtraFiles
["private.h"] = guard(R
"cpp(
425 // IWYU pragma: private
428 Inputs
.ExtraFiles
["exporter.h"] = guard(R
"cpp(
429 #include "private.h
" // IWYU pragma: export
432 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("exporter.h"),
433 physicalHeader("private.h")));
436 TEST_F(HeadersForSymbolTest
, ExporterIsDownRanked
) {
438 #include "exporter
.h
"
441 // Deliberately named as zoo to make sure it doesn't get name-match boost and
442 // also gets lexicographically bigger order than "exporter".
443 Inputs
.ExtraFiles
["zoo.h"] = guard(R
"cpp(
446 Inputs
.ExtraFiles
["exporter.h"] = guard(R
"cpp(
447 #include "zoo
.h
" // IWYU pragma: export
450 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("zoo.h"),
451 physicalHeader("exporter.h")));
454 TEST_F(HeadersForSymbolTest
, PreferPublicOverNameMatchOnPrivate
) {
458 Inputs
.ExtraFiles
["foo.h"] = guard(R
"cpp(
459 // IWYU pragma: private, include "public.h
"
463 EXPECT_THAT(headersForFoo(), ElementsAre(Header(StringRef("\"public.h\"")),
464 physicalHeader("foo.h")));
467 TEST_F(HeadersForSymbolTest
, PublicOverPrivateWithoutUmbrella
) {
472 Inputs
.ExtraFiles
["bar.h"] =
473 guard(R
"cpp(#include "foo
.h
" // IWYU pragma: export)cpp");
474 Inputs
.ExtraFiles
["foo.h"] = guard(R
"cpp(
475 // IWYU pragma: private
479 EXPECT_THAT(headersForFoo(),
480 ElementsAre(physicalHeader("bar.h"), physicalHeader("foo.h")));
483 TEST_F(HeadersForSymbolTest
, IWYUTransitiveExport
) {
487 Inputs
.ExtraFiles
["export1.h"] = guard(R
"cpp(
488 #include "export2
.h
" // IWYU pragma: export
490 Inputs
.ExtraFiles
["export2.h"] = guard(R
"cpp(
491 #include "foo
.h
" // IWYU pragma: export
493 Inputs
.ExtraFiles
["foo.h"] = guard(R
"cpp(
497 EXPECT_THAT(headersForFoo(),
498 ElementsAre(physicalHeader("foo.h"), physicalHeader("export1.h"),
499 physicalHeader("export2.h")));
502 TEST_F(HeadersForSymbolTest
, IWYUTransitiveExportWithPrivate
) {
507 Inputs
.ExtraFiles
["export1.h"] = guard(R
"cpp(
508 // IWYU pragma: private, include "public1
.h
"
509 #include "export2
.h
" // IWYU pragma: export
512 Inputs
.ExtraFiles
["export2.h"] = guard(R
"cpp(
513 // IWYU pragma: private, include "public2
.h
"
514 #include "export3
.h
" // IWYU pragma: export
516 Inputs
.ExtraFiles
["export3.h"] = guard(R
"cpp(
517 // IWYU pragma: private, include "public3
.h
"
518 #include "foo
.h
" // IWYU pragma: export
520 Inputs
.ExtraFiles
["foo.h"] = guard(R
"cpp(
524 EXPECT_THAT(headersForFoo(),
525 ElementsAre(physicalHeader("foo.h"),
526 Header(StringRef("\"public1.h\"")),
527 physicalHeader("export1.h"),
528 physicalHeader("export2.h"),
529 physicalHeader("export3.h")));
532 TEST_F(HeadersForSymbolTest
, AmbiguousStdSymbols
) {
534 llvm::StringRef Code
;
535 llvm::StringRef Name
;
537 llvm::StringRef ExpectedHeader
;
542 template <typename InputIt, typename OutputIt>
543 constexpr OutputIt move(InputIt first, InputIt last, OutputIt dest);
551 template<class ExecutionPolicy, class ForwardIt1, class ForwardIt2>
552 ForwardIt2 move(ExecutionPolicy&& policy,
553 ForwardIt1 first, ForwardIt1 last, ForwardIt2 d_first);
561 template<typename T> constexpr T move(T&& t) noexcept;
569 template<class ForwardIt, class T>
570 ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
576 "namespace std { int remove(const char*); }",
582 for (const auto &T
: TestCases
) {
583 Inputs
.Code
= T
.Code
;
585 EXPECT_THAT(headersFor(T
.Name
),
586 UnorderedElementsAre(
587 Header(*tooling::stdlib::Header::named(T
.ExpectedHeader
))));
591 TEST_F(HeadersForSymbolTest
, AmbiguousStdSymbolsUsingShadow
) {
594 namespace std { using ::remove; }
602 // Find the DeclRefExpr in the std::remove("abc") function call.
603 struct Visitor
: public RecursiveASTVisitor
<Visitor
> {
604 const DeclRefExpr
*Out
= nullptr;
605 bool VisitDeclRefExpr(const DeclRefExpr
*DRE
) {
606 EXPECT_TRUE(Out
== nullptr) << "Found multiple DeclRefExpr!";
612 V
.TraverseDecl(AST
->context().getTranslationUnitDecl());
613 ASSERT_TRUE(V
.Out
) << "Couldn't find a DeclRefExpr!";
614 EXPECT_THAT(headersForSymbol(*(V
.Out
->getFoundDecl()),
615 AST
->sourceManager(), &PI
),
616 UnorderedElementsAre(
617 Header(*tooling::stdlib::Header::named("<cstdio>"))));
621 TEST_F(HeadersForSymbolTest
, StandardHeaders
) {
622 Inputs
.Code
= "void assert();";
625 headersFor("assert"),
626 // Respect the ordering from the stdlib mapping.
627 UnorderedElementsAre(tooling::stdlib::Header::named("<cassert>"),
628 tooling::stdlib::Header::named("<assert.h>")));
632 } // namespace clang::include_cleaner