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