xref: /llvm-project/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp (revision ec6c3448d31056db5d63d7aed3e9f207edb49321)
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 
29 namespace clang::include_cleaner {
30 namespace {
31 using testing::ElementsAre;
32 using testing::UnorderedElementsAre;
33 
34 std::string guard(llvm::StringRef Code) {
35   return "#pragma once\n" + Code.str();
36 }
37 
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;
51         }
52 
53         PragmaIncludes *Out;
54       };
55       return std::make_unique<Hook>(&PI);
56     };
57   }
58   void buildAST() { AST = std::make_unique<TestAST>(Inputs); }
59 
60   llvm::SmallVector<Hinted<Header>> findHeaders(llvm::StringRef FileName) {
61     return include_cleaner::findHeaders(
62         AST->sourceManager().translateFileLineCol(
63             *AST->fileManager().getOptionalFileRef(FileName),
64             /*Line=*/1, /*Col=*/1),
65         AST->sourceManager(), &PI);
66   }
67   FileEntryRef physicalHeader(llvm::StringRef FileName) {
68     return *AST->fileManager().getOptionalFileRef(FileName);
69   };
70 };
71 
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\"")));
83 }
84 
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
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("");
99   Inputs.ExtraFiles["exported2.h"] = guard("");
100   Inputs.ExtraFiles["normal.h"] = guard("");
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 
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()));
130 }
131 
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")));
144 }
145 
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";
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 
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")));
182 }
183 
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;
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(T.DeclareHeader);
226     Inputs.ExtraFiles["macro.h"] = guard(T.MacroHeader);
227     buildAST();
228 
229     CustomVisitor Visitor;
230     Visitor.TraverseDecl(AST->context().getTranslationUnitDecl());
231 
232     auto Headers = clang::include_cleaner::findHeaders(
233         Visitor.Out->getLocation(), AST->sourceManager(),
234         /*PragmaIncludes=*/nullptr);
235     EXPECT_THAT(Headers, UnorderedElementsAre(physicalHeader("declare.h")));
236   }
237 }
238 
239 MATCHER_P2(HintedHeader, Header, Hint, "") {
240   return std::tie(arg.Hint, arg) == std::tie(Hint, Header);
241 }
242 
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)));
266 }
267 
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)));
283 }
284 
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;
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->preprocessor(), &PI);
310   }
311   llvm::SmallVector<Header> headersForFoo() { return headersFor("foo"); }
312 };
313 
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\"")));
329 }
330 
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")));
345 }
346 
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")));
372 }
373 
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")));
388 }
389 
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")));
400 }
401 
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()))));
417 }
418 
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")));
434 }
435 
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")));
452 }
453 
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")));
465 }
466 
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")));
481 }
482 
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")));
500 }
501 
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")));
530 }
531 
532 TEST_F(HeadersForSymbolTest, AmbiguousStdSymbols) {
533   struct {
534     llvm::StringRef Code;
535     llvm::StringRef Name;
536 
537     llvm::StringRef ExpectedHeader;
538   } TestCases[] = {
539       {
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>",
547       },
548       {
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>",
557       },
558       {
559           R"cpp(
560             namespace std {
561               template<typename T> constexpr T move(T&& t) noexcept;
562             })cpp",
563           "move",
564           "<utility>",
565       },
566       {
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>",
574       },
575       {
576           "namespace std { int remove(const char*); }",
577           "remove",
578           "<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 
591 TEST_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(
615       headersForSymbol(*(V.Out->getFoundDecl()), AST->preprocessor(), &PI),
616       UnorderedElementsAre(
617           Header(*tooling::stdlib::Header::named("<cstdio>"))));
618 }
619 
620 TEST_F(HeadersForSymbolTest, StandardHeaders) {
621   Inputs.Code = R"cpp(
622     #include "stdlib_internal.h"
623     void assert();
624     void foo() { assert(); }
625   )cpp";
626   Inputs.ExtraFiles["stdlib_internal.h"] = "void assert();";
627   buildAST();
628   EXPECT_THAT(
629       headersFor("assert"),
630       // Respect the ordering from the stdlib mapping.
631       // FIXME: Report physical locations too, stdlib_internal.h and main-file
632       // should also be candidates. But they should be down-ranked compared to
633       // stdlib providers.
634       UnorderedElementsAre(tooling::stdlib::Header::named("<cassert>"),
635                            tooling::stdlib::Header::named("<assert.h>")));
636 }
637 
638 TEST_F(HeadersForSymbolTest, StdlibLangForMacros) {
639   Inputs.Code = R"cpp(
640     #define EOF 0
641     void foo() { EOF; }
642   )cpp";
643   {
644     buildAST();
645     const Macro Eof{AST->preprocessor().getIdentifierInfo("EOF"), {}};
646     EXPECT_THAT(
647         headersForSymbol(Eof, AST->preprocessor(), nullptr),
648         UnorderedElementsAre(tooling::stdlib::Header::named("<cstdio>"),
649                              tooling::stdlib::Header::named("<stdio.h>")));
650   }
651 
652   {
653     Inputs.ExtraArgs.push_back("-xc");
654     buildAST();
655     const Macro Eof{AST->preprocessor().getIdentifierInfo("EOF"), {}};
656     EXPECT_THAT(headersForSymbol(Eof, AST->preprocessor(), nullptr),
657                 UnorderedElementsAre(tooling::stdlib::Header::named(
658                     "<stdio.h>", tooling::stdlib::Lang::C)));
659   }
660 }
661 
662 TEST_F(HeadersForSymbolTest, ExporterNoNameMatch) {
663   Inputs.Code = R"cpp(
664     #include "exporter/foo.h"
665     #include "foo_public.h"
666   )cpp";
667   Inputs.ExtraArgs.emplace_back("-I.");
668   // Deliberately named as foo_public to make sure it doesn't get name-match
669   // boost and also gets lexicographically bigger order than "exporter/foo.h".
670   Inputs.ExtraFiles["foo_public.h"] = guard(R"cpp(
671     struct foo {};
672   )cpp");
673   Inputs.ExtraFiles["exporter/foo.h"] = guard(R"cpp(
674     #include "foo_public.h" // IWYU pragma: export
675   )cpp");
676   buildAST();
677   EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("foo_public.h"),
678                                            physicalHeader("exporter/foo.h")));
679 }
680 
681 } // namespace
682 } // namespace clang::include_cleaner
683