1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// 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 #include "clang/AST/ASTConsumer.h" 11 #include "clang/AST/DeclCXX.h" 12 #include "clang/AST/DeclGroup.h" 13 #include "clang/Frontend/ASTUnit.h" 14 #include "clang/Frontend/CompilerInstance.h" 15 #include "clang/Frontend/FrontendAction.h" 16 #include "clang/Frontend/FrontendActions.h" 17 #include "clang/Tooling/CompilationDatabase.h" 18 #include "clang/Tooling/Tooling.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/Config/llvm-config.h" 21 #include "llvm/Support/TargetSelect.h" 22 #include "llvm/Support/TargetRegistry.h" 23 #include "gtest/gtest.h" 24 #include <algorithm> 25 #include <string> 26 27 namespace clang { 28 namespace tooling { 29 30 namespace { 31 /// Takes an ast consumer and returns it from CreateASTConsumer. This only 32 /// works with single translation unit compilations. 33 class TestAction : public clang::ASTFrontendAction { 34 public: 35 /// Takes ownership of TestConsumer. 36 explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer) 37 : TestConsumer(std::move(TestConsumer)) {} 38 39 protected: 40 std::unique_ptr<clang::ASTConsumer> 41 CreateASTConsumer(clang::CompilerInstance &compiler, 42 StringRef dummy) override { 43 /// TestConsumer will be deleted by the framework calling us. 44 return std::move(TestConsumer); 45 } 46 47 private: 48 std::unique_ptr<clang::ASTConsumer> TestConsumer; 49 }; 50 51 class FindTopLevelDeclConsumer : public clang::ASTConsumer { 52 public: 53 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) 54 : FoundTopLevelDecl(FoundTopLevelDecl) {} 55 bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override { 56 *FoundTopLevelDecl = true; 57 return true; 58 } 59 private: 60 bool * const FoundTopLevelDecl; 61 }; 62 } // end namespace 63 64 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 65 bool FoundTopLevelDecl = false; 66 EXPECT_TRUE( 67 runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>( 68 &FoundTopLevelDecl)), 69 "")); 70 EXPECT_FALSE(FoundTopLevelDecl); 71 } 72 73 namespace { 74 class FindClassDeclXConsumer : public clang::ASTConsumer { 75 public: 76 FindClassDeclXConsumer(bool *FoundClassDeclX) 77 : FoundClassDeclX(FoundClassDeclX) {} 78 bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override { 79 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( 80 *GroupRef.begin())) { 81 if (Record->getName() == "X") { 82 *FoundClassDeclX = true; 83 } 84 } 85 return true; 86 } 87 private: 88 bool *FoundClassDeclX; 89 }; 90 bool FindClassDeclX(ASTUnit *AST) { 91 for (std::vector<Decl *>::iterator i = AST->top_level_begin(), 92 e = AST->top_level_end(); 93 i != e; ++i) { 94 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) { 95 if (Record->getName() == "X") { 96 return true; 97 } 98 } 99 } 100 return false; 101 } 102 } // end namespace 103 104 TEST(runToolOnCode, FindsClassDecl) { 105 bool FoundClassDeclX = false; 106 EXPECT_TRUE( 107 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( 108 &FoundClassDeclX)), 109 "class X;")); 110 EXPECT_TRUE(FoundClassDeclX); 111 112 FoundClassDeclX = false; 113 EXPECT_TRUE( 114 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( 115 &FoundClassDeclX)), 116 "class Y;")); 117 EXPECT_FALSE(FoundClassDeclX); 118 } 119 120 TEST(buildASTFromCode, FindsClassDecl) { 121 std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;"); 122 ASSERT_TRUE(AST.get()); 123 EXPECT_TRUE(FindClassDeclX(AST.get())); 124 125 AST = buildASTFromCode("class Y;"); 126 ASSERT_TRUE(AST.get()); 127 EXPECT_FALSE(FindClassDeclX(AST.get())); 128 } 129 130 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 131 std::unique_ptr<FrontendActionFactory> Factory( 132 newFrontendActionFactory<SyntaxOnlyAction>()); 133 std::unique_ptr<FrontendAction> Action(Factory->create()); 134 EXPECT_TRUE(Action.get() != nullptr); 135 } 136 137 struct IndependentFrontendActionCreator { 138 std::unique_ptr<ASTConsumer> newASTConsumer() { 139 return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr); 140 } 141 }; 142 143 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { 144 IndependentFrontendActionCreator Creator; 145 std::unique_ptr<FrontendActionFactory> Factory( 146 newFrontendActionFactory(&Creator)); 147 std::unique_ptr<FrontendAction> Action(Factory->create()); 148 EXPECT_TRUE(Action.get() != nullptr); 149 } 150 151 TEST(ToolInvocation, TestMapVirtualFile) { 152 llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem( 153 new vfs::OverlayFileSystem(vfs::getRealFileSystem())); 154 llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( 155 new vfs::InMemoryFileSystem); 156 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 157 llvm::IntrusiveRefCntPtr<FileManager> Files( 158 new FileManager(FileSystemOptions(), OverlayFileSystem)); 159 std::vector<std::string> Args; 160 Args.push_back("tool-executable"); 161 Args.push_back("-Idef"); 162 Args.push_back("-fsyntax-only"); 163 Args.push_back("test.cpp"); 164 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, 165 Files.get()); 166 InMemoryFileSystem->addFile( 167 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 168 InMemoryFileSystem->addFile("def/abc", 0, 169 llvm::MemoryBuffer::getMemBuffer("\n")); 170 EXPECT_TRUE(Invocation.run()); 171 } 172 173 TEST(ToolInvocation, TestVirtualModulesCompilation) { 174 // FIXME: Currently, this only tests that we don't exit with an error if a 175 // mapped module.map is found on the include path. In the future, expand this 176 // test to run a full modules enabled compilation, so we make sure we can 177 // rerun modules compilations with a virtual file system. 178 llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem( 179 new vfs::OverlayFileSystem(vfs::getRealFileSystem())); 180 llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( 181 new vfs::InMemoryFileSystem); 182 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 183 llvm::IntrusiveRefCntPtr<FileManager> Files( 184 new FileManager(FileSystemOptions(), OverlayFileSystem)); 185 std::vector<std::string> Args; 186 Args.push_back("tool-executable"); 187 Args.push_back("-Idef"); 188 Args.push_back("-fsyntax-only"); 189 Args.push_back("test.cpp"); 190 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, 191 Files.get()); 192 InMemoryFileSystem->addFile( 193 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 194 InMemoryFileSystem->addFile("def/abc", 0, 195 llvm::MemoryBuffer::getMemBuffer("\n")); 196 // Add a module.map file in the include directory of our header, so we trigger 197 // the module.map header search logic. 198 InMemoryFileSystem->addFile("def/module.map", 0, 199 llvm::MemoryBuffer::getMemBuffer("\n")); 200 EXPECT_TRUE(Invocation.run()); 201 } 202 203 struct VerifyEndCallback : public SourceFileCallbacks { 204 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 205 bool handleBeginSource(CompilerInstance &CI, StringRef Filename) override { 206 ++BeginCalled; 207 return true; 208 } 209 void handleEndSource() override { ++EndCalled; } 210 std::unique_ptr<ASTConsumer> newASTConsumer() { 211 return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched); 212 } 213 unsigned BeginCalled; 214 unsigned EndCalled; 215 bool Matched; 216 }; 217 218 #if !defined(LLVM_ON_WIN32) 219 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 220 VerifyEndCallback EndCallback; 221 222 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 223 std::vector<std::string> Sources; 224 Sources.push_back("/a.cc"); 225 Sources.push_back("/b.cc"); 226 ClangTool Tool(Compilations, Sources); 227 228 Tool.mapVirtualFile("/a.cc", "void a() {}"); 229 Tool.mapVirtualFile("/b.cc", "void b() {}"); 230 231 std::unique_ptr<FrontendActionFactory> Action( 232 newFrontendActionFactory(&EndCallback, &EndCallback)); 233 Tool.run(Action.get()); 234 235 EXPECT_TRUE(EndCallback.Matched); 236 EXPECT_EQ(2u, EndCallback.BeginCalled); 237 EXPECT_EQ(2u, EndCallback.EndCalled); 238 } 239 #endif 240 241 struct SkipBodyConsumer : public clang::ASTConsumer { 242 /// Skip the 'skipMe' function. 243 bool shouldSkipFunctionBody(Decl *D) override { 244 FunctionDecl *F = dyn_cast<FunctionDecl>(D); 245 return F && F->getNameAsString() == "skipMe"; 246 } 247 }; 248 249 struct SkipBodyAction : public clang::ASTFrontendAction { 250 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 251 StringRef) override { 252 Compiler.getFrontendOpts().SkipFunctionBodies = true; 253 return llvm::make_unique<SkipBodyConsumer>(); 254 } 255 }; 256 257 TEST(runToolOnCode, TestSkipFunctionBody) { 258 EXPECT_TRUE(runToolOnCode(new SkipBodyAction, 259 "int skipMe() { an_error_here }")); 260 EXPECT_FALSE(runToolOnCode(new SkipBodyAction, 261 "int skipMeNot() { an_error_here }")); 262 } 263 264 TEST(runToolOnCodeWithArgs, TestNoDepFile) { 265 llvm::SmallString<32> DepFilePath; 266 ASSERT_FALSE( 267 llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath)); 268 std::vector<std::string> Args; 269 Args.push_back("-MMD"); 270 Args.push_back("-MT"); 271 Args.push_back(DepFilePath.str()); 272 Args.push_back("-MF"); 273 Args.push_back(DepFilePath.str()); 274 EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args)); 275 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); 276 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); 277 } 278 279 TEST(ClangToolTest, ArgumentAdjusters) { 280 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 281 282 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 283 Tool.mapVirtualFile("/a.cc", "void a() {}"); 284 285 std::unique_ptr<FrontendActionFactory> Action( 286 newFrontendActionFactory<SyntaxOnlyAction>()); 287 288 bool Found = false; 289 bool Ran = false; 290 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 291 [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { 292 Ran = true; 293 if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end()) 294 Found = true; 295 return Args; 296 }; 297 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 298 Tool.run(Action.get()); 299 EXPECT_TRUE(Ran); 300 EXPECT_TRUE(Found); 301 302 Ran = Found = false; 303 Tool.clearArgumentsAdjusters(); 304 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 305 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 306 Tool.run(Action.get()); 307 EXPECT_TRUE(Ran); 308 EXPECT_FALSE(Found); 309 } 310 311 namespace { 312 /// Find a target name such that looking for it in TargetRegistry by that name 313 /// returns the same target. We expect that there is at least one target 314 /// configured with this property. 315 std::string getAnyTarget() { 316 llvm::InitializeAllTargets(); 317 for (const auto &Target : llvm::TargetRegistry::targets()) { 318 std::string Error; 319 StringRef TargetName(Target.getName()); 320 if (TargetName == "x86-64") 321 TargetName = "x86_64"; 322 if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) { 323 return TargetName; 324 } 325 } 326 return ""; 327 } 328 } 329 330 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { 331 std::string Target = getAnyTarget(); 332 ASSERT_FALSE(Target.empty()); 333 334 std::vector<std::string> Args = {"clang", "-foo"}; 335 addTargetAndModeForProgramName(Args, ""); 336 EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args); 337 addTargetAndModeForProgramName(Args, Target + "-g++"); 338 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 339 "--driver-mode=g++", "-foo"}), 340 Args); 341 } 342 343 TEST(addTargetAndModeForProgramName, PathIgnored) { 344 std::string Target = getAnyTarget(); 345 ASSERT_FALSE(Target.empty()); 346 347 SmallString<32> ToolPath; 348 llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++"); 349 350 std::vector<std::string> Args = {"clang", "-foo"}; 351 addTargetAndModeForProgramName(Args, ToolPath); 352 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 353 "--driver-mode=g++", "-foo"}), 354 Args); 355 } 356 357 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { 358 std::string Target = getAnyTarget(); 359 ASSERT_FALSE(Target.empty()); 360 361 std::vector<std::string> Args = {"clang", "-foo", "-target", "something"}; 362 addTargetAndModeForProgramName(Args, Target + "-g++"); 363 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 364 "-target", "something"}), 365 Args); 366 367 std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"}; 368 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 369 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 370 "-target=something"}), 371 ArgsAlt); 372 } 373 374 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { 375 std::string Target = getAnyTarget(); 376 ASSERT_FALSE(Target.empty()); 377 378 std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"}; 379 addTargetAndModeForProgramName(Args, Target + "-g++"); 380 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 381 "--driver-mode=abc"}), 382 Args); 383 384 std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"}; 385 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 386 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 387 "--driver-mode", "abc"}), 388 ArgsAlt); 389 } 390 391 #ifndef LLVM_ON_WIN32 392 TEST(ClangToolTest, BuildASTs) { 393 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 394 395 std::vector<std::string> Sources; 396 Sources.push_back("/a.cc"); 397 Sources.push_back("/b.cc"); 398 ClangTool Tool(Compilations, Sources); 399 400 Tool.mapVirtualFile("/a.cc", "void a() {}"); 401 Tool.mapVirtualFile("/b.cc", "void b() {}"); 402 403 std::vector<std::unique_ptr<ASTUnit>> ASTs; 404 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 405 EXPECT_EQ(2u, ASTs.size()); 406 } 407 408 struct TestDiagnosticConsumer : public DiagnosticConsumer { 409 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 410 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 411 const Diagnostic &Info) override { 412 ++NumDiagnosticsSeen; 413 } 414 unsigned NumDiagnosticsSeen; 415 }; 416 417 TEST(ClangToolTest, InjectDiagnosticConsumer) { 418 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 419 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 420 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 421 TestDiagnosticConsumer Consumer; 422 Tool.setDiagnosticConsumer(&Consumer); 423 std::unique_ptr<FrontendActionFactory> Action( 424 newFrontendActionFactory<SyntaxOnlyAction>()); 425 Tool.run(Action.get()); 426 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 427 } 428 429 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 430 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 431 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 432 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 433 TestDiagnosticConsumer Consumer; 434 Tool.setDiagnosticConsumer(&Consumer); 435 std::vector<std::unique_ptr<ASTUnit>> ASTs; 436 Tool.buildASTs(ASTs); 437 EXPECT_EQ(1u, ASTs.size()); 438 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 439 } 440 #endif 441 442 } // end namespace tooling 443 } // end namespace clang 444