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(BoundNodesCallback *FindResultVerifier, bool *Verified) 41 : Verified(Verified), FindResultReviewer(FindResultVerifier) {} 42 43 virtual 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 BoundNodesCallback *const 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 bool Found = false, DynamicFound = false; 67 MatchFinder Finder; 68 VerifyMatch VerifyFound(nullptr, &Found); 69 Finder.addMatcher(AMatcher, &VerifyFound); 70 VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound); 71 if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound)) 72 return testing::AssertionFailure() << "Could not add dynamic matcher"; 73 std::unique_ptr<FrontendActionFactory> Factory( 74 newFrontendActionFactory(&Finder)); 75 // Some tests use typeof, which is a gnu extension. 76 std::vector<std::string> Args(1, CompileArg); 77 if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, "input.cc", 78 VirtualMappedFiles)) { 79 return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; 80 } 81 if (Found != DynamicFound) { 82 return testing::AssertionFailure() << "Dynamic match result (" 83 << DynamicFound 84 << ") does not match static result (" 85 << Found << ")"; 86 } 87 if (!Found && ExpectMatch) { 88 return testing::AssertionFailure() 89 << "Could not find match in \"" << Code << "\""; 90 } else if (Found && !ExpectMatch) { 91 return testing::AssertionFailure() 92 << "Found unexpected match in \"" << Code << "\""; 93 } 94 return testing::AssertionSuccess(); 95 } 96 97 template <typename T> 98 testing::AssertionResult matches(const std::string &Code, const T &AMatcher) { 99 return matchesConditionally(Code, AMatcher, true, "-std=c++11"); 100 } 101 102 template <typename T> 103 testing::AssertionResult notMatches(const std::string &Code, 104 const T &AMatcher) { 105 return matchesConditionally(Code, AMatcher, false, "-std=c++11"); 106 } 107 108 // Function based on matchesConditionally with "-x cuda" argument added and 109 // small CUDA header prepended to the code string. 110 template <typename T> 111 testing::AssertionResult matchesConditionallyWithCuda( 112 const std::string &Code, const T &AMatcher, bool ExpectMatch, 113 llvm::StringRef CompileArg) { 114 const std::string CudaHeader = 115 "typedef unsigned int size_t;\n" 116 "#define __constant__ __attribute__((constant))\n" 117 "#define __device__ __attribute__((device))\n" 118 "#define __global__ __attribute__((global))\n" 119 "#define __host__ __attribute__((host))\n" 120 "#define __shared__ __attribute__((shared))\n" 121 "struct dim3 {" 122 " unsigned x, y, z;" 123 " __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1)" 124 " : x(x), y(y), z(z) {}" 125 "};" 126 "typedef struct cudaStream *cudaStream_t;" 127 "int cudaConfigureCall(dim3 gridSize, dim3 blockSize," 128 " size_t sharedSize = 0," 129 " cudaStream_t stream = 0);"; 130 131 bool Found = false, DynamicFound = false; 132 MatchFinder Finder; 133 VerifyMatch VerifyFound(nullptr, &Found); 134 Finder.addMatcher(AMatcher, &VerifyFound); 135 VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound); 136 if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound)) 137 return testing::AssertionFailure() << "Could not add dynamic matcher"; 138 std::unique_ptr<FrontendActionFactory> Factory( 139 newFrontendActionFactory(&Finder)); 140 // Some tests use typeof, which is a gnu extension. 141 std::vector<std::string> Args; 142 Args.push_back("-xcuda"); 143 Args.push_back("-fno-ms-extensions"); 144 Args.push_back(CompileArg); 145 if (!runToolOnCodeWithArgs(Factory->create(), 146 CudaHeader + Code, Args)) { 147 return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; 148 } 149 if (Found != DynamicFound) { 150 return testing::AssertionFailure() << "Dynamic match result (" 151 << DynamicFound 152 << ") does not match static result (" 153 << Found << ")"; 154 } 155 if (!Found && ExpectMatch) { 156 return testing::AssertionFailure() 157 << "Could not find match in \"" << Code << "\""; 158 } else if (Found && !ExpectMatch) { 159 return testing::AssertionFailure() 160 << "Found unexpected match in \"" << Code << "\""; 161 } 162 return testing::AssertionSuccess(); 163 } 164 165 template <typename T> 166 testing::AssertionResult matchesWithCuda(const std::string &Code, 167 const T &AMatcher) { 168 return matchesConditionallyWithCuda(Code, AMatcher, true, "-std=c++11"); 169 } 170 171 template <typename T> 172 testing::AssertionResult notMatchesWithCuda(const std::string &Code, 173 const T &AMatcher) { 174 return matchesConditionallyWithCuda(Code, AMatcher, false, "-std=c++11"); 175 } 176 177 template <typename T> 178 testing::AssertionResult 179 matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, 180 BoundNodesCallback *FindResultVerifier, 181 bool ExpectResult) { 182 std::unique_ptr<BoundNodesCallback> ScopedVerifier(FindResultVerifier); 183 bool VerifiedResult = false; 184 MatchFinder Finder; 185 VerifyMatch VerifyVerifiedResult(FindResultVerifier, &VerifiedResult); 186 Finder.addMatcher(AMatcher, &VerifyVerifiedResult); 187 std::unique_ptr<FrontendActionFactory> Factory( 188 newFrontendActionFactory(&Finder)); 189 // Some tests use typeof, which is a gnu extension. 190 std::vector<std::string> Args(1, "-std=gnu++98"); 191 if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { 192 return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; 193 } 194 if (!VerifiedResult && ExpectResult) { 195 return testing::AssertionFailure() 196 << "Could not verify result in \"" << Code << "\""; 197 } else if (VerifiedResult && !ExpectResult) { 198 return testing::AssertionFailure() 199 << "Verified unexpected result in \"" << Code << "\""; 200 } 201 202 VerifiedResult = false; 203 std::unique_ptr<ASTUnit> AST(buildASTFromCodeWithArgs(Code, Args)); 204 if (!AST.get()) 205 return testing::AssertionFailure() << "Parsing error in \"" << Code 206 << "\" while building AST"; 207 Finder.matchAST(AST->getASTContext()); 208 if (!VerifiedResult && ExpectResult) { 209 return testing::AssertionFailure() 210 << "Could not verify result in \"" << Code << "\" with AST"; 211 } else if (VerifiedResult && !ExpectResult) { 212 return testing::AssertionFailure() 213 << "Verified unexpected result in \"" << Code << "\" with AST"; 214 } 215 216 return testing::AssertionSuccess(); 217 } 218 219 // FIXME: Find better names for these functions (or document what they 220 // do more precisely). 221 template <typename T> 222 testing::AssertionResult 223 matchAndVerifyResultTrue(const std::string &Code, const T &AMatcher, 224 BoundNodesCallback *FindResultVerifier) { 225 return matchAndVerifyResultConditionally( 226 Code, AMatcher, FindResultVerifier, true); 227 } 228 229 template <typename T> 230 testing::AssertionResult 231 matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher, 232 BoundNodesCallback *FindResultVerifier) { 233 return matchAndVerifyResultConditionally( 234 Code, AMatcher, FindResultVerifier, false); 235 } 236 237 } // end namespace ast_matchers 238 } // end namespace clang 239 240 #endif // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H 241