brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.8 KiB · 195d6a6 Raw
183 lines · c
1//===--- ClangTidyTest.h - clang-tidy ---------------------------*- C++ -*-===//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#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H10#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H11 12#include "ClangTidy.h"13#include "ClangTidyCheck.h"14#include "ClangTidyDiagnosticConsumer.h"15#include "clang/ASTMatchers/ASTMatchFinder.h"16#include "clang/Frontend/CompilerInstance.h"17#include "clang/Frontend/FrontendActions.h"18#include "clang/Tooling/Core/Diagnostic.h"19#include "clang/Tooling/Core/Replacement.h"20#include "clang/Tooling/Refactoring.h"21#include "clang/Tooling/Tooling.h"22#include "llvm/Support/Path.h"23#include <map>24#include <memory>25 26namespace clang {27namespace tidy {28namespace test {29 30template <typename Check, typename... Checks> struct CheckFactory {31  static void32  createChecks(ClangTidyContext *Context,33               SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {34    CheckFactory<Check>::createChecks(Context, Result);35    CheckFactory<Checks...>::createChecks(Context, Result);36  }37};38 39template <typename Check> struct CheckFactory<Check> {40  static void41  createChecks(ClangTidyContext *Context,42               SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {43    Result.emplace_back(std::make_unique<Check>(44        "test-check-" + std::to_string(Result.size()), Context));45  }46};47 48template <typename... CheckTypes>49class TestClangTidyAction : public ASTFrontendAction {50public:51  TestClangTidyAction(SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks,52                      ast_matchers::MatchFinder &Finder,53                      ClangTidyContext &Context)54      : Checks(Checks), Finder(Finder), Context(Context) {}55 56private:57  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,58                                                 StringRef File) override {59    Context.setSourceManager(&Compiler.getSourceManager());60    Context.setCurrentFile(File);61    Context.setASTContext(&Compiler.getASTContext());62 63    Preprocessor *PP = &Compiler.getPreprocessor();64 65    // Checks must be created here, _after_ `Context` has been initialized, so66    // that check constructors can access the context (for example, through67    // `getLangOpts()`).68    CheckFactory<CheckTypes...>::createChecks(&Context, Checks);69    assert(!Checks.empty() && "No checks created");70    for (auto &Check : Checks) {71      assert(Check.get() && "Checks can't be null");72      if (!Check->isLanguageVersionSupported(Context.getLangOpts()))73        continue;74      Check->registerMatchers(&Finder);75      Check->registerPPCallbacks(Compiler.getSourceManager(), PP, PP);76    }77    return Finder.newASTConsumer();78  }79 80  SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks;81  ast_matchers::MatchFinder &Finder;82  ClangTidyContext &Context;83};84 85template <typename... CheckTypes>86std::string87runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors = nullptr,88               const Twine &Filename = "input.cc",89               ArrayRef<std::string> ExtraArgs = {},90               const ClangTidyOptions &ExtraOptions = ClangTidyOptions(),91               std::map<StringRef, StringRef> PathsToContent =92                   std::map<StringRef, StringRef>()) {93  static_assert(sizeof...(CheckTypes) > 0, "No checks specified");94  ClangTidyOptions Options = ExtraOptions;95  Options.Checks = "*";96  ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(97                               ClangTidyGlobalOptions(), Options),98                           false, false, false);99  ClangTidyDiagnosticConsumer DiagConsumer(Context);100  auto DiagOpts = std::make_unique<DiagnosticOptions>();101  DiagnosticsEngine DE(DiagnosticIDs::create(), *DiagOpts, &DiagConsumer,102                       false);103  Context.setDiagnosticsEngine(std::move(DiagOpts), &DE);104 105  std::vector<std::string> Args(1, "clang-tidy");106  Args.push_back("-fsyntax-only");107  Args.push_back("-fno-delayed-template-parsing");108  std::string extension(109      std::string(llvm::sys::path::extension(Filename.str())));110  if (extension == ".m" || extension == ".mm") {111    Args.push_back("-fobjc-abi-version=2");112    Args.push_back("-fobjc-arc");113  }114  if (extension == ".cc" || extension == ".cpp" || extension == ".mm") {115    Args.push_back("-std=c++20");116  }117  Args.push_back("-Iinclude");118  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());119  Args.push_back(Filename.str());120 121  ast_matchers::MatchFinder Finder;122  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(123      new llvm::vfs::InMemoryFileSystem);124  llvm::IntrusiveRefCntPtr<FileManager> Files(125      new FileManager(FileSystemOptions(), InMemoryFileSystem));126 127  SmallVector<std::unique_ptr<ClangTidyCheck>, sizeof...(CheckTypes)> Checks;128  tooling::ToolInvocation Invocation(129      Args,130      std::make_unique<TestClangTidyAction<CheckTypes...>>(Checks, Finder,131                                                           Context),132      Files.get());133  InMemoryFileSystem->addFile(Filename, 0,134                              llvm::MemoryBuffer::getMemBuffer(Code));135  for (const auto &FileContent : PathsToContent) {136    InMemoryFileSystem->addFile(137        Twine("include/") + FileContent.first, 0,138        llvm::MemoryBuffer::getMemBuffer(FileContent.second));139  }140  Invocation.setDiagnosticConsumer(&DiagConsumer);141  if (!Invocation.run()) {142    std::string ErrorText;143    for (const auto &Error : DiagConsumer.take()) {144      ErrorText += Error.Message.Message + "\n";145    }146    llvm::report_fatal_error(llvm::Twine(ErrorText));147  }148 149  tooling::Replacements Fixes;150  std::vector<ClangTidyError> Diags = DiagConsumer.take();151  for (const ClangTidyError &Error : Diags) {152    if (const auto *ChosenFix = tooling::selectFirstFix(Error))153      for (const auto &FileAndFixes : *ChosenFix) {154        for (const auto &Fix : FileAndFixes.second) {155          auto Err = Fixes.add(Fix);156          // FIXME: better error handling. Keep the behavior for now.157          if (Err) {158            llvm::errs() << llvm::toString(std::move(Err)) << "\n";159            return "";160          }161        }162      }163  }164  if (Errors)165    *Errors = std::move(Diags);166  auto Result = tooling::applyAllReplacements(Code, Fixes);167  if (!Result) {168    // FIXME: propagate the error.169    llvm::consumeError(Result.takeError());170    return "";171  }172  return *Result;173}174 175#define EXPECT_NO_CHANGES(Check, Code)                                         \176  EXPECT_EQ(Code, runCheckOnCode<Check>(Code))177 178} // namespace test179} // namespace tidy180} // namespace clang181 182#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H183