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//===----------------------------------------------------------------------===//
8
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>
28
29namespace clang::include_cleaner {
30namespace {
31using testing::ElementsAre;
32using testing::UnorderedElementsAre;
33
34std::string guard(llvm::StringRef Code) {
35 return "#pragma once\n" + Code.str();
36}
37
38class FindHeadersTest : public testing::Test {
39protected:
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;
51 }
52
53 PragmaIncludes *Out;
54 };
55 return std::make_unique<Hook>(args: &PI);
56 };
57 }
58 void buildAST() { AST = std::make_unique<TestAST>(args&: Inputs); }
59
60 llvm::SmallVector<Hinted<Header>> findHeaders(llvm::StringRef FileName) {
61 return include_cleaner::findHeaders(
62 Loc: AST->sourceManager().translateFileLineCol(
63 SourceFile: AST->fileManager().getFile(Filename: FileName).get(),
64 /*Line=*/1, /*Col=*/1),
65 SM: AST->sourceManager(), PI: &PI);
66 }
67 FileEntryRef physicalHeader(llvm::StringRef FileName) {
68 return *AST->fileManager().getOptionalFileRef(Filename: FileName);
69 };
70};
71
72TEST_F(FindHeadersTest, IWYUPrivateToPublic) {
73 Inputs.Code = R"cpp(
74 #include "private.h"
75 )cpp";
76 Inputs.ExtraFiles["private.h"] = guard(Code: 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\"")));
83}
84
85TEST_F(FindHeadersTest, IWYUExport) {
86 Inputs.Code = R"cpp(
87 #include "exporter.h"
88 )cpp";
89 Inputs.ExtraFiles["exporter.h"] = guard(Code: R"cpp(
90 #include "exported1.h" // IWYU pragma: export
91
92 // IWYU pragma: begin_exports
93 #include "exported2.h"
94 // IWYU pragma: end_exports
95
96 #include "normal.h"
97 )cpp");
98 Inputs.ExtraFiles["exported1.h"] = guard(Code: "");
99 Inputs.ExtraFiles["exported2.h"] = guard(Code: "");
100 Inputs.ExtraFiles["normal.h"] = guard(Code: "");
101
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")));
113}
114
115TEST_F(FindHeadersTest, IWYUExportForStandardHeaders) {
116 Inputs.Code = R"cpp(
117 #include "exporter.h"
118 )cpp";
119 Inputs.ExtraFiles["exporter.h"] = guard(Code: R"cpp(
120 #include <string> // IWYU pragma: export
121 )cpp");
122 Inputs.ExtraFiles["string"] = guard(Code: "");
123 Inputs.ExtraArgs.push_back(x: "-isystem.");
124 buildAST();
125 tooling::stdlib::Symbol StdString =
126 *tooling::stdlib::Symbol::named(Scope: "std::", Name: "string");
127 EXPECT_THAT(
128 include_cleaner::findHeaders(StdString, AST->sourceManager(), &PI),
129 UnorderedElementsAre(physicalHeader("exporter.h"), StdString.header()));
130}
131
132TEST_F(FindHeadersTest, SelfContained) {
133 Inputs.Code = R"cpp(
134 #include "header.h"
135 )cpp";
136 Inputs.ExtraFiles["header.h"] = guard(Code: 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")));
144}
145
146TEST_F(FindHeadersTest, NonSelfContainedTraversePrivate) {
147 Inputs.Code = R"cpp(
148 #include "header.h"
149 )cpp";
150 Inputs.ExtraFiles["header.h"] = guard(Code: R"cpp(
151 #include "fragment.inc"
152 )cpp");
153 Inputs.ExtraFiles["fragment.inc"] = R"cpp(
154 // IWYU pragma: private, include "public.h"
155 )cpp";
156
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\"")));
163}
164
165TEST_F(FindHeadersTest, NonSelfContainedTraverseExporter) {
166 Inputs.Code = R"cpp(
167 #include "exporter.h"
168 )cpp";
169 Inputs.ExtraFiles["exporter.h"] = guard(Code: R"cpp(
170 #include "exported.h" // IWYU pragma: export
171 )cpp");
172 Inputs.ExtraFiles["exported.h"] = guard(Code: 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")));
182}
183
184TEST_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;
191 }
192 return true;
193 }
194 };
195
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"},
221 };
222
223 for (const auto &T : TestCases) {
224 Inputs.Code = R"cpp(#include "declare.h")cpp";
225 Inputs.ExtraFiles["declare.h"] = guard(Code: T.DeclareHeader);
226 Inputs.ExtraFiles["macro.h"] = guard(Code: T.MacroHeader);
227 buildAST();
228
229 CustomVisitor Visitor;
230 Visitor.TraverseDecl(AST->context().getTranslationUnitDecl());
231
232 auto Headers = clang::include_cleaner::findHeaders(
233 Loc: Visitor.Out->getLocation(), SM: AST->sourceManager(),
234 /*PragmaIncludes=*/PI: nullptr);
235 EXPECT_THAT(Headers, UnorderedElementsAre(physicalHeader("declare.h")));
236 }
237}
238
239MATCHER_P2(HintedHeader, Header, Hint, "") {
240 return std::tie(arg.Hint, arg) == std::tie(Hint, Header);
241}
242
243TEST_F(FindHeadersTest, PublicHeaderHint) {
244 Inputs.Code = R"cpp(
245 #include "public.h"
246 )cpp";
247 Inputs.ExtraFiles["public.h"] = guard(Code: R"cpp(
248 #include "private.h"
249 #include "private.inc"
250 )cpp");
251 Inputs.ExtraFiles["private.h"] = guard(Code: 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)));
266}
267
268TEST_F(FindHeadersTest, PreferredHeaderHint) {
269 Inputs.Code = R"cpp(
270 #include "private.h"
271 )cpp";
272 Inputs.ExtraFiles["private.h"] = guard(Code: 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)));
283}
284
285class HeadersForSymbolTest : public FindHeadersTest {
286protected:
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;
295
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());
300 }
301 return true;
302 }
303 };
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);
310 }
311 llvm::SmallVector<Header> headersForFoo() { return headersFor(Name: "foo"); }
312};
313
314TEST_F(HeadersForSymbolTest, Deduplicates) {
315 Inputs.Code = R"cpp(
316 #include "foo.h"
317 )cpp";
318 Inputs.ExtraFiles["foo.h"] = guard(Code: 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\"")));
329}
330
331TEST_F(HeadersForSymbolTest, RankByName) {
332 Inputs.Code = R"cpp(
333 #include "fox.h"
334 #include "bar.h"
335 )cpp";
336 Inputs.ExtraFiles["fox.h"] = guard(Code: R"cpp(
337 void foo();
338 )cpp");
339 Inputs.ExtraFiles["bar.h"] = guard(Code: R"cpp(
340 void foo();
341 )cpp");
342 buildAST();
343 EXPECT_THAT(headersForFoo(),
344 ElementsAre(physicalHeader("bar.h"), physicalHeader("fox.h")));
345}
346
347TEST_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(Code: R"cpp(
356 struct foo;
357 )cpp");
358 Inputs.ExtraFiles["private.h"] = guard(Code: R"cpp(
359 // IWYU pragma: private, include "canonical.h"
360 struct foo;
361 )cpp");
362 Inputs.ExtraFiles["exporter.h"] = guard(Code: R"cpp(
363 #include "private.h" // IWYU pragma: export
364 )cpp");
365 Inputs.ExtraFiles["public_complete.h"] = guard(Code: "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")));
372}
373
374TEST_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(Code: R"cpp(
380 // IWYU pragma: private
381 struct foo {};
382 )cpp");
383 Inputs.ExtraFiles["public.h"] = guard(Code: "struct foo;");
384 buildAST();
385 EXPECT_THAT(headersForFoo(),
386 ElementsAre(physicalHeader("public.h"),
387 physicalHeader("complete_private.h")));
388}
389
390TEST_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(Code: "struct foo {};");
396 Inputs.ExtraFiles["test/foo.fwd.h"] = guard(Code: "struct foo;");
397 buildAST();
398 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("public_complete.h"),
399 physicalHeader("test/foo.fwd.h")));
400}
401
402TEST_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(Code: 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()))));
417}
418
419TEST_F(HeadersForSymbolTest, PreferExporterOfPrivate) {
420 Inputs.Code = R"cpp(
421 #include "private.h"
422 #include "exporter.h"
423 )cpp";
424 Inputs.ExtraFiles["private.h"] = guard(Code: R"cpp(
425 // IWYU pragma: private
426 struct foo {};
427 )cpp");
428 Inputs.ExtraFiles["exporter.h"] = guard(Code: 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")));
434}
435
436TEST_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(Code: R"cpp(
444 struct foo {};
445 )cpp");
446 Inputs.ExtraFiles["exporter.h"] = guard(Code: 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")));
452}
453
454TEST_F(HeadersForSymbolTest, PreferPublicOverNameMatchOnPrivate) {
455 Inputs.Code = R"cpp(
456 #include "foo.h"
457 )cpp";
458 Inputs.ExtraFiles["foo.h"] = guard(Code: 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")));
465}
466
467TEST_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(Code: R"cpp(#include "foo.h" // IWYU pragma: export)cpp");
474 Inputs.ExtraFiles["foo.h"] = guard(Code: 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")));
481}
482
483TEST_F(HeadersForSymbolTest, IWYUTransitiveExport) {
484 Inputs.Code = R"cpp(
485 #include "export1.h"
486 )cpp";
487 Inputs.ExtraFiles["export1.h"] = guard(Code: R"cpp(
488 #include "export2.h" // IWYU pragma: export
489 )cpp");
490 Inputs.ExtraFiles["export2.h"] = guard(Code: R"cpp(
491 #include "foo.h" // IWYU pragma: export
492 )cpp");
493 Inputs.ExtraFiles["foo.h"] = guard(Code: 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")));
500}
501
502TEST_F(HeadersForSymbolTest, IWYUTransitiveExportWithPrivate) {
503 Inputs.Code = R"cpp(
504 #include "export1.h"
505 void bar() { foo();}
506 )cpp";
507 Inputs.ExtraFiles["export1.h"] = guard(Code: 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(Code: R"cpp(
513 // IWYU pragma: private, include "public2.h"
514 #include "export3.h" // IWYU pragma: export
515 )cpp");
516 Inputs.ExtraFiles["export3.h"] = guard(Code: R"cpp(
517 // IWYU pragma: private, include "public3.h"
518 #include "foo.h" // IWYU pragma: export
519 )cpp");
520 Inputs.ExtraFiles["foo.h"] = guard(Code: 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")));
530}
531
532TEST_F(HeadersForSymbolTest, AmbiguousStdSymbols) {
533 struct {
534 llvm::StringRef Code;
535 llvm::StringRef Name;
536
537 llvm::StringRef ExpectedHeader;
538 } TestCases[] = {
539 {
540 .Code: R"cpp(
541 namespace std {
542 template <typename InputIt, typename OutputIt>
543 constexpr OutputIt move(InputIt first, InputIt last, OutputIt dest);
544 })cpp",
545 .Name: "move",
546 .ExpectedHeader: "<algorithm>",
547 },
548 {
549 .Code: 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 .Name: "move",
556 .ExpectedHeader: "<algorithm>",
557 },
558 {
559 .Code: R"cpp(
560 namespace std {
561 template<typename T> constexpr T move(T&& t) noexcept;
562 })cpp",
563 .Name: "move",
564 .ExpectedHeader: "<utility>",
565 },
566 {
567 .Code: R"cpp(
568 namespace std {
569 template<class ForwardIt, class T>
570 ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
571 })cpp",
572 .Name: "remove",
573 .ExpectedHeader: "<algorithm>",
574 },
575 {
576 .Code: "namespace std { int remove(const char*); }",
577 .Name: "remove",
578 .ExpectedHeader: "<cstdio>",
579 },
580 };
581
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))));
588 }
589}
590
591TEST_F(HeadersForSymbolTest, AmbiguousStdSymbolsUsingShadow) {
592 Inputs.Code = R"cpp(
593 void remove(char*);
594 namespace std { using ::remove; }
595
596 void k() {
597 std::remove("abc");
598 }
599 )cpp";
600 buildAST();
601
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;
609 }
610 };
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>"))));
618}
619
620
621TEST_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>")));
629}
630
631TEST_F(HeadersForSymbolTest, ExporterNoNameMatch) {
632 Inputs.Code = R"cpp(
633 #include "exporter/foo.h"
634 #include "foo_public.h"
635 )cpp";
636 Inputs.ExtraArgs.emplace_back(args: "-I.");
637 // Deliberately named as foo_public to make sure it doesn't get name-match
638 // boost and also gets lexicographically bigger order than "exporter/foo.h".
639 Inputs.ExtraFiles["foo_public.h"] = guard(Code: R"cpp(
640 struct foo {};
641 )cpp");
642 Inputs.ExtraFiles["exporter/foo.h"] = guard(Code: R"cpp(
643 #include "foo_public.h" // IWYU pragma: export
644 )cpp");
645 buildAST();
646 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("foo_public.h"),
647 physicalHeader("exporter/foo.h")));
648}
649
650} // namespace
651} // namespace clang::include_cleaner
652

source code of clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp