[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang-tools-extra / include-cleaner / unittests / FindHeadersTest.cpp
blob5a2a41b2d99bdd78b52b63fda3ce5db38394d404
1 //===--- FindHeadersTest.cpp ----------------------------------------------===//
2 //
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
6 //
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"
26 #include <cassert>
27 #include <memory>
29 namespace clang::include_cleaner {
30 namespace {
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 {
39 protected:
40 TestInputs Inputs;
41 PragmaIncludes PI;
42 std::unique_ptr<TestAST> AST;
43 FindHeadersTest() {
44 Inputs.MakeAction = [this] {
45 struct Hook : public SyntaxOnlyAction {
46 public:
47 Hook(PragmaIncludes *Out) : Out(Out) {}
48 bool BeginSourceFileAction(clang::CompilerInstance &CI) override {
49 Out->record(CI);
50 return true;
53 PragmaIncludes *Out;
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) {
73 Inputs.Code = R"cpp(
74 #include "private.h"
75 )cpp";
76 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
77 // IWYU pragma: private, include "path/public.h"
78 )cpp");
79 buildAST();
80 EXPECT_THAT(findHeaders("private.h"),
81 UnorderedElementsAre(physicalHeader("private.h"),
82 Header("\"path/public.h\"")));
85 TEST_F(FindHeadersTest, IWYUExport) {
86 Inputs.Code = R"cpp(
87 #include "exporter.h"
88 )cpp";
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
96 #include "normal.h"
97 )cpp");
98 Inputs.ExtraFiles["exported1.h"] = guard("");
99 Inputs.ExtraFiles["exported2.h"] = guard("");
100 Inputs.ExtraFiles["normal.h"] = guard("");
102 buildAST();
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) {
116 Inputs.Code = R"cpp(
117 #include "exporter.h"
118 )cpp";
119 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
120 #include <string> // IWYU pragma: export
121 )cpp");
122 Inputs.ExtraFiles["string"] = guard("");
123 Inputs.ExtraArgs.push_back("-isystem.");
124 buildAST();
125 tooling::stdlib::Symbol StdString =
126 *tooling::stdlib::Symbol::named("std::", "string");
127 EXPECT_THAT(
128 include_cleaner::findHeaders(StdString, AST->sourceManager(), &PI),
129 UnorderedElementsAre(physicalHeader("exporter.h"), StdString.header()));
132 TEST_F(FindHeadersTest, SelfContained) {
133 Inputs.Code = R"cpp(
134 #include "header.h"
135 )cpp";
136 Inputs.ExtraFiles["header.h"] = guard(R"cpp(
137 #include "fragment.inc"
138 )cpp");
139 Inputs.ExtraFiles["fragment.inc"] = "";
140 buildAST();
141 EXPECT_THAT(findHeaders("fragment.inc"),
142 UnorderedElementsAre(physicalHeader("fragment.inc"),
143 physicalHeader("header.h")));
146 TEST_F(FindHeadersTest, NonSelfContainedTraversePrivate) {
147 Inputs.Code = R"cpp(
148 #include "header.h"
149 )cpp";
150 Inputs.ExtraFiles["header.h"] = guard(R"cpp(
151 #include "fragment.inc"
152 )cpp");
153 Inputs.ExtraFiles["fragment.inc"] = R"cpp(
154 // IWYU pragma: private, include "public.h"
155 )cpp";
157 buildAST();
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) {
166 Inputs.Code = R"cpp(
167 #include "exporter.h"
168 )cpp";
169 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
170 #include "exported.h" // IWYU pragma: export
171 )cpp");
172 Inputs.ExtraFiles["exported.h"] = guard(R"cpp(
173 #include "fragment.inc"
174 )cpp");
175 Inputs.ExtraFiles["fragment.inc"] = "";
176 buildAST();
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);
190 Out = ND;
192 return true;
196 struct {
197 llvm::StringRef MacroHeader;
198 llvm::StringRef DeclareHeader;
199 } TestCases[] = {
200 {/*MacroHeader=*/R"cpp(
201 #define DEFINE_CLASS(name) class name {};
202 )cpp",
203 /*DeclareHeader=*/R"cpp(
204 #include "macro.h"
205 DEFINE_CLASS(Foo)
206 )cpp"},
207 {/*MacroHeader=*/R"cpp(
208 #define DEFINE_Foo class Foo {};
209 )cpp",
210 /*DeclareHeader=*/R"cpp(
211 #include "macro.h"
212 DEFINE_Foo
213 )cpp"},
214 {/*MacroHeader=*/R"cpp(
215 #define DECLARE_FLAGS(name) extern int FLAG_##name
216 )cpp",
217 /*DeclareHeader=*/R"cpp(
218 #include "macro.h"
219 DECLARE_FLAGS(foo);
220 )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);
227 buildAST();
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) {
244 Inputs.Code = R"cpp(
245 #include "public.h"
246 )cpp";
247 Inputs.ExtraFiles["public.h"] = guard(R"cpp(
248 #include "private.h"
249 #include "private.inc"
250 )cpp");
251 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
252 // IWYU pragma: private
253 )cpp");
254 Inputs.ExtraFiles["private.inc"] = "";
255 buildAST();
256 // Non self-contained files and headers marked with IWYU private pragma
257 // shouldn't have PublicHeader hint.
258 EXPECT_THAT(
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) {
269 Inputs.Code = R"cpp(
270 #include "private.h"
271 )cpp";
272 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
273 // IWYU pragma: private, include "public.h"
274 )cpp");
275 buildAST();
276 // Headers explicitly marked should've preferred signal.
277 EXPECT_THAT(
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 {
286 protected:
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())
294 ND = TD;
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());
301 return true;
304 Visitor V(Name);
305 V.TraverseDecl(AST->context().getTranslationUnitDecl());
306 if (!V.Out)
307 ADD_FAILURE() << "Couldn't find any decls named " << Name << ".";
308 assert(V.Out);
309 return headersForSymbol(*V.Out, AST->sourceManager(), &PI);
311 llvm::SmallVector<Header> headersForFoo() { return headersFor("foo"); }
314 TEST_F(HeadersForSymbolTest, Deduplicates) {
315 Inputs.Code = R"cpp(
316 #include "foo.h"
317 )cpp";
318 Inputs.ExtraFiles["foo.h"] = guard(R"cpp(
319 // IWYU pragma: private, include "foo.h"
320 void foo();
321 void foo();
322 )cpp");
323 buildAST();
324 EXPECT_THAT(
325 headersForFoo(),
326 UnorderedElementsAre(physicalHeader("foo.h"),
327 // FIXME: de-duplicate across different kinds.
328 Header("\"foo.h\"")));
331 TEST_F(HeadersForSymbolTest, RankByName) {
332 Inputs.Code = R"cpp(
333 #include "fox.h"
334 #include "bar.h"
335 )cpp";
336 Inputs.ExtraFiles["fox.h"] = guard(R"cpp(
337 void foo();
338 )cpp");
339 Inputs.ExtraFiles["bar.h"] = guard(R"cpp(
340 void foo();
341 )cpp");
342 buildAST();
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.
349 Inputs.Code = R"cpp(
350 #include "private.h"
351 #include "public.h"
352 #include "public_complete.h"
353 #include "exporter.h"
354 )cpp";
355 Inputs.ExtraFiles["public.h"] = guard(R"cpp(
356 struct foo;
357 )cpp");
358 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
359 // IWYU pragma: private, include "canonical.h"
360 struct foo;
361 )cpp");
362 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
363 #include "private.h" // IWYU pragma: export
364 )cpp");
365 Inputs.ExtraFiles["public_complete.h"] = guard("struct foo {};");
366 buildAST();
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) {
375 Inputs.Code = R"cpp(
376 #include "complete_private.h"
377 #include "public.h"
378 )cpp";
379 Inputs.ExtraFiles["complete_private.h"] = guard(R"cpp(
380 // IWYU pragma: private
381 struct foo {};
382 )cpp");
383 Inputs.ExtraFiles["public.h"] = guard("struct foo;");
384 buildAST();
385 EXPECT_THAT(headersForFoo(),
386 ElementsAre(physicalHeader("public.h"),
387 physicalHeader("complete_private.h")));
390 TEST_F(HeadersForSymbolTest, PreferNameMatch) {
391 Inputs.Code = R"cpp(
392 #include "public_complete.h"
393 #include "test/foo.fwd.h"
394 )cpp";
395 Inputs.ExtraFiles["public_complete.h"] = guard("struct foo {};");
396 Inputs.ExtraFiles["test/foo.fwd.h"] = guard("struct foo;");
397 buildAST();
398 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("public_complete.h"),
399 physicalHeader("test/foo.fwd.h")));
402 TEST_F(HeadersForSymbolTest, MainFile) {
403 Inputs.Code = R"cpp(
404 #include "public_complete.h"
405 struct foo;
406 )cpp";
407 Inputs.ExtraFiles["public_complete.h"] = guard(R"cpp(
408 struct foo {};
409 )cpp");
410 buildAST();
411 auto &SM = AST->sourceManager();
412 // FIXME: Symbols provided by main file should be treated specially.
413 EXPECT_THAT(
414 headersForFoo(),
415 ElementsAre(physicalHeader("public_complete.h"),
416 Header(*SM.getFileEntryRefForID(SM.getMainFileID()))));
419 TEST_F(HeadersForSymbolTest, PreferExporterOfPrivate) {
420 Inputs.Code = R"cpp(
421 #include "private.h"
422 #include "exporter.h"
423 )cpp";
424 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
425 // IWYU pragma: private
426 struct foo {};
427 )cpp");
428 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
429 #include "private.h" // IWYU pragma: export
430 )cpp");
431 buildAST();
432 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("exporter.h"),
433 physicalHeader("private.h")));
436 TEST_F(HeadersForSymbolTest, ExporterIsDownRanked) {
437 Inputs.Code = R"cpp(
438 #include "exporter.h"
439 #include "zoo.h"
440 )cpp";
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(
444 struct foo {};
445 )cpp");
446 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
447 #include "zoo.h" // IWYU pragma: export
448 )cpp");
449 buildAST();
450 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("zoo.h"),
451 physicalHeader("exporter.h")));
454 TEST_F(HeadersForSymbolTest, PreferPublicOverNameMatchOnPrivate) {
455 Inputs.Code = R"cpp(
456 #include "foo.h"
457 )cpp";
458 Inputs.ExtraFiles["foo.h"] = guard(R"cpp(
459 // IWYU pragma: private, include "public.h"
460 struct foo {};
461 )cpp");
462 buildAST();
463 EXPECT_THAT(headersForFoo(), ElementsAre(Header(StringRef("\"public.h\"")),
464 physicalHeader("foo.h")));
467 TEST_F(HeadersForSymbolTest, PublicOverPrivateWithoutUmbrella) {
468 Inputs.Code = R"cpp(
469 #include "bar.h"
470 #include "foo.h"
471 )cpp";
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
476 struct foo {};
477 )cpp");
478 buildAST();
479 EXPECT_THAT(headersForFoo(),
480 ElementsAre(physicalHeader("bar.h"), physicalHeader("foo.h")));
483 TEST_F(HeadersForSymbolTest, IWYUTransitiveExport) {
484 Inputs.Code = R"cpp(
485 #include "export1.h"
486 )cpp";
487 Inputs.ExtraFiles["export1.h"] = guard(R"cpp(
488 #include "export2.h" // IWYU pragma: export
489 )cpp");
490 Inputs.ExtraFiles["export2.h"] = guard(R"cpp(
491 #include "foo.h" // IWYU pragma: export
492 )cpp");
493 Inputs.ExtraFiles["foo.h"] = guard(R"cpp(
494 struct foo {};
495 )cpp");
496 buildAST();
497 EXPECT_THAT(headersForFoo(),
498 ElementsAre(physicalHeader("foo.h"), physicalHeader("export1.h"),
499 physicalHeader("export2.h")));
502 TEST_F(HeadersForSymbolTest, IWYUTransitiveExportWithPrivate) {
503 Inputs.Code = R"cpp(
504 #include "export1.h"
505 void bar() { foo();}
506 )cpp";
507 Inputs.ExtraFiles["export1.h"] = guard(R"cpp(
508 // IWYU pragma: private, include "public1.h"
509 #include "export2.h" // IWYU pragma: export
510 void foo();
511 )cpp");
512 Inputs.ExtraFiles["export2.h"] = guard(R"cpp(
513 // IWYU pragma: private, include "public2.h"
514 #include "export3.h" // IWYU pragma: export
515 )cpp");
516 Inputs.ExtraFiles["export3.h"] = guard(R"cpp(
517 // IWYU pragma: private, include "public3.h"
518 #include "foo.h" // IWYU pragma: export
519 )cpp");
520 Inputs.ExtraFiles["foo.h"] = guard(R"cpp(
521 void foo();
522 )cpp");
523 buildAST();
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) {
533 struct {
534 llvm::StringRef Code;
535 llvm::StringRef Name;
537 llvm::StringRef ExpectedHeader;
538 } TestCases[] = {
540 R"cpp(
541 namespace std {
542 template <typename InputIt, typename OutputIt>
543 constexpr OutputIt move(InputIt first, InputIt last, OutputIt dest);
544 })cpp",
545 "move",
546 "<algorithm>",
549 R"cpp(
550 namespace std {
551 template<class ExecutionPolicy, class ForwardIt1, class ForwardIt2>
552 ForwardIt2 move(ExecutionPolicy&& policy,
553 ForwardIt1 first, ForwardIt1 last, ForwardIt2 d_first);
554 })cpp",
555 "move",
556 "<algorithm>",
559 R"cpp(
560 namespace std {
561 template<typename T> constexpr T move(T&& t) noexcept;
562 })cpp",
563 "move",
564 "<utility>",
567 R"cpp(
568 namespace std {
569 template<class ForwardIt, class T>
570 ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
571 })cpp",
572 "remove",
573 "<algorithm>",
576 "namespace std { int remove(const char*); }",
577 "remove",
578 "<cstdio>",
582 for (const auto &T : TestCases) {
583 Inputs.Code = T.Code;
584 buildAST();
585 EXPECT_THAT(headersFor(T.Name),
586 UnorderedElementsAre(
587 Header(*tooling::stdlib::Header::named(T.ExpectedHeader))));
591 TEST_F(HeadersForSymbolTest, AmbiguousStdSymbolsUsingShadow) {
592 Inputs.Code = R"cpp(
593 void remove(char*);
594 namespace std { using ::remove; }
596 void k() {
597 std::remove("abc");
599 )cpp";
600 buildAST();
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!";
607 Out = DRE;
608 return true;
611 Visitor V;
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();";
623 buildAST();
624 EXPECT_THAT(
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>")));
631 } // namespace
632 } // namespace clang::include_cleaner