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