//===--- ClangTidyTest.h - clang-tidy ---------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H #define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H #include "ClangTidy.h" #include "ClangTidyDiagnosticConsumer.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Path.h" #include #include namespace clang { namespace tidy { namespace test { class TestClangTidyAction : public ASTFrontendAction { public: TestClangTidyAction(SmallVectorImpl> &Checks, ast_matchers::MatchFinder &Finder, ClangTidyContext &Context) : Checks(Checks), Finder(Finder), Context(Context) {} private: std::unique_ptr CreateASTConsumer(CompilerInstance &Compiler, StringRef File) override { Context.setSourceManager(&Compiler.getSourceManager()); Context.setCurrentFile(File); Context.setASTContext(&Compiler.getASTContext()); for (auto &Check : Checks) { Check->registerMatchers(&Finder); Check->registerPPCallbacks(Compiler); } return Finder.newASTConsumer(); } SmallVectorImpl> &Checks; ast_matchers::MatchFinder &Finder; ClangTidyContext &Context; }; template struct CheckFactory { static void createChecks(ClangTidyContext *Context, SmallVectorImpl> &Result) { CheckFactory::createChecks(Context, Result); CheckFactory::createChecks(Context, Result); } }; template struct CheckFactory { static void createChecks(ClangTidyContext *Context, SmallVectorImpl> &Result) { Result.emplace_back(llvm::make_unique( "test-check-" + std::to_string(Result.size()), Context)); } }; template std::string runCheckOnCode(StringRef Code, std::vector *Errors = nullptr, const Twine &Filename = "input.cc", ArrayRef ExtraArgs = None, const ClangTidyOptions &ExtraOptions = ClangTidyOptions(), std::map PathsToContent = std::map()) { ClangTidyOptions Options = ExtraOptions; Options.Checks = "*"; ClangTidyContext Context(llvm::make_unique( ClangTidyGlobalOptions(), Options)); ClangTidyDiagnosticConsumer DiagConsumer(Context); std::vector Args(1, "clang-tidy"); Args.push_back("-fsyntax-only"); std::string extension(llvm::sys::path::extension(Filename.str())); if (extension == ".m" || extension == ".mm") { Args.push_back("-fobjc-abi-version=2"); Args.push_back("-fobjc-arc"); } if (extension == ".cc" || extension == ".cpp" || extension == ".mm") { Args.push_back("-std=c++11"); } Args.push_back("-Iinclude"); Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end()); Args.push_back(Filename.str()); ast_matchers::MatchFinder Finder; llvm::IntrusiveRefCntPtr InMemoryFileSystem( new vfs::InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions(), InMemoryFileSystem)); SmallVector, 1> Checks; CheckFactory::createChecks(&Context, Checks); tooling::ToolInvocation Invocation( Args, new TestClangTidyAction(Checks, Finder, Context), Files.get()); InMemoryFileSystem->addFile(Filename, 0, llvm::MemoryBuffer::getMemBuffer(Code)); for (const auto &FileContent : PathsToContent) { InMemoryFileSystem->addFile( Twine("include/") + FileContent.first, 0, llvm::MemoryBuffer::getMemBuffer(FileContent.second)); } Invocation.setDiagnosticConsumer(&DiagConsumer); if (!Invocation.run()) { std::string ErrorText; for (const auto &Error : Context.getErrors()) { ErrorText += Error.Message.Message + "\n"; } llvm::report_fatal_error(ErrorText); } DiagConsumer.finish(); tooling::Replacements Fixes; for (const ClangTidyError &Error : Context.getErrors()) { for (const auto &FileAndFixes : Error.Fix) { for (const auto &Fix : FileAndFixes.second) { auto Err = Fixes.add(Fix); // FIXME: better error handling. Keep the behavior for now. if (Err) { llvm::errs() << llvm::toString(std::move(Err)) << "\n"; return ""; } } } } if (Errors) *Errors = Context.getErrors(); auto Result = tooling::applyAllReplacements(Code, Fixes); if (!Result) { // FIXME: propogate the error. llvm::consumeError(Result.takeError()); return ""; } return *Result; } #define EXPECT_NO_CHANGES(Check, Code) \ EXPECT_EQ(Code, runCheckOnCode(Code)) } // namespace test } // namespace tidy } // namespace clang #endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H