brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.4 KiB · c1d4dae Raw
474 lines · c
1//===- unittest/Tooling/ASTMatchersTest.h - Matcher tests helpers ------===//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_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H10#define LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H11 12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/Frontend/ASTUnit.h"14#include "clang/Testing/CommandLineArgs.h"15#include "clang/Testing/TestClangConfig.h"16#include "clang/Tooling/Tooling.h"17#include "gtest/gtest.h"18 19namespace clang {20namespace ast_matchers {21 22using clang::tooling::buildASTFromCodeWithArgs;23using clang::tooling::FileContentMappings;24using clang::tooling::FrontendActionFactory;25using clang::tooling::newFrontendActionFactory;26using clang::tooling::runToolOnCodeWithArgs;27 28class BoundNodesCallback {29public:30  virtual ~BoundNodesCallback() {}31  virtual bool run(const BoundNodes *BoundNodes, ASTContext *Context) = 0;32  virtual void onEndOfTranslationUnit() {}33};34 35// If 'FindResultVerifier' is not NULL, sets *Verified to the result of36// running 'FindResultVerifier' with the bound nodes as argument.37// If 'FindResultVerifier' is NULL, sets *Verified to true when Run is called.38class VerifyMatch : public MatchFinder::MatchCallback {39public:40  VerifyMatch(std::unique_ptr<BoundNodesCallback> FindResultVerifier,41              bool *Verified)42      : Verified(Verified), FindResultReviewer(std::move(FindResultVerifier)) {}43 44  void run(const MatchFinder::MatchResult &Result) override {45    if (FindResultReviewer != nullptr) {46      *Verified |= FindResultReviewer->run(&Result.Nodes, Result.Context);47    } else {48      *Verified = true;49    }50  }51 52  void onEndOfTranslationUnit() override {53    if (FindResultReviewer)54      FindResultReviewer->onEndOfTranslationUnit();55  }56 57private:58  bool *const Verified;59  const std::unique_ptr<BoundNodesCallback> FindResultReviewer;60};61 62inline ArrayRef<TestLanguage> langCxx11OrLater() {63  static const TestLanguage Result[] = {Lang_CXX11, Lang_CXX14, Lang_CXX17,64                                        Lang_CXX20, Lang_CXX23};65  return ArrayRef<TestLanguage>(Result);66}67 68inline ArrayRef<TestLanguage> langCxx14OrLater() {69  static const TestLanguage Result[] = {Lang_CXX14, Lang_CXX17, Lang_CXX20,70                                        Lang_CXX23};71  return ArrayRef<TestLanguage>(Result);72}73 74inline ArrayRef<TestLanguage> langCxx17OrLater() {75  static const TestLanguage Result[] = {Lang_CXX17, Lang_CXX20, Lang_CXX23};76  return ArrayRef<TestLanguage>(Result);77}78 79inline ArrayRef<TestLanguage> langCxx20OrLater() {80  static const TestLanguage Result[] = {Lang_CXX20, Lang_CXX23};81  return ArrayRef<TestLanguage>(Result);82}83 84inline ArrayRef<TestLanguage> langCxx23OrLater() {85  static const TestLanguage Result[] = {Lang_CXX23};86  return ArrayRef<TestLanguage>(Result);87}88 89template <typename T>90testing::AssertionResult matchesConditionally(91    const Twine &Code, const T &AMatcher, bool ExpectMatch,92    ArrayRef<std::string> CompileArgs,93    const FileContentMappings &VirtualMappedFiles = FileContentMappings(),94    StringRef Filename = "input.cc") {95  bool Found = false, DynamicFound = false;96  MatchFinder Finder;97  VerifyMatch VerifyFound(nullptr, &Found);98  Finder.addMatcher(AMatcher, &VerifyFound);99  VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound);100  if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound))101    return testing::AssertionFailure() << "Could not add dynamic matcher";102  std::unique_ptr<FrontendActionFactory> Factory(103      newFrontendActionFactory(&Finder));104  std::vector<std::string> Args = {105      // Some tests need rtti/exceptions on.106      "-frtti", "-fexceptions",107      // Ensure that tests specify the C++ standard version that they need.108      "-Werror=c++14-extensions", "-Werror=c++17-extensions",109      "-Werror=c++20-extensions"};110  // Append additional arguments at the end to allow overriding the default111  // choices that we made above.112  llvm::copy(CompileArgs, std::back_inserter(Args));113  if (!llvm::is_contained(Args, "-target")) {114    // Use an unknown-unknown triple so we don't instantiate the full system115    // toolchain.  On Linux, instantiating the toolchain involves stat'ing116    // large portions of /usr/lib, and this slows down not only this test, but117    // all other tests, via contention in the kernel.118    //119    // FIXME: This is a hack to work around the fact that there's no way to do120    // the equivalent of runToolOnCodeWithArgs without instantiating a full121    // Driver.  We should consider having a function, at least for tests, that122    // invokes cc1.123    Args.push_back("-target");124    Args.push_back("i386-unknown-unknown");125  }126 127  if (!runToolOnCodeWithArgs(128          Factory->create(), Code, Args, Filename, "clang-tool",129          std::make_shared<PCHContainerOperations>(), VirtualMappedFiles)) {130    return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";131  }132  if (Found != DynamicFound) {133    return testing::AssertionFailure()134           << "Dynamic match result (" << DynamicFound135           << ") does not match static result (" << Found << ")";136  }137  if (!Found && ExpectMatch) {138    return testing::AssertionFailure()139           << "Could not find match in \"" << Code << "\"";140  } else if (Found && !ExpectMatch) {141    return testing::AssertionFailure()142           << "Found unexpected match in \"" << Code << "\"";143  }144  return testing::AssertionSuccess();145}146 147template <typename T>148testing::AssertionResult149matchesConditionally(const Twine &Code, const T &AMatcher, bool ExpectMatch,150                     ArrayRef<TestLanguage> TestLanguages) {151  for (auto Lang : TestLanguages) {152    auto Result = matchesConditionally(153        Code, AMatcher, ExpectMatch, getCommandLineArgsForTesting(Lang),154        FileContentMappings(), getFilenameForTesting(Lang));155    if (!Result)156      return Result;157  }158 159  return testing::AssertionSuccess();160}161 162template <typename T>163testing::AssertionResult164matches(const Twine &Code, const T &AMatcher,165        ArrayRef<TestLanguage> TestLanguages = {Lang_CXX11}) {166  return matchesConditionally(Code, AMatcher, true, TestLanguages);167}168 169template <typename T>170testing::AssertionResult171notMatches(const Twine &Code, const T &AMatcher,172           ArrayRef<TestLanguage> TestLanguages = {Lang_CXX11}) {173  return matchesConditionally(Code, AMatcher, false, TestLanguages);174}175 176template <typename T>177testing::AssertionResult matchesObjC(const Twine &Code, const T &AMatcher,178                                     bool ExpectMatch = true) {179  return matchesConditionally(Code, AMatcher, ExpectMatch,180                              {"-fobjc-nonfragile-abi", "-Wno-objc-root-class",181                               "-fblocks", "-Wno-incomplete-implementation"},182                              FileContentMappings(), "input.m");183}184 185template <typename T>186testing::AssertionResult matchesC(const Twine &Code, const T &AMatcher) {187  return matchesConditionally(Code, AMatcher, true, {}, FileContentMappings(),188                              "input.c");189}190 191template <typename T>192testing::AssertionResult notMatchesObjC(const Twine &Code, const T &AMatcher) {193  return matchesObjC(Code, AMatcher, false);194}195 196// Function based on matchesConditionally with "-x cuda" argument added and197// small CUDA header prepended to the code string.198template <typename T>199testing::AssertionResult200matchesConditionallyWithCuda(const Twine &Code, const T &AMatcher,201                             bool ExpectMatch, llvm::StringRef CompileArg) {202  const std::string CudaHeader =203      "typedef unsigned int size_t;\n"204      "#define __constant__ __attribute__((constant))\n"205      "#define __device__ __attribute__((device))\n"206      "#define __global__ __attribute__((global))\n"207      "#define __host__ __attribute__((host))\n"208      "#define __shared__ __attribute__((shared))\n"209      "struct dim3 {"210      "  unsigned x, y, z;"211      "  __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1)"212      "      : x(x), y(y), z(z) {}"213      "};"214      "typedef struct cudaStream *cudaStream_t;"215      "int cudaConfigureCall(dim3 gridSize, dim3 blockSize,"216      "                      size_t sharedSize = 0,"217      "                      cudaStream_t stream = 0);"218      "extern \"C\" unsigned __cudaPushCallConfiguration("219      "    dim3 gridDim, dim3 blockDim, size_t sharedMem = 0, void *stream = "220      "0);";221 222  bool Found = false, DynamicFound = false;223  MatchFinder Finder;224  VerifyMatch VerifyFound(nullptr, &Found);225  Finder.addMatcher(AMatcher, &VerifyFound);226  VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound);227  if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound))228    return testing::AssertionFailure() << "Could not add dynamic matcher";229  std::unique_ptr<FrontendActionFactory> Factory(230      newFrontendActionFactory(&Finder));231  // Some tests use typeof, which is a gnu extension.  Using an explicit232  // unknown-unknown triple is good for a large speedup, because it lets us233  // avoid constructing a full system triple.234  std::vector<std::string> Args = {235      "-xcuda",  "-fno-ms-extensions",     "--cuda-host-only",     "-nocudainc",236      "-target", "x86_64-unknown-unknown", std::string(CompileArg)};237  if (!runToolOnCodeWithArgs(Factory->create(), CudaHeader + Code, Args)) {238    return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";239  }240  if (Found != DynamicFound) {241    return testing::AssertionFailure()242           << "Dynamic match result (" << DynamicFound243           << ") does not match static result (" << Found << ")";244  }245  if (!Found && ExpectMatch) {246    return testing::AssertionFailure()247           << "Could not find match in \"" << Code << "\"";248  } else if (Found && !ExpectMatch) {249    return testing::AssertionFailure()250           << "Found unexpected match in \"" << Code << "\"";251  }252  return testing::AssertionSuccess();253}254 255template <typename T>256testing::AssertionResult matchesWithCuda(const Twine &Code, const T &AMatcher) {257  return matchesConditionallyWithCuda(Code, AMatcher, true, "-std=c++11");258}259 260template <typename T>261testing::AssertionResult notMatchesWithCuda(const Twine &Code,262                                            const T &AMatcher) {263  return matchesConditionallyWithCuda(Code, AMatcher, false, "-std=c++11");264}265 266template <typename T>267testing::AssertionResult matchesWithOpenMP(const Twine &Code,268                                           const T &AMatcher) {269  return matchesConditionally(Code, AMatcher, true, {"-fopenmp=libomp"});270}271 272template <typename T>273testing::AssertionResult notMatchesWithOpenMP(const Twine &Code,274                                              const T &AMatcher) {275  return matchesConditionally(Code, AMatcher, false, {"-fopenmp=libomp"});276}277 278template <typename T>279testing::AssertionResult matchesWithOpenMP51(const Twine &Code,280                                             const T &AMatcher) {281  return matchesConditionally(Code, AMatcher, true,282                              {"-fopenmp=libomp", "-fopenmp-version=51"});283}284 285template <typename T>286testing::AssertionResult notMatchesWithOpenMP51(const Twine &Code,287                                                const T &AMatcher) {288  return matchesConditionally(Code, AMatcher, false,289                              {"-fopenmp=libomp", "-fopenmp-version=51"});290}291 292template <typename T>293testing::AssertionResult matchesWithFixedpoint(const std::string &Code,294                                               const T &AMatcher) {295  return matchesConditionally(Code, AMatcher, true, {"-ffixed-point"},296                              FileContentMappings(), "input.c");297}298 299template <typename T>300testing::AssertionResult notMatchesWithFixedpoint(const std::string &Code,301                                                  const T &AMatcher) {302  return matchesConditionally(Code, AMatcher, false, {"-ffixed-point"},303                              FileContentMappings(), "input.c");304}305 306template <typename T>307testing::AssertionResult matchAndVerifyResultConditionally(308    const Twine &Code, const T &AMatcher,309    std::unique_ptr<BoundNodesCallback> FindResultVerifier, bool ExpectResult,310    ArrayRef<std::string> Args = {}, StringRef Filename = "input.cc") {311  bool VerifiedResult = false;312  MatchFinder Finder;313  VerifyMatch VerifyVerifiedResult(std::move(FindResultVerifier),314                                   &VerifiedResult);315  Finder.addMatcher(AMatcher, &VerifyVerifiedResult);316  std::unique_ptr<FrontendActionFactory> Factory(317      newFrontendActionFactory(&Finder));318  // Some tests use typeof, which is a gnu extension.  Using an explicit319  // unknown-unknown triple is good for a large speedup, because it lets us320  // avoid constructing a full system triple.321  std::vector<std::string> CompileArgs = {"-std=gnu++11", "-target",322                                          "i386-unknown-unknown"};323  // Append additional arguments at the end to allow overriding the default324  // choices that we made above.325  llvm::copy(Args, std::back_inserter(CompileArgs));326 327  if (!runToolOnCodeWithArgs(Factory->create(), Code, CompileArgs, Filename)) {328    return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";329  }330  if (!VerifiedResult && ExpectResult) {331    return testing::AssertionFailure()332           << "Could not verify result in \"" << Code << "\"";333  } else if (VerifiedResult && !ExpectResult) {334    return testing::AssertionFailure()335           << "Verified unexpected result in \"" << Code << "\"";336  }337 338  VerifiedResult = false;339  SmallString<256> Buffer;340  std::unique_ptr<ASTUnit> AST(buildASTFromCodeWithArgs(341      Code.toStringRef(Buffer), CompileArgs, Filename));342  if (!AST)343    return testing::AssertionFailure()344           << "Parsing error in \"" << Code << "\" while building AST";345  Finder.matchAST(AST->getASTContext());346  if (!VerifiedResult && ExpectResult) {347    return testing::AssertionFailure()348           << "Could not verify result in \"" << Code << "\" with AST";349  } else if (VerifiedResult && !ExpectResult) {350    return testing::AssertionFailure()351           << "Verified unexpected result in \"" << Code << "\" with AST";352  }353 354  return testing::AssertionSuccess();355}356 357// FIXME: Find better names for these functions (or document what they358// do more precisely).359template <typename T>360testing::AssertionResult361matchAndVerifyResultTrue(const Twine &Code, const T &AMatcher,362                         std::unique_ptr<BoundNodesCallback> FindResultVerifier,363                         ArrayRef<std::string> Args = {},364                         StringRef Filename = "input.cc") {365  return matchAndVerifyResultConditionally(366      Code, AMatcher, std::move(FindResultVerifier),367      /*ExpectResult=*/true, Args, Filename);368}369 370template <typename T>371testing::AssertionResult matchAndVerifyResultFalse(372    const Twine &Code, const T &AMatcher,373    std::unique_ptr<BoundNodesCallback> FindResultVerifier,374    ArrayRef<std::string> Args = {}, StringRef Filename = "input.cc") {375  return matchAndVerifyResultConditionally(376      Code, AMatcher, std::move(FindResultVerifier),377      /*ExpectResult=*/false, Args, Filename);378}379 380// Implements a run method that returns whether BoundNodes contains a381// Decl bound to Id that can be dynamically cast to T.382// Optionally checks that the check succeeded a specific number of times.383template <typename T> class VerifyIdIsBoundTo : public BoundNodesCallback {384public:385  // Create an object that checks that a node of type \c T was bound to \c Id.386  // Does not check for a certain number of matches.387  explicit VerifyIdIsBoundTo(llvm::StringRef Id)388      : Id(std::string(Id)), ExpectedCount(-1), Count(0) {}389 390  // Create an object that checks that a node of type \c T was bound to \c Id.391  // Checks that there were exactly \c ExpectedCount matches.392  VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount)393      : Id(std::string(Id)), ExpectedCount(ExpectedCount), Count(0) {}394 395  // Create an object that checks that a node of type \c T was bound to \c Id.396  // Checks that there was exactly one match with the name \c ExpectedName.397  // Note that \c T must be a NamedDecl for this to work.398  VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName,399                    int ExpectedCount = 1)400      : Id(std::string(Id)), ExpectedCount(ExpectedCount), Count(0),401        ExpectedName(std::string(ExpectedName)) {}402 403  void onEndOfTranslationUnit() override {404    if (ExpectedCount != -1) {405      EXPECT_EQ(ExpectedCount, Count);406    }407    if (!ExpectedName.empty()) {408      EXPECT_EQ(ExpectedName, Name);409    }410    Count = 0;411    Name.clear();412  }413 414  ~VerifyIdIsBoundTo() override {415    EXPECT_EQ(0, Count);416    EXPECT_EQ("", Name);417  }418 419  bool run(const BoundNodes *Nodes, ASTContext * /*Context*/) override {420    const BoundNodes::IDToNodeMap &M = Nodes->getMap();421    if (Nodes->getNodeAs<T>(Id)) {422      ++Count;423      if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {424        Name = Named->getNameAsString();425      } else if (const NestedNameSpecifier *NNS =426                     Nodes->getNodeAs<NestedNameSpecifier>(Id)) {427        llvm::raw_string_ostream OS(Name);428        NNS->print(OS, PrintingPolicy(LangOptions()));429      }430      BoundNodes::IDToNodeMap::const_iterator I = M.find(Id);431      EXPECT_NE(M.end(), I);432      if (I != M.end()) {433        EXPECT_EQ(Nodes->getNodeAs<T>(Id), I->second.get<T>());434      }435      return true;436    }437    EXPECT_TRUE(M.count(Id) == 0 ||438                M.find(Id)->second.template get<T>() == nullptr);439    return false;440  }441 442private:443  const std::string Id;444  const int ExpectedCount;445  int Count;446  const std::string ExpectedName;447  std::string Name;448};449 450class ASTMatchersTest : public ::testing::Test,451                        public ::testing::WithParamInterface<TestClangConfig> {452protected:453  template <typename T>454  testing::AssertionResult matches(const Twine &Code, const T &AMatcher) {455    const TestClangConfig &TestConfig = GetParam();456    return clang::ast_matchers::matchesConditionally(457        Code, AMatcher, /*ExpectMatch=*/true, TestConfig.getCommandLineArgs(),458        FileContentMappings(), getFilenameForTesting(TestConfig.Language));459  }460 461  template <typename T>462  testing::AssertionResult notMatches(const Twine &Code, const T &AMatcher) {463    const TestClangConfig &TestConfig = GetParam();464    return clang::ast_matchers::matchesConditionally(465        Code, AMatcher, /*ExpectMatch=*/false, TestConfig.getCommandLineArgs(),466        FileContentMappings(), getFilenameForTesting(TestConfig.Language));467  }468};469 470} // namespace ast_matchers471} // namespace clang472 473#endif // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H474