Home | History | Annotate | Download | only in ASTMatchers
      1 //===- unittest/Tooling/ASTMatchersTest.h - Matcher tests helpers ------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #ifndef LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H
     11 #define LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H
     12 
     13 #include "clang/ASTMatchers/ASTMatchFinder.h"
     14 #include "clang/Frontend/ASTUnit.h"
     15 #include "clang/Tooling/Tooling.h"
     16 #include "gtest/gtest.h"
     17 
     18 namespace clang {
     19 namespace ast_matchers {
     20 
     21 using clang::tooling::buildASTFromCodeWithArgs;
     22 using clang::tooling::newFrontendActionFactory;
     23 using clang::tooling::runToolOnCodeWithArgs;
     24 using clang::tooling::FrontendActionFactory;
     25 using clang::tooling::FileContentMappings;
     26 
     27 class BoundNodesCallback {
     28 public:
     29   virtual ~BoundNodesCallback() {}
     30   virtual bool run(const BoundNodes *BoundNodes) = 0;
     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 of
     36 // running 'FindResultVerifier' with the bound nodes as argument.
     37 // If 'FindResultVerifier' is NULL, sets *Verified to true when Run is called.
     38 class VerifyMatch : public MatchFinder::MatchCallback {
     39 public:
     40   VerifyMatch(std::unique_ptr<BoundNodesCallback> FindResultVerifier, bool *Verified)
     41       : Verified(Verified), FindResultReviewer(std::move(FindResultVerifier)) {}
     42 
     43   void run(const MatchFinder::MatchResult &Result) override {
     44     if (FindResultReviewer != nullptr) {
     45       *Verified |= FindResultReviewer->run(&Result.Nodes, Result.Context);
     46     } else {
     47       *Verified = true;
     48     }
     49   }
     50 
     51   void onEndOfTranslationUnit() override {
     52     if (FindResultReviewer)
     53       FindResultReviewer->onEndOfTranslationUnit();
     54   }
     55 
     56 private:
     57   bool *const Verified;
     58   const std::unique_ptr<BoundNodesCallback> FindResultReviewer;
     59 };
     60 
     61 template <typename T>
     62 testing::AssertionResult matchesConditionally(
     63     const std::string &Code, const T &AMatcher, bool ExpectMatch,
     64     llvm::StringRef CompileArg,
     65     const FileContentMappings &VirtualMappedFiles = FileContentMappings(),
     66     const std::string &Filename = "input.cc") {
     67   bool Found = false, DynamicFound = false;
     68   MatchFinder Finder;
     69   VerifyMatch VerifyFound(nullptr, &Found);
     70   Finder.addMatcher(AMatcher, &VerifyFound);
     71   VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound);
     72   if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound))
     73     return testing::AssertionFailure() << "Could not add dynamic matcher";
     74   std::unique_ptr<FrontendActionFactory> Factory(
     75       newFrontendActionFactory(&Finder));
     76   // Some tests need rtti/exceptions on.  Use an unknown-unknown triple so we
     77   // don't instantiate the full system toolchain.  On Linux, instantiating the
     78   // toolchain involves stat'ing large portions of /usr/lib, and this slows down
     79   // not only this test, but all other tests, via contention in the kernel.
     80   //
     81   // FIXME: This is a hack to work around the fact that there's no way to do the
     82   // equivalent of runToolOnCodeWithArgs without instantiating a full Driver.
     83   // We should consider having a function, at least for tests, that invokes cc1.
     84   std::vector<std::string> Args = {CompileArg, "-frtti", "-fexceptions",
     85                                    "-target", "i386-unknown-unknown"};
     86   if (!runToolOnCodeWithArgs(
     87           Factory->create(), Code, Args, Filename, "clang-tool",
     88           std::make_shared<PCHContainerOperations>(), VirtualMappedFiles)) {
     89     return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
     90   }
     91   if (Found != DynamicFound) {
     92     return testing::AssertionFailure() << "Dynamic match result ("
     93                                        << DynamicFound
     94                                        << ") does not match static result ("
     95                                        << Found << ")";
     96   }
     97   if (!Found && ExpectMatch) {
     98     return testing::AssertionFailure()
     99       << "Could not find match in \"" << Code << "\"";
    100   } else if (Found && !ExpectMatch) {
    101     return testing::AssertionFailure()
    102       << "Found unexpected match in \"" << Code << "\"";
    103   }
    104   return testing::AssertionSuccess();
    105 }
    106 
    107 template <typename T>
    108 testing::AssertionResult matches(const std::string &Code, const T &AMatcher) {
    109   return matchesConditionally(Code, AMatcher, true, "-std=c++11");
    110 }
    111 
    112 template <typename T>
    113 testing::AssertionResult notMatches(const std::string &Code,
    114                                     const T &AMatcher) {
    115   return matchesConditionally(Code, AMatcher, false, "-std=c++11");
    116 }
    117 
    118 template <typename T>
    119 testing::AssertionResult matchesObjC(const std::string &Code,
    120                                      const T &AMatcher) {
    121   return matchesConditionally(
    122     Code, AMatcher, true,
    123     "", FileContentMappings(), "input.m");
    124 }
    125 
    126 template <typename T>
    127 testing::AssertionResult matchesC(const std::string &Code, const T &AMatcher) {
    128   return matchesConditionally(Code, AMatcher, true, "", FileContentMappings(),
    129                               "input.c");
    130 }
    131 
    132 template <typename T>
    133 testing::AssertionResult matchesC99(const std::string &Code,
    134                                     const T &AMatcher) {
    135   return matchesConditionally(Code, AMatcher, true, "-std=c99",
    136                               FileContentMappings(), "input.c");
    137 }
    138 
    139 template <typename T>
    140 testing::AssertionResult notMatchesC(const std::string &Code,
    141                                      const T &AMatcher) {
    142   return matchesConditionally(Code, AMatcher, false, "", FileContentMappings(),
    143                               "input.c");
    144 }
    145 
    146 template <typename T>
    147 testing::AssertionResult notMatchesObjC(const std::string &Code,
    148                                      const T &AMatcher) {
    149   return matchesConditionally(
    150     Code, AMatcher, false,
    151     "", FileContentMappings(), "input.m");
    152 }
    153 
    154 
    155 // Function based on matchesConditionally with "-x cuda" argument added and
    156 // small CUDA header prepended to the code string.
    157 template <typename T>
    158 testing::AssertionResult matchesConditionallyWithCuda(
    159     const std::string &Code, const T &AMatcher, bool ExpectMatch,
    160     llvm::StringRef CompileArg) {
    161   const std::string CudaHeader =
    162       "typedef unsigned int size_t;\n"
    163       "#define __constant__ __attribute__((constant))\n"
    164       "#define __device__ __attribute__((device))\n"
    165       "#define __global__ __attribute__((global))\n"
    166       "#define __host__ __attribute__((host))\n"
    167       "#define __shared__ __attribute__((shared))\n"
    168       "struct dim3 {"
    169       "  unsigned x, y, z;"
    170       "  __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1)"
    171       "      : x(x), y(y), z(z) {}"
    172       "};"
    173       "typedef struct cudaStream *cudaStream_t;"
    174       "int cudaConfigureCall(dim3 gridSize, dim3 blockSize,"
    175       "                      size_t sharedSize = 0,"
    176       "                      cudaStream_t stream = 0);";
    177 
    178   bool Found = false, DynamicFound = false;
    179   MatchFinder Finder;
    180   VerifyMatch VerifyFound(nullptr, &Found);
    181   Finder.addMatcher(AMatcher, &VerifyFound);
    182   VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound);
    183   if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound))
    184     return testing::AssertionFailure() << "Could not add dynamic matcher";
    185   std::unique_ptr<FrontendActionFactory> Factory(
    186       newFrontendActionFactory(&Finder));
    187   // Some tests use typeof, which is a gnu extension.  Using an explicit
    188   // unknown-unknown triple is good for a large speedup, because it lets us
    189   // avoid constructing a full system triple.
    190   std::vector<std::string> Args = {
    191       "-xcuda",  "-fno-ms-extensions",      "--cuda-host-only", "-nocudainc",
    192       "-target", "nvptx64-unknown-unknown", CompileArg};
    193   if (!runToolOnCodeWithArgs(Factory->create(),
    194                              CudaHeader + Code, Args)) {
    195     return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
    196   }
    197   if (Found != DynamicFound) {
    198     return testing::AssertionFailure() << "Dynamic match result ("
    199                                        << DynamicFound
    200                                        << ") does not match static result ("
    201                                        << Found << ")";
    202   }
    203   if (!Found && ExpectMatch) {
    204     return testing::AssertionFailure()
    205       << "Could not find match in \"" << Code << "\"";
    206   } else if (Found && !ExpectMatch) {
    207     return testing::AssertionFailure()
    208       << "Found unexpected match in \"" << Code << "\"";
    209   }
    210   return testing::AssertionSuccess();
    211 }
    212 
    213 template <typename T>
    214 testing::AssertionResult matchesWithCuda(const std::string &Code,
    215                                          const T &AMatcher) {
    216   return matchesConditionallyWithCuda(Code, AMatcher, true, "-std=c++11");
    217 }
    218 
    219 template <typename T>
    220 testing::AssertionResult notMatchesWithCuda(const std::string &Code,
    221                                     const T &AMatcher) {
    222   return matchesConditionallyWithCuda(Code, AMatcher, false, "-std=c++11");
    223 }
    224 
    225 template <typename T>
    226 testing::AssertionResult
    227 matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher,
    228                                   std::unique_ptr<BoundNodesCallback> FindResultVerifier,
    229                                   bool ExpectResult) {
    230   bool VerifiedResult = false;
    231   MatchFinder Finder;
    232   VerifyMatch VerifyVerifiedResult(std::move(FindResultVerifier), &VerifiedResult);
    233   Finder.addMatcher(AMatcher, &VerifyVerifiedResult);
    234   std::unique_ptr<FrontendActionFactory> Factory(
    235       newFrontendActionFactory(&Finder));
    236   // Some tests use typeof, which is a gnu extension.  Using an explicit
    237   // unknown-unknown triple is good for a large speedup, because it lets us
    238   // avoid constructing a full system triple.
    239   std::vector<std::string> Args = {"-std=gnu++98", "-target",
    240                                    "i386-unknown-unknown"};
    241   if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) {
    242     return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
    243   }
    244   if (!VerifiedResult && ExpectResult) {
    245     return testing::AssertionFailure()
    246       << "Could not verify result in \"" << Code << "\"";
    247   } else if (VerifiedResult && !ExpectResult) {
    248     return testing::AssertionFailure()
    249       << "Verified unexpected result in \"" << Code << "\"";
    250   }
    251 
    252   VerifiedResult = false;
    253   std::unique_ptr<ASTUnit> AST(buildASTFromCodeWithArgs(Code, Args));
    254   if (!AST.get())
    255     return testing::AssertionFailure() << "Parsing error in \"" << Code
    256                                        << "\" while building AST";
    257   Finder.matchAST(AST->getASTContext());
    258   if (!VerifiedResult && ExpectResult) {
    259     return testing::AssertionFailure()
    260       << "Could not verify result in \"" << Code << "\" with AST";
    261   } else if (VerifiedResult && !ExpectResult) {
    262     return testing::AssertionFailure()
    263       << "Verified unexpected result in \"" << Code << "\" with AST";
    264   }
    265 
    266   return testing::AssertionSuccess();
    267 }
    268 
    269 // FIXME: Find better names for these functions (or document what they
    270 // do more precisely).
    271 template <typename T>
    272 testing::AssertionResult
    273 matchAndVerifyResultTrue(const std::string &Code, const T &AMatcher,
    274                          std::unique_ptr<BoundNodesCallback> FindResultVerifier) {
    275   return matchAndVerifyResultConditionally(
    276       Code, AMatcher, std::move(FindResultVerifier), true);
    277 }
    278 
    279 template <typename T>
    280 testing::AssertionResult
    281 matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher,
    282                           std::unique_ptr<BoundNodesCallback> FindResultVerifier) {
    283   return matchAndVerifyResultConditionally(
    284       Code, AMatcher, std::move(FindResultVerifier), false);
    285 }
    286 
    287 // Implements a run method that returns whether BoundNodes contains a
    288 // Decl bound to Id that can be dynamically cast to T.
    289 // Optionally checks that the check succeeded a specific number of times.
    290 template <typename T>
    291 class VerifyIdIsBoundTo : public BoundNodesCallback {
    292 public:
    293   // Create an object that checks that a node of type \c T was bound to \c Id.
    294   // Does not check for a certain number of matches.
    295   explicit VerifyIdIsBoundTo(llvm::StringRef Id)
    296     : Id(Id), ExpectedCount(-1), Count(0) {}
    297 
    298   // Create an object that checks that a node of type \c T was bound to \c Id.
    299   // Checks that there were exactly \c ExpectedCount matches.
    300   VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount)
    301     : Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
    302 
    303   // Create an object that checks that a node of type \c T was bound to \c Id.
    304   // Checks that there was exactly one match with the name \c ExpectedName.
    305   // Note that \c T must be a NamedDecl for this to work.
    306   VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName,
    307                     int ExpectedCount = 1)
    308     : Id(Id), ExpectedCount(ExpectedCount), Count(0),
    309       ExpectedName(ExpectedName) {}
    310 
    311   void onEndOfTranslationUnit() override {
    312     if (ExpectedCount != -1)
    313       EXPECT_EQ(ExpectedCount, Count);
    314     if (!ExpectedName.empty())
    315       EXPECT_EQ(ExpectedName, Name);
    316     Count = 0;
    317     Name.clear();
    318   }
    319 
    320   ~VerifyIdIsBoundTo() override {
    321     EXPECT_EQ(0, Count);
    322     EXPECT_EQ("", Name);
    323   }
    324 
    325   bool run(const BoundNodes *Nodes) override {
    326     const BoundNodes::IDToNodeMap &M = Nodes->getMap();
    327     if (Nodes->getNodeAs<T>(Id)) {
    328       ++Count;
    329       if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
    330         Name = Named->getNameAsString();
    331       } else if (const NestedNameSpecifier *NNS =
    332         Nodes->getNodeAs<NestedNameSpecifier>(Id)) {
    333         llvm::raw_string_ostream OS(Name);
    334         NNS->print(OS, PrintingPolicy(LangOptions()));
    335       }
    336       BoundNodes::IDToNodeMap::const_iterator I = M.find(Id);
    337       EXPECT_NE(M.end(), I);
    338       if (I != M.end())
    339         EXPECT_EQ(Nodes->getNodeAs<T>(Id), I->second.get<T>());
    340       return true;
    341     }
    342     EXPECT_TRUE(M.count(Id) == 0 ||
    343       M.find(Id)->second.template get<T>() == nullptr);
    344     return false;
    345   }
    346 
    347   bool run(const BoundNodes *Nodes, ASTContext *Context) override {
    348     return run(Nodes);
    349   }
    350 
    351 private:
    352   const std::string Id;
    353   const int ExpectedCount;
    354   int Count;
    355   const std::string ExpectedName;
    356   std::string Name;
    357 };
    358 
    359 } // namespace ast_matchers
    360 } // namespace clang
    361 
    362 #endif  // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
    363