diff --git a/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h b/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h --- a/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h +++ b/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h @@ -10,7 +10,8 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_TRANSFORMER_CLANG_TIDY_CHECK_H #include "../ClangTidy.h" -#include "../utils/IncludeInserter.h" +#include "IncludeInserter.h" +#include "IncludeSorter.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Tooling/Transformer/Transformer.h" @@ -31,6 +32,13 @@ // MyCheck(StringRef Name, ClangTidyContext *Context) // : TransformerClangTidyCheck(MyCheckAsRewriteRule, Name, Context) {} // }; +// +// `TransformerClangTidyCheck` recognizes this clang-tidy option: +// +// * IncludeStyle. A string specifying which file naming convention is used by +// the source code, 'llvm' or 'google'. Default is 'llvm'. The naming +// convention influences how canonical headers are distinguished from other +// includes. class TransformerClangTidyCheck : public ClangTidyCheck { public: // \p MakeRule generates the rewrite rule to be used by the check, based on @@ -61,7 +69,8 @@ private: Optional Rule; - std::unique_ptr Inserter; + const IncludeSorter::IncludeStyle IncludeStyle; + std::unique_ptr Inserter; }; } // namespace utils diff --git a/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp --- a/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.cpp @@ -30,7 +30,10 @@ const OptionsView &)> MakeRule, StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), Rule(MakeRule(getLangOpts(), Options)) { + : ClangTidyCheck(Name, Context), Rule(MakeRule(getLangOpts(), Options)), + IncludeStyle(Options.getLocalOrGlobal("IncludeStyle", + IncludeSorter::getMapping(), + IncludeSorter::IS_LLVM)) { if (Rule) assert(llvm::all_of(Rule->Cases, hasExplanation) && "clang-tidy checks must have an explanation by default;" @@ -40,7 +43,10 @@ TransformerClangTidyCheck::TransformerClangTidyCheck(RewriteRule R, StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), Rule(std::move(R)) { + : ClangTidyCheck(Name, Context), Rule(std::move(R)), + IncludeStyle(Options.getLocalOrGlobal("IncludeStyle", + IncludeSorter::getMapping(), + IncludeSorter::IS_LLVM)) { assert(llvm::all_of(Rule->Cases, hasExplanation) && "clang-tidy checks must have an explanation by default;" " explicitly provide an empty explanation if none is desired"); @@ -53,8 +59,8 @@ if (Rule && llvm::any_of(Rule->Cases, [](const RewriteRule::Case &C) { return !C.AddedIncludes.empty(); })) { - Inserter = std::make_unique( - SM, getLangOpts(), utils::IncludeSorter::IS_LLVM); + Inserter = + std::make_unique(SM, getLangOpts(), IncludeStyle); PP->addPPCallbacks(Inserter->CreatePPCallbacks()); } } diff --git a/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp b/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp --- a/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp +++ b/clang-tools-extra/unittests/clang-tidy/TransformerClangTidyCheckTest.cpp @@ -221,6 +221,84 @@ test::runCheckOnCode>(Input)); } +class IncludeOrderCheck : public TransformerClangTidyCheck { + static RewriteRule rule() { + using namespace ::clang::ast_matchers; + RewriteRule Rule = transformer::makeRule(integerLiteral(), change(cat("5")), + cat("no message")); + addInclude(Rule, "bar.h", IncludeFormat::Quoted); + return Rule; + } + +public: + IncludeOrderCheck(StringRef Name, ClangTidyContext *Context) + : TransformerClangTidyCheck(rule(), Name, Context) {} +}; + +TEST(TransformerClangTidyCheckTest, AddIncludeObeysSortStyleLocalOption) { + std::string Input = R"cc(#include "input.h" +int h(int x) { return 3; })cc"; + + std::string TreatsAsLibraryHeader = R"cc(#include "input.h" + +#include "bar.h" +int h(int x) { return 5; })cc"; + + std::string TreatsAsNormalHeader = R"cc(#include "bar.h" +#include "input.h" +int h(int x) { return 5; })cc"; + + ClangTidyOptions Options; + std::map PathsToContent = {{"input.h", "\n"}}; + Options.CheckOptions["test-check-0.IncludeStyle"] = "llvm"; + EXPECT_EQ(TreatsAsLibraryHeader, test::runCheckOnCode( + Input, nullptr, "inputTest.cpp", None, + Options, PathsToContent)); + EXPECT_EQ(TreatsAsNormalHeader, test::runCheckOnCode( + Input, nullptr, "input_test.cpp", None, + Options, PathsToContent)); + + Options.CheckOptions["test-check-0.IncludeStyle"] = "google"; + EXPECT_EQ(TreatsAsNormalHeader, + test::runCheckOnCode( + Input, nullptr, "inputTest.cc", None, Options, PathsToContent)); + EXPECT_EQ(TreatsAsLibraryHeader, test::runCheckOnCode( + Input, nullptr, "input_test.cc", None, + Options, PathsToContent)); +} + +TEST(TransformerClangTidyCheckTest, AddIncludeObeysSortStyleGlobalOption) { + std::string Input = R"cc(#include "input.h" +int h(int x) { return 3; })cc"; + + std::string TreatsAsLibraryHeader = R"cc(#include "input.h" + +#include "bar.h" +int h(int x) { return 5; })cc"; + + std::string TreatsAsNormalHeader = R"cc(#include "bar.h" +#include "input.h" +int h(int x) { return 5; })cc"; + + ClangTidyOptions Options; + std::map PathsToContent = {{"input.h", "\n"}}; + Options.CheckOptions["IncludeStyle"] = "llvm"; + EXPECT_EQ(TreatsAsLibraryHeader, test::runCheckOnCode( + Input, nullptr, "inputTest.cpp", None, + Options, PathsToContent)); + EXPECT_EQ(TreatsAsNormalHeader, test::runCheckOnCode( + Input, nullptr, "input_test.cpp", None, + Options, PathsToContent)); + + Options.CheckOptions["IncludeStyle"] = "google"; + EXPECT_EQ(TreatsAsNormalHeader, + test::runCheckOnCode( + Input, nullptr, "inputTest.cc", None, Options, PathsToContent)); + EXPECT_EQ(TreatsAsLibraryHeader, test::runCheckOnCode( + Input, nullptr, "input_test.cc", None, + Options, PathsToContent)); +} + } // namespace } // namespace utils } // namespace tidy