819 lines · cpp
1//===---- IncludeInserterTest.cpp - clang-tidy ----------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "../clang-tidy/utils/IncludeInserter.h"10#include "clang/Lex/Preprocessor.h"11#include "clang/Frontend/CompilerInstance.h"12#include "ClangTidyTest.h"13#include "gtest/gtest.h"14 15// FIXME: Canonicalize paths correctly on windows.16// Currently, adding virtual files will canonicalize the paths before17// storing the virtual entries.18// When resolving virtual entries in the FileManager, the paths (for19// example coming from a #include directive) are not canonicalized20// to native paths; thus, the virtual file is not found.21// This needs to be fixed in the FileManager before we can make22// clang-tidy tests work.23#if !defined(_WIN32)24 25namespace clang {26namespace tidy {27namespace {28 29class IncludeInserterCheckBase : public ClangTidyCheck {30public:31 IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context,32 utils::IncludeSorter::IncludeStyle Style =33 utils::IncludeSorter::IS_Google,34 bool SelfContainedDiags = false)35 : ClangTidyCheck(CheckName, Context),36 Inserter(Style, SelfContainedDiags) {}37 38 void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,39 Preprocessor *ModuleExpanderPP) override {40 Inserter.registerPreprocessor(PP);41 }42 43 void registerMatchers(ast_matchers::MatchFinder *Finder) override {44 Finder->addMatcher(ast_matchers::declStmt().bind("stmt"), this);45 }46 47 void check(const ast_matchers::MatchFinder::MatchResult &Result) override {48 auto Diag = diag(Result.Nodes.getNodeAs<DeclStmt>("stmt")->getBeginLoc(),49 "foo, bar");50 for (StringRef Header : headersToInclude()) {51 Diag << Inserter.createMainFileIncludeInsertion(Header);52 }53 }54 55 virtual std::vector<StringRef> headersToInclude() const = 0;56 57 utils::IncludeInserter Inserter;58};59 60class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {61public:62 NonSystemHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)63 : IncludeInserterCheckBase(CheckName, Context) {}64 65 std::vector<StringRef> headersToInclude() const override {66 return {"path/to/header.h"};67 }68};69 70class EarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase {71public:72 EarlyInAlphabetHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)73 : IncludeInserterCheckBase(CheckName, Context) {}74 75 std::vector<StringRef> headersToInclude() const override {76 return {"a/header.h"};77 }78};79 80class MultipleHeaderInserterCheck : public IncludeInserterCheckBase {81public:82 MultipleHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)83 : IncludeInserterCheckBase(CheckName, Context) {}84 85 std::vector<StringRef> headersToInclude() const override {86 return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"};87 }88};89 90class MultipleHeaderSingleInserterCheck : public IncludeInserterCheckBase {91public:92 MultipleHeaderSingleInserterCheck(StringRef CheckName,93 ClangTidyContext *Context)94 : IncludeInserterCheckBase(CheckName, Context,95 utils::IncludeSorter::IS_Google,96 /*SelfContainedDiags=*/true) {}97 98 std::vector<StringRef> headersToInclude() const override {99 return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"};100 }101};102 103class CSystemIncludeInserterCheck : public IncludeInserterCheckBase {104public:105 CSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)106 : IncludeInserterCheckBase(CheckName, Context) {}107 108 std::vector<StringRef> headersToInclude() const override {109 return {"<stdlib.h>"};110 }111};112 113class CXXSystemIncludeInserterCheck : public IncludeInserterCheckBase {114public:115 CXXSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)116 : IncludeInserterCheckBase(CheckName, Context) {}117 118 std::vector<StringRef> headersToInclude() const override { return {"<set>"}; }119};120 121class InvalidIncludeInserterCheck : public IncludeInserterCheckBase {122public:123 InvalidIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)124 : IncludeInserterCheckBase(CheckName, Context) {}125 126 std::vector<StringRef> headersToInclude() const override {127 return {"a.h", "<stdlib.h", "cstdlib>", "b.h", "<c.h>", "<d>"};128 }129};130 131class ObjCEarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase {132public:133 ObjCEarlyInAlphabetHeaderInserterCheck(StringRef CheckName,134 ClangTidyContext *Context)135 : IncludeInserterCheckBase(CheckName, Context,136 utils::IncludeSorter::IS_Google_ObjC) {}137 138 std::vector<StringRef> headersToInclude() const override {139 return {"a/header.h"};140 }141};142 143class ObjCCategoryHeaderInserterCheck : public IncludeInserterCheckBase {144public:145 ObjCCategoryHeaderInserterCheck(StringRef CheckName,146 ClangTidyContext *Context)147 : IncludeInserterCheckBase(CheckName, Context,148 utils::IncludeSorter::IS_Google_ObjC) {}149 150 std::vector<StringRef> headersToInclude() const override {151 return {"top_level_test_header+foo.h"};152 }153};154 155class ObjCGeneratedHeaderInserterCheck : public IncludeInserterCheckBase {156public:157 ObjCGeneratedHeaderInserterCheck(StringRef CheckName,158 ClangTidyContext *Context)159 : IncludeInserterCheckBase(CheckName, Context,160 utils::IncludeSorter::IS_Google_ObjC) {}161 162 std::vector<StringRef> headersToInclude() const override {163 return {"clang_tidy/tests/generated_file.proto.h"};164 }165};166 167template <typename Check>168std::string runCheckOnCode(StringRef Code, StringRef Filename) {169 std::vector<ClangTidyError> Errors;170 return test::runCheckOnCode<Check>(Code, &Errors, Filename, {},171 ClangTidyOptions(),172 {// Main file include173 {"clang_tidy/tests/"174 "insert_includes_test_header.h",175 "\n"},176 // Top-level main file include +177 // category.178 {"top_level_test_header.h", "\n"},179 {"top_level_test_header+foo.h", "\n"},180 // ObjC category.181 {"clang_tidy/tests/"182 "insert_includes_test_header+foo.h",183 "\n"},184 // Non system headers185 {"a/header.h", "\n"},186 {"path/to/a/header.h", "\n"},187 {"path/to/z/header.h", "\n"},188 {"path/to/header.h", "\n"},189 {"path/to/header2.h", "\n"},190 // Generated headers191 {"clang_tidy/tests/"192 "generated_file.proto.h",193 "\n"},194 // Fake system headers.195 {"stdlib.h", "\n"},196 {"unistd.h", "\n"},197 {"list", "\n"},198 {"map", "\n"},199 {"set", "\n"},200 {"vector", "\n"}});201}202 203TEST(IncludeInserterTest, InsertAfterLastNonSystemInclude) {204 const char *PreCode = R"(205#include "clang_tidy/tests/insert_includes_test_header.h"206 207#include <list>208#include <map>209 210#include "path/to/a/header.h"211 212void foo() {213 int a = 0;214})";215 const char *PostCode = R"(216#include "clang_tidy/tests/insert_includes_test_header.h"217 218#include <list>219#include <map>220 221#include "path/to/a/header.h"222#include "path/to/header.h"223 224void foo() {225 int a = 0;226})";227 228 EXPECT_EQ(PostCode,229 runCheckOnCode<NonSystemHeaderInserterCheck>(230 PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));231}232 233TEST(IncludeInserterTest, InsertMultipleIncludesAndDeduplicate) {234 const char *PreCode = R"(235#include "clang_tidy/tests/insert_includes_test_header.h"236 237#include <list>238#include <map>239 240#include "path/to/a/header.h"241 242void foo() {243 int a = 0;244})";245 const char *PostCode = R"(246#include "clang_tidy/tests/insert_includes_test_header.h"247 248#include <list>249#include <map>250 251#include "path/to/a/header.h"252#include "path/to/header.h"253#include "path/to/header2.h"254 255void foo() {256 int a = 0;257})";258 259 EXPECT_EQ(PostCode,260 runCheckOnCode<MultipleHeaderInserterCheck>(261 PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));262}263 264TEST(IncludeInserterTest, InsertMultipleIncludesNoDeduplicate) {265 const char *PreCode = R"(266#include "clang_tidy/tests/insert_includes_test_header.h"267 268#include <list>269#include <map>270 271#include "path/to/a/header.h"272 273void foo() {274 int a = 0;275})";276 // FIXME: ClangFormat bug - https://bugs.llvm.org/show_bug.cgi?id=49298277 // clang-format off278 const char *PostCode = R"(279#include "clang_tidy/tests/insert_includes_test_header.h"280 281#include <list>282#include <map>283 284#include "path/to/a/header.h"285#include "path/to/header.h"286#include "path/to/header2.h"287#include "path/to/header.h"288 289void foo() {290 int a = 0;291})";292 // clang-format on293 294 EXPECT_EQ(PostCode,295 runCheckOnCode<MultipleHeaderSingleInserterCheck>(296 PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));297}298 299TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) {300 const char *PreCode = R"(301#include "clang_tidy/tests/insert_includes_test_header.h"302 303#include <list>304#include <map>305 306#include "path/to/z/header.h"307 308void foo() {309 int a = 0;310})";311 const char *PostCode = R"(312#include "clang_tidy/tests/insert_includes_test_header.h"313 314#include <list>315#include <map>316 317#include "path/to/header.h"318#include "path/to/z/header.h"319 320void foo() {321 int a = 0;322})";323 324 EXPECT_EQ(PostCode,325 runCheckOnCode<NonSystemHeaderInserterCheck>(326 PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));327}328 329TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) {330 const char *PreCode = R"(331#include "clang_tidy/tests/insert_includes_test_header.h"332 333#include <list>334#include <map>335 336#include "path/to/a/header.h"337#include "path/to/z/header.h"338 339void foo() {340 int a = 0;341})";342 const char *PostCode = R"(343#include "clang_tidy/tests/insert_includes_test_header.h"344 345#include <list>346#include <map>347 348#include "path/to/a/header.h"349#include "path/to/header.h"350#include "path/to/z/header.h"351 352void foo() {353 int a = 0;354})";355 356 EXPECT_EQ(PostCode,357 runCheckOnCode<NonSystemHeaderInserterCheck>(358 PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));359}360 361TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {362 const char *PreCode = R"(363#include "clang_tidy/tests/insert_includes_test_header.h"364 365#include <list>366#include <map>367 368#include "path/to/a/header.h"369#include "path/to/header.h"370#include "path/to/z/header.h"371 372void foo() {373 int a = 0;374})";375 EXPECT_EQ(PreCode,376 runCheckOnCode<NonSystemHeaderInserterCheck>(377 PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));378}379 380TEST(IncludeInserterTest, InsertNonSystemIncludeAfterLastCXXSystemInclude) {381 const char *PreCode = R"(382#include "clang_tidy/tests/insert_includes_test_header.h"383 384#include <list>385#include <map>386 387void foo() {388 int a = 0;389})";390 const char *PostCode = R"(391#include "clang_tidy/tests/insert_includes_test_header.h"392 393#include <list>394#include <map>395 396#include "path/to/header.h"397 398void foo() {399 int a = 0;400})";401 402 EXPECT_EQ(PostCode,403 runCheckOnCode<NonSystemHeaderInserterCheck>(404 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));405}406 407TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) {408 const char *PreCode = R"(409#include "clang_tidy/tests/insert_includes_test_header.h"410 411void foo() {412 int a = 0;413})";414 const char *PostCode = R"(415#include "clang_tidy/tests/insert_includes_test_header.h"416 417#include "path/to/header.h"418 419void foo() {420 int a = 0;421})";422 423 EXPECT_EQ(PostCode,424 runCheckOnCode<NonSystemHeaderInserterCheck>(425 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));426}427 428TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterLastCXXSystemInclude) {429 const char *PreCode = R"(430#include "clang_tidy/tests/insert_includes_test_header.h"431 432#include <list>433#include <map>434 435#include "path/to/a/header.h"436 437void foo() {438 int a = 0;439})";440 const char *PostCode = R"(441#include "clang_tidy/tests/insert_includes_test_header.h"442 443#include <list>444#include <map>445#include <set>446 447#include "path/to/a/header.h"448 449void foo() {450 int a = 0;451})";452 453 EXPECT_EQ(PostCode,454 runCheckOnCode<CXXSystemIncludeInserterCheck>(455 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));456}457 458TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) {459 const char *PreCode = R"(460#include "clang_tidy/tests/insert_includes_test_header.h"461 462#include <vector>463 464#include "path/to/a/header.h"465 466void foo() {467 int a = 0;468})";469 const char *PostCode = R"(470#include "clang_tidy/tests/insert_includes_test_header.h"471 472#include <set>473#include <vector>474 475#include "path/to/a/header.h"476 477void foo() {478 int a = 0;479})";480 481 EXPECT_EQ(PostCode,482 runCheckOnCode<CXXSystemIncludeInserterCheck>(483 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));484}485 486TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes) {487 const char *PreCode = R"(488#include "clang_tidy/tests/insert_includes_test_header.h"489 490#include <map>491#include <vector>492 493#include "path/to/a/header.h"494 495void foo() {496 int a = 0;497})";498 const char *PostCode = R"(499#include "clang_tidy/tests/insert_includes_test_header.h"500 501#include <map>502#include <set>503#include <vector>504 505#include "path/to/a/header.h"506 507void foo() {508 int a = 0;509})";510 511 EXPECT_EQ(PostCode,512 runCheckOnCode<CXXSystemIncludeInserterCheck>(513 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));514}515 516TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) {517 const char *PreCode = R"(518#include "clang_tidy/tests/insert_includes_test_header.h"519 520#include "path/to/a/header.h"521 522void foo() {523 int a = 0;524})";525 const char *PostCode = R"(526#include "clang_tidy/tests/insert_includes_test_header.h"527 528#include <set>529 530#include "path/to/a/header.h"531 532void foo() {533 int a = 0;534})";535 536 EXPECT_EQ(PostCode,537 runCheckOnCode<CXXSystemIncludeInserterCheck>(538 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));539}540 541TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) {542 const char *PreCode = R"(543#include "clang_tidy/tests/insert_includes_test_header.h"544 545#include <stdlib.h>546 547#include "path/to/a/header.h"548 549void foo() {550 int a = 0;551})";552 const char *PostCode = R"(553#include "clang_tidy/tests/insert_includes_test_header.h"554 555#include <stdlib.h>556 557#include <set>558 559#include "path/to/a/header.h"560 561void foo() {562 int a = 0;563})";564 565 EXPECT_EQ(PostCode,566 runCheckOnCode<CXXSystemIncludeInserterCheck>(567 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));568}569 570TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeNonSystemInclude) {571 const char *PreCode = R"(572#include "path/to/a/header.h"573 574void foo() {575 int a = 0;576})";577 const char *PostCode = R"(578#include <set>579 580#include "path/to/a/header.h"581 582void foo() {583 int a = 0;584})";585 586 EXPECT_EQ(587 PostCode,588 runCheckOnCode<CXXSystemIncludeInserterCheck>(589 PreCode, "repo/clang_tidy/tests/insert_includes_test_header.cc"));590}591 592TEST(IncludeInserterTest, InsertCSystemIncludeBeforeCXXSystemInclude) {593 const char *PreCode = R"(594#include <set>595 596#include "path/to/a/header.h"597 598void foo() {599 int a = 0;600})";601 const char *PostCode = R"(602#include <stdlib.h>603 604#include <set>605 606#include "path/to/a/header.h"607 608void foo() {609 int a = 0;610})";611 612 EXPECT_EQ(613 PostCode,614 runCheckOnCode<CSystemIncludeInserterCheck>(615 PreCode, "repo/clang_tidy/tests/insert_includes_test_header.cc"));616}617 618TEST(IncludeInserterTest, InsertIncludeIfThereWasNoneBefore) {619 const char *PreCode = R"(620void foo() {621 int a = 0;622})";623 const char *PostCode = R"(#include <set>624 625 626void foo() {627 int a = 0;628})";629 630 EXPECT_EQ(631 PostCode,632 runCheckOnCode<CXXSystemIncludeInserterCheck>(633 PreCode, "repo/clang_tidy/tests/insert_includes_test_header.cc"));634}635 636TEST(IncludeInserterTest, DontInsertDuplicateIncludeEvenIfMiscategorized) {637 const char *PreCode = R"(638#include "clang_tidy/tests/insert_includes_test_header.h"639 640#include <map>641#include <set>642#include <vector>643 644#include "a/header.h"645#include "path/to/a/header.h"646#include "path/to/header.h"647 648void foo() {649 int a = 0;650})";651 652 const char *PostCode = R"(653#include "clang_tidy/tests/insert_includes_test_header.h"654 655#include <map>656#include <set>657#include <vector>658 659#include "a/header.h"660#include "path/to/a/header.h"661#include "path/to/header.h"662 663void foo() {664 int a = 0;665})";666 667 EXPECT_EQ(PostCode, runCheckOnCode<EarlyInAlphabetHeaderInserterCheck>(668 PreCode, "workspace_folder/clang_tidy/tests/"669 "insert_includes_test_header.cc"));670}671 672TEST(IncludeInserterTest, HandleOrderInSubdirectory) {673 const char *PreCode = R"(674#include "clang_tidy/tests/insert_includes_test_header.h"675 676#include <map>677#include <set>678#include <vector>679 680#include "path/to/a/header.h"681#include "path/to/header.h"682 683void foo() {684 int a = 0;685})";686 687 const char *PostCode = R"(688#include "clang_tidy/tests/insert_includes_test_header.h"689 690#include <map>691#include <set>692#include <vector>693 694#include "a/header.h"695#include "path/to/a/header.h"696#include "path/to/header.h"697 698void foo() {699 int a = 0;700})";701 702 EXPECT_EQ(PostCode, runCheckOnCode<EarlyInAlphabetHeaderInserterCheck>(703 PreCode, "workspace_folder/clang_tidy/tests/"704 "insert_includes_test_header.cc"));705}706 707TEST(IncludeInserterTest, InvalidHeaderName) {708 const char *PreCode = R"(709#include "clang_tidy/tests/insert_includes_test_header.h"710 711#include <list>712#include <map>713 714#include "path/to/a/header.h"715 716void foo() {717 int a = 0;718})";719 const char *PostCode = R"(720#include "clang_tidy/tests/insert_includes_test_header.h"721 722#include <c.h>723 724#include <d>725#include <list>726#include <map>727 728#include "a.h"729#include "b.h"730#include "path/to/a/header.h"731 732void foo() {733 int a = 0;734})";735 736 EXPECT_EQ(PostCode,737 runCheckOnCode<InvalidIncludeInserterCheck>(738 PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));739}740 741TEST(IncludeInserterTest, InsertHeaderObjectiveC) {742 const char *PreCode = R"(743#import "clang_tidy/tests/insert_includes_test_header.h"744 745void foo() {746 int a = 0;747})";748 const char *PostCode = R"(749#import "clang_tidy/tests/insert_includes_test_header.h"750 751#import "a/header.h"752 753void foo() {754 int a = 0;755})";756 757 EXPECT_EQ(758 PostCode,759 runCheckOnCode<ObjCEarlyInAlphabetHeaderInserterCheck>(760 PreCode, "repo/clang_tidy/tests/insert_includes_test_header.mm"));761}762 763TEST(IncludeInserterTest, InsertCategoryHeaderObjectiveC) {764 const char *PreCode = R"(765#import "top_level_test_header.h"766 767void foo() {768 int a = 0;769})";770 const char *PostCode = R"(771#import "top_level_test_header.h"772#import "top_level_test_header+foo.h"773 774void foo() {775 int a = 0;776})";777 778 EXPECT_EQ(PostCode, runCheckOnCode<ObjCCategoryHeaderInserterCheck>(779 PreCode, "top_level_test_header.mm"));780}781 782TEST(IncludeInserterTest, InsertGeneratedHeaderObjectiveC) {783 const char *PreCode = R"(784#import "clang_tidy/tests/insert_includes_test_header.h"785 786#include <list>787#include <map>788 789#include "path/to/a/header.h"790 791void foo() {792 int a = 0;793})";794 const char *PostCode = R"(795#import "clang_tidy/tests/insert_includes_test_header.h"796 797#include <list>798#include <map>799 800#include "path/to/a/header.h"801 802#import "clang_tidy/tests/generated_file.proto.h"803 804void foo() {805 int a = 0;806})";807 808 EXPECT_EQ(809 PostCode,810 runCheckOnCode<ObjCGeneratedHeaderInserterCheck>(811 PreCode, "repo/clang_tidy/tests/insert_includes_test_header.mm"));812}813 814} // anonymous namespace815} // namespace tidy816} // namespace clang817 818#endif819