//===---- IncludeInserterTest.cpp - clang-tidy ----------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "../clang-tidy/utils/IncludeInserter.h" #include "clang/Lex/Preprocessor.h" #include "clang/Frontend/CompilerInstance.h" #include "ClangTidyTest.h" #include "gtest/gtest.h" // FIXME: Canonicalize paths correctly on windows. // Currently, adding virtual files will canonicalize the paths before // storing the virtual entries. // When resolving virtual entries in the FileManager, the paths (for // example coming from a #include directive) are not canonicalized // to native paths; thus, the virtual file is not found. // This needs to be fixed in the FileManager before we can make // clang-tidy tests work. #if !defined(_WIN32) namespace clang { namespace tidy { namespace { class IncludeInserterCheckBase : public ClangTidyCheck { public: IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context) : ClangTidyCheck(CheckName, Context) {} void registerPPCallbacks(CompilerInstance &Compiler) override { Inserter.reset(new utils::IncludeInserter( Compiler.getSourceManager(), Compiler.getLangOpts(), utils::IncludeSorter::IS_Google)); Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); } void registerMatchers(ast_matchers::MatchFinder *Finder) override { Finder->addMatcher(ast_matchers::declStmt().bind("stmt"), this); } void check(const ast_matchers::MatchFinder::MatchResult &Result) override { auto Diag = diag(Result.Nodes.getNodeAs("stmt")->getLocStart(), "foo, bar"); for (StringRef header : HeadersToInclude()) { auto Fixit = Inserter->CreateIncludeInsertion( Result.SourceManager->getMainFileID(), header, IsAngledInclude()); if (Fixit) { Diag << *Fixit; } } } virtual std::vector HeadersToInclude() const = 0; virtual bool IsAngledInclude() const = 0; std::unique_ptr Inserter; }; class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase { public: NonSystemHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context) : IncludeInserterCheckBase(CheckName, Context) {} std::vector HeadersToInclude() const override { return {"path/to/header.h"}; } bool IsAngledInclude() const override { return false; } }; class EarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase { public: EarlyInAlphabetHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context) : IncludeInserterCheckBase(CheckName, Context) {} std::vector HeadersToInclude() const override { return {"a/header.h"}; } bool IsAngledInclude() const override { return false; } }; class MultipleHeaderInserterCheck : public IncludeInserterCheckBase { public: MultipleHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context) : IncludeInserterCheckBase(CheckName, Context) {} std::vector HeadersToInclude() const override { return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"}; } bool IsAngledInclude() const override { return false; } }; class CSystemIncludeInserterCheck : public IncludeInserterCheckBase { public: CSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context) : IncludeInserterCheckBase(CheckName, Context) {} std::vector HeadersToInclude() const override { return {"stdlib.h"}; } bool IsAngledInclude() const override { return true; } }; class CXXSystemIncludeInserterCheck : public IncludeInserterCheckBase { public: CXXSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context) : IncludeInserterCheckBase(CheckName, Context) {} std::vector HeadersToInclude() const override { return {"set"}; } bool IsAngledInclude() const override { return true; } }; template std::string runCheckOnCode(StringRef Code, StringRef Filename) { std::vector Errors; return test::runCheckOnCode(Code, &Errors, Filename, None, ClangTidyOptions(), {// Main file include {"clang_tidy/tests/" "insert_includes_test_header.h", "\n"}, // Non system headers {"a/header.h", "\n"}, {"path/to/a/header.h", "\n"}, {"path/to/z/header.h", "\n"}, {"path/to/header.h", "\n"}, {"path/to/header2.h", "\n"}, // Fake system headers. {"stdlib.h", "\n"}, {"unistd.h", "\n"}, {"list", "\n"}, {"map", "\n"}, {"set", "\n"}, {"vector", "\n"}}); } TEST(IncludeInserterTest, InsertAfterLastNonSystemInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" #include "path/to/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_input2.cc")); } TEST(IncludeInserterTest, InsertMultipleIncludesAndDeduplicate) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" #include "path/to/header.h" #include "path/to/header2.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_input2.cc")); } TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/z/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/header.h" #include "path/to/z/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_input2.cc")); } TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" #include "path/to/z/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" #include "path/to/header.h" #include "path/to/z/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_input2.cc")); } TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" #include "path/to/header.h" #include "path/to/z/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PreCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_input2.cc")); } TEST(IncludeInserterTest, InsertNonSystemIncludeAfterLastCXXSystemInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include "path/to/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterLastCXXSystemInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include "path/to/a/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeNonSystemInclude) { const char *PreCode = R"( #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include #include "path/to/a/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "devtools/cymbal/clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertCSystemIncludeBeforeCXXSystemInclude) { const char *PreCode = R"( #include #include "path/to/a/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include #include #include "path/to/a/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "devtools/cymbal/clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, InsertIncludeIfThereWasNoneBefore) { const char *PreCode = R"( void foo() { int a = 0; })"; const char *PostCode = R"(#include void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "devtools/cymbal/clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, DontInsertDuplicateIncludeEvenIfMiscategorized) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include #include "a/header.h" #include "path/to/a/header.h" #include "path/to/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include #include "a/header.h" #include "path/to/a/header.h" #include "path/to/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "workspace_folder/clang_tidy/tests/" "insert_includes_test_header.cc")); } TEST(IncludeInserterTest, HandleOrderInSubdirectory) { const char *PreCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include #include "path/to/a/header.h" #include "path/to/header.h" void foo() { int a = 0; })"; const char *PostCode = R"( #include "clang_tidy/tests/insert_includes_test_header.h" #include #include #include #include "a/header.h" #include "path/to/a/header.h" #include "path/to/header.h" void foo() { int a = 0; })"; EXPECT_EQ(PostCode, runCheckOnCode( PreCode, "workspace_folder/clang_tidy/tests/" "insert_includes_test_header.cc")); } } // anonymous namespace } // namespace tidy } // namespace clang #endif