1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/AST/ASTConsumer.h" 10 #include "clang/AST/DeclCXX.h" 11 #include "clang/AST/DeclGroup.h" 12 #include "clang/Frontend/ASTUnit.h" 13 #include "clang/Frontend/CompilerInstance.h" 14 #include "clang/Frontend/FrontendAction.h" 15 #include "clang/Frontend/FrontendActions.h" 16 #include "clang/Tooling/ArgumentsAdjusters.h" 17 #include "clang/Tooling/CompilationDatabase.h" 18 #include "clang/Tooling/Tooling.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/ADT/StringRef.h" 21 #include "llvm/Support/Path.h" 22 #include "llvm/Support/TargetRegistry.h" 23 #include "llvm/Support/TargetSelect.h" 24 #include "gtest/gtest.h" 25 #include <algorithm> 26 #include <string> 27 #include <vector> 28 29 namespace clang { 30 namespace tooling { 31 32 namespace { 33 /// Takes an ast consumer and returns it from CreateASTConsumer. This only 34 /// works with single translation unit compilations. 35 class TestAction : public clang::ASTFrontendAction { 36 public: 37 /// Takes ownership of TestConsumer. 38 explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer) 39 : TestConsumer(std::move(TestConsumer)) {} 40 41 protected: 42 std::unique_ptr<clang::ASTConsumer> 43 CreateASTConsumer(clang::CompilerInstance &compiler, 44 StringRef dummy) override { 45 /// TestConsumer will be deleted by the framework calling us. 46 return std::move(TestConsumer); 47 } 48 49 private: 50 std::unique_ptr<clang::ASTConsumer> TestConsumer; 51 }; 52 53 class FindTopLevelDeclConsumer : public clang::ASTConsumer { 54 public: 55 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) 56 : FoundTopLevelDecl(FoundTopLevelDecl) {} 57 bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override { 58 *FoundTopLevelDecl = true; 59 return true; 60 } 61 private: 62 bool * const FoundTopLevelDecl; 63 }; 64 } // end namespace 65 66 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 67 bool FoundTopLevelDecl = false; 68 EXPECT_TRUE(runToolOnCode( 69 std::make_unique<TestAction>( 70 std::make_unique<FindTopLevelDeclConsumer>(&FoundTopLevelDecl)), 71 "")); 72 EXPECT_FALSE(FoundTopLevelDecl); 73 } 74 75 namespace { 76 class FindClassDeclXConsumer : public clang::ASTConsumer { 77 public: 78 FindClassDeclXConsumer(bool *FoundClassDeclX) 79 : FoundClassDeclX(FoundClassDeclX) {} 80 bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override { 81 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( 82 *GroupRef.begin())) { 83 if (Record->getName() == "X") { 84 *FoundClassDeclX = true; 85 } 86 } 87 return true; 88 } 89 private: 90 bool *FoundClassDeclX; 91 }; 92 bool FindClassDeclX(ASTUnit *AST) { 93 for (std::vector<Decl *>::iterator i = AST->top_level_begin(), 94 e = AST->top_level_end(); 95 i != e; ++i) { 96 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) { 97 if (Record->getName() == "X") { 98 return true; 99 } 100 } 101 } 102 return false; 103 } 104 } // end namespace 105 106 TEST(runToolOnCode, FindsClassDecl) { 107 bool FoundClassDeclX = false; 108 EXPECT_TRUE(runToolOnCode( 109 std::make_unique<TestAction>( 110 std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)), 111 "class X;")); 112 EXPECT_TRUE(FoundClassDeclX); 113 114 FoundClassDeclX = false; 115 EXPECT_TRUE(runToolOnCode( 116 std::make_unique<TestAction>( 117 std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)), 118 "class Y;")); 119 EXPECT_FALSE(FoundClassDeclX); 120 } 121 122 TEST(buildASTFromCode, FindsClassDecl) { 123 std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;"); 124 ASSERT_TRUE(AST.get()); 125 EXPECT_TRUE(FindClassDeclX(AST.get())); 126 127 AST = buildASTFromCode("class Y;"); 128 ASSERT_TRUE(AST.get()); 129 EXPECT_FALSE(FindClassDeclX(AST.get())); 130 } 131 132 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 133 std::unique_ptr<FrontendActionFactory> Factory( 134 newFrontendActionFactory<SyntaxOnlyAction>()); 135 std::unique_ptr<FrontendAction> Action(Factory->create()); 136 EXPECT_TRUE(Action.get() != nullptr); 137 } 138 139 struct IndependentFrontendActionCreator { 140 std::unique_ptr<ASTConsumer> newASTConsumer() { 141 return std::make_unique<FindTopLevelDeclConsumer>(nullptr); 142 } 143 }; 144 145 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { 146 IndependentFrontendActionCreator Creator; 147 std::unique_ptr<FrontendActionFactory> Factory( 148 newFrontendActionFactory(&Creator)); 149 std::unique_ptr<FrontendAction> Action(Factory->create()); 150 EXPECT_TRUE(Action.get() != nullptr); 151 } 152 153 TEST(ToolInvocation, TestMapVirtualFile) { 154 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 155 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 156 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 157 new llvm::vfs::InMemoryFileSystem); 158 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 159 llvm::IntrusiveRefCntPtr<FileManager> Files( 160 new FileManager(FileSystemOptions(), OverlayFileSystem)); 161 std::vector<std::string> Args; 162 Args.push_back("tool-executable"); 163 Args.push_back("-Idef"); 164 Args.push_back("-fsyntax-only"); 165 Args.push_back("test.cpp"); 166 clang::tooling::ToolInvocation Invocation( 167 Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); 168 InMemoryFileSystem->addFile( 169 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 170 InMemoryFileSystem->addFile("def/abc", 0, 171 llvm::MemoryBuffer::getMemBuffer("\n")); 172 EXPECT_TRUE(Invocation.run()); 173 } 174 175 TEST(ToolInvocation, TestVirtualModulesCompilation) { 176 // FIXME: Currently, this only tests that we don't exit with an error if a 177 // mapped module.map is found on the include path. In the future, expand this 178 // test to run a full modules enabled compilation, so we make sure we can 179 // rerun modules compilations with a virtual file system. 180 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 181 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 182 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 183 new llvm::vfs::InMemoryFileSystem); 184 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 185 llvm::IntrusiveRefCntPtr<FileManager> Files( 186 new FileManager(FileSystemOptions(), OverlayFileSystem)); 187 std::vector<std::string> Args; 188 Args.push_back("tool-executable"); 189 Args.push_back("-Idef"); 190 Args.push_back("-fsyntax-only"); 191 Args.push_back("test.cpp"); 192 clang::tooling::ToolInvocation Invocation( 193 Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); 194 InMemoryFileSystem->addFile( 195 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 196 InMemoryFileSystem->addFile("def/abc", 0, 197 llvm::MemoryBuffer::getMemBuffer("\n")); 198 // Add a module.map file in the include directory of our header, so we trigger 199 // the module.map header search logic. 200 InMemoryFileSystem->addFile("def/module.map", 0, 201 llvm::MemoryBuffer::getMemBuffer("\n")); 202 EXPECT_TRUE(Invocation.run()); 203 } 204 205 struct VerifyEndCallback : public SourceFileCallbacks { 206 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 207 bool handleBeginSource(CompilerInstance &CI) override { 208 ++BeginCalled; 209 return true; 210 } 211 void handleEndSource() override { ++EndCalled; } 212 std::unique_ptr<ASTConsumer> newASTConsumer() { 213 return std::make_unique<FindTopLevelDeclConsumer>(&Matched); 214 } 215 unsigned BeginCalled; 216 unsigned EndCalled; 217 bool Matched; 218 }; 219 220 #if !defined(_WIN32) 221 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 222 VerifyEndCallback EndCallback; 223 224 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 225 std::vector<std::string> Sources; 226 Sources.push_back("/a.cc"); 227 Sources.push_back("/b.cc"); 228 ClangTool Tool(Compilations, Sources); 229 230 Tool.mapVirtualFile("/a.cc", "void a() {}"); 231 Tool.mapVirtualFile("/b.cc", "void b() {}"); 232 233 std::unique_ptr<FrontendActionFactory> Action( 234 newFrontendActionFactory(&EndCallback, &EndCallback)); 235 Tool.run(Action.get()); 236 237 EXPECT_TRUE(EndCallback.Matched); 238 EXPECT_EQ(2u, EndCallback.BeginCalled); 239 EXPECT_EQ(2u, EndCallback.EndCalled); 240 } 241 #endif 242 243 struct SkipBodyConsumer : public clang::ASTConsumer { 244 /// Skip the 'skipMe' function. 245 bool shouldSkipFunctionBody(Decl *D) override { 246 NamedDecl *F = dyn_cast<NamedDecl>(D); 247 return F && F->getNameAsString() == "skipMe"; 248 } 249 }; 250 251 struct SkipBodyAction : public clang::ASTFrontendAction { 252 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 253 StringRef) override { 254 Compiler.getFrontendOpts().SkipFunctionBodies = true; 255 return std::make_unique<SkipBodyConsumer>(); 256 } 257 }; 258 259 TEST(runToolOnCode, TestSkipFunctionBody) { 260 std::vector<std::string> Args = {"-std=c++11"}; 261 std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"}; 262 263 EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(), 264 "int skipMe() { an_error_here }")); 265 EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(), 266 "int skipMeNot() { an_error_here }")); 267 268 // Test constructors with initializers 269 EXPECT_TRUE(runToolOnCodeWithArgs( 270 std::make_unique<SkipBodyAction>(), 271 "struct skipMe { skipMe() : an_error() { more error } };", Args)); 272 EXPECT_TRUE(runToolOnCodeWithArgs( 273 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" 274 "skipMe::skipMe() : an_error([](){;}) { more error }", 275 Args)); 276 EXPECT_TRUE(runToolOnCodeWithArgs( 277 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" 278 "skipMe::skipMe() : an_error{[](){;}} { more error }", 279 Args)); 280 EXPECT_TRUE(runToolOnCodeWithArgs( 281 std::make_unique<SkipBodyAction>(), 282 "struct skipMe { skipMe(); };" 283 "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }", 284 Args)); 285 EXPECT_TRUE(runToolOnCodeWithArgs( 286 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };", 287 Args)); 288 289 EXPECT_FALSE(runToolOnCodeWithArgs( 290 std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };", 291 Args)); 292 EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), 293 "struct skipMeNot { skipMeNot(); };" 294 "skipMeNot::skipMeNot() : an_error() { }", 295 Args)); 296 297 // Try/catch 298 EXPECT_TRUE(runToolOnCode( 299 std::make_unique<SkipBodyAction>(), 300 "void skipMe() try { an_error() } catch(error) { error };")); 301 EXPECT_TRUE(runToolOnCode( 302 std::make_unique<SkipBodyAction>(), 303 "struct S { void skipMe() try { an_error() } catch(error) { error } };")); 304 EXPECT_TRUE( 305 runToolOnCode(std::make_unique<SkipBodyAction>(), 306 "void skipMe() try { an_error() } catch(error) { error; }" 307 "catch(error) { error } catch (error) { }")); 308 EXPECT_FALSE(runToolOnCode( 309 std::make_unique<SkipBodyAction>(), 310 "void skipMe() try something;")); // don't crash while parsing 311 312 // Template 313 EXPECT_TRUE(runToolOnCode( 314 std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }" 315 "int x = skipMe<int>();")); 316 EXPECT_FALSE(runToolOnCodeWithArgs( 317 std::make_unique<SkipBodyAction>(), 318 "template<typename T> int skipMeNot() { an_error_here }", Args2)); 319 } 320 321 TEST(runToolOnCodeWithArgs, TestNoDepFile) { 322 llvm::SmallString<32> DepFilePath; 323 ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d", 324 DepFilePath)); 325 std::vector<std::string> Args; 326 Args.push_back("-MMD"); 327 Args.push_back("-MT"); 328 Args.push_back(std::string(DepFilePath.str())); 329 Args.push_back("-MF"); 330 Args.push_back(std::string(DepFilePath.str())); 331 EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args)); 332 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); 333 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); 334 } 335 336 struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction { 337 CheckColoredDiagnosticsAction(bool ShouldShowColor) 338 : ShouldShowColor(ShouldShowColor) {} 339 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 340 StringRef) override { 341 if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor) 342 Compiler.getDiagnostics().Report( 343 Compiler.getDiagnostics().getCustomDiagID( 344 DiagnosticsEngine::Fatal, 345 "getDiagnosticOpts().ShowColors != ShouldShowColor")); 346 return std::make_unique<ASTConsumer>(); 347 } 348 349 private: 350 bool ShouldShowColor = true; 351 }; 352 353 TEST(runToolOnCodeWithArgs, DiagnosticsColor) { 354 EXPECT_TRUE(runToolOnCodeWithArgs( 355 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 356 {"-fcolor-diagnostics"})); 357 EXPECT_TRUE(runToolOnCodeWithArgs( 358 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 359 {"-fno-color-diagnostics"})); 360 EXPECT_TRUE(runToolOnCodeWithArgs( 361 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 362 {"-fno-color-diagnostics", "-fcolor-diagnostics"})); 363 EXPECT_TRUE(runToolOnCodeWithArgs( 364 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 365 {"-fcolor-diagnostics", "-fno-color-diagnostics"})); 366 EXPECT_TRUE(runToolOnCodeWithArgs( 367 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 368 {"-fno-color-diagnostics", "-fdiagnostics-color=always"})); 369 370 // Check that this test would fail if ShowColors is not what it should. 371 EXPECT_FALSE(runToolOnCodeWithArgs( 372 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 373 {"-fcolor-diagnostics"})); 374 } 375 376 TEST(ClangToolTest, ArgumentAdjusters) { 377 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 378 379 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 380 Tool.mapVirtualFile("/a.cc", "void a() {}"); 381 382 std::unique_ptr<FrontendActionFactory> Action( 383 newFrontendActionFactory<SyntaxOnlyAction>()); 384 385 bool Found = false; 386 bool Ran = false; 387 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 388 [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { 389 Ran = true; 390 if (llvm::is_contained(Args, "-fsyntax-only")) 391 Found = true; 392 return Args; 393 }; 394 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 395 Tool.run(Action.get()); 396 EXPECT_TRUE(Ran); 397 EXPECT_TRUE(Found); 398 399 Ran = Found = false; 400 Tool.clearArgumentsAdjusters(); 401 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 402 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 403 Tool.run(Action.get()); 404 EXPECT_TRUE(Ran); 405 EXPECT_FALSE(Found); 406 } 407 408 TEST(ClangToolTest, NoDoubleSyntaxOnly) { 409 FixedCompilationDatabase Compilations("/", {"-fsyntax-only"}); 410 411 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 412 Tool.mapVirtualFile("/a.cc", "void a() {}"); 413 414 std::unique_ptr<FrontendActionFactory> Action( 415 newFrontendActionFactory<SyntaxOnlyAction>()); 416 417 size_t SyntaxOnlyCount = 0; 418 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 419 [&SyntaxOnlyCount](const CommandLineArguments &Args, 420 StringRef /*unused*/) { 421 for (llvm::StringRef Arg : Args) { 422 if (Arg == "-fsyntax-only") 423 ++SyntaxOnlyCount; 424 } 425 return Args; 426 }; 427 428 Tool.clearArgumentsAdjusters(); 429 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 430 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 431 Tool.run(Action.get()); 432 EXPECT_EQ(SyntaxOnlyCount, 1U); 433 } 434 435 TEST(ClangToolTest, NoOutputCommands) { 436 FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd", 437 "--save-temps", 438 "--save-temps=somedir"}); 439 440 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 441 Tool.mapVirtualFile("/a.cc", "void a() {}"); 442 443 std::unique_ptr<FrontendActionFactory> Action( 444 newFrontendActionFactory<SyntaxOnlyAction>()); 445 446 const std::vector<llvm::StringRef> OutputCommands = {"-save-temps"}; 447 bool Ran = false; 448 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 449 [&OutputCommands, &Ran](const CommandLineArguments &Args, 450 StringRef /*unused*/) { 451 for (llvm::StringRef Arg : Args) { 452 for (llvm::StringRef OutputCommand : OutputCommands) 453 EXPECT_FALSE(Arg.contains(OutputCommand)); 454 } 455 Ran = true; 456 return Args; 457 }; 458 459 Tool.clearArgumentsAdjusters(); 460 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 461 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 462 Tool.run(Action.get()); 463 EXPECT_TRUE(Ran); 464 } 465 466 TEST(ClangToolTest, BaseVirtualFileSystemUsage) { 467 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 468 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 469 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 470 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 471 new llvm::vfs::InMemoryFileSystem); 472 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 473 474 InMemoryFileSystem->addFile( 475 "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}")); 476 477 ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"), 478 std::make_shared<PCHContainerOperations>(), OverlayFileSystem); 479 std::unique_ptr<FrontendActionFactory> Action( 480 newFrontendActionFactory<SyntaxOnlyAction>()); 481 EXPECT_EQ(0, Tool.run(Action.get())); 482 } 483 484 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD. 485 TEST(ClangToolTest, StripDependencyFileAdjuster) { 486 FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"}); 487 488 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 489 Tool.mapVirtualFile("/a.cc", "void a() {}"); 490 491 std::unique_ptr<FrontendActionFactory> Action( 492 newFrontendActionFactory<SyntaxOnlyAction>()); 493 494 CommandLineArguments FinalArgs; 495 ArgumentsAdjuster CheckFlagsAdjuster = 496 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 497 FinalArgs = Args; 498 return Args; 499 }; 500 Tool.clearArgumentsAdjusters(); 501 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 502 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 503 Tool.run(Action.get()); 504 505 auto HasFlag = [&FinalArgs](const std::string &Flag) { 506 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 507 }; 508 EXPECT_FALSE(HasFlag("-MD")); 509 EXPECT_FALSE(HasFlag("-MMD")); 510 EXPECT_TRUE(HasFlag("-c")); 511 EXPECT_TRUE(HasFlag("-w")); 512 } 513 514 // Check getClangStripPluginsAdjuster strips plugin related args. 515 TEST(ClangToolTest, StripPluginsAdjuster) { 516 FixedCompilationDatabase Compilations( 517 "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"}); 518 519 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 520 Tool.mapVirtualFile("/a.cc", "void a() {}"); 521 522 std::unique_ptr<FrontendActionFactory> Action( 523 newFrontendActionFactory<SyntaxOnlyAction>()); 524 525 CommandLineArguments FinalArgs; 526 ArgumentsAdjuster CheckFlagsAdjuster = 527 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 528 FinalArgs = Args; 529 return Args; 530 }; 531 Tool.clearArgumentsAdjusters(); 532 Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); 533 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 534 Tool.run(Action.get()); 535 536 auto HasFlag = [&FinalArgs](const std::string &Flag) { 537 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 538 }; 539 EXPECT_FALSE(HasFlag("-Xclang")); 540 EXPECT_FALSE(HasFlag("-add-plugin")); 541 EXPECT_FALSE(HasFlag("-random-plugin")); 542 } 543 544 namespace { 545 /// Find a target name such that looking for it in TargetRegistry by that name 546 /// returns the same target. We expect that there is at least one target 547 /// configured with this property. 548 std::string getAnyTarget() { 549 llvm::InitializeAllTargets(); 550 for (const auto &Target : llvm::TargetRegistry::targets()) { 551 std::string Error; 552 StringRef TargetName(Target.getName()); 553 if (TargetName == "x86-64") 554 TargetName = "x86_64"; 555 if (llvm::TargetRegistry::lookupTarget(std::string(TargetName), Error) == 556 &Target) { 557 return std::string(TargetName); 558 } 559 } 560 return ""; 561 } 562 } 563 564 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { 565 std::string Target = getAnyTarget(); 566 ASSERT_FALSE(Target.empty()); 567 568 std::vector<std::string> Args = {"clang", "-foo"}; 569 addTargetAndModeForProgramName(Args, ""); 570 EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args); 571 addTargetAndModeForProgramName(Args, Target + "-g++"); 572 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 573 "--driver-mode=g++", "-foo"}), 574 Args); 575 } 576 577 TEST(addTargetAndModeForProgramName, PathIgnored) { 578 std::string Target = getAnyTarget(); 579 ASSERT_FALSE(Target.empty()); 580 581 SmallString<32> ToolPath; 582 llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++"); 583 584 std::vector<std::string> Args = {"clang", "-foo"}; 585 addTargetAndModeForProgramName(Args, ToolPath); 586 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 587 "--driver-mode=g++", "-foo"}), 588 Args); 589 } 590 591 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { 592 std::string Target = getAnyTarget(); 593 ASSERT_FALSE(Target.empty()); 594 595 std::vector<std::string> Args = {"clang", "-foo", "-target", "something"}; 596 addTargetAndModeForProgramName(Args, Target + "-g++"); 597 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 598 "-target", "something"}), 599 Args); 600 601 std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"}; 602 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 603 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 604 "-target=something"}), 605 ArgsAlt); 606 } 607 608 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { 609 std::string Target = getAnyTarget(); 610 ASSERT_FALSE(Target.empty()); 611 612 std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"}; 613 addTargetAndModeForProgramName(Args, Target + "-g++"); 614 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 615 "--driver-mode=abc"}), 616 Args); 617 618 std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"}; 619 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 620 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 621 "--driver-mode", "abc"}), 622 ArgsAlt); 623 } 624 625 #ifndef _WIN32 626 TEST(ClangToolTest, BuildASTs) { 627 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 628 629 std::vector<std::string> Sources; 630 Sources.push_back("/a.cc"); 631 Sources.push_back("/b.cc"); 632 ClangTool Tool(Compilations, Sources); 633 634 Tool.mapVirtualFile("/a.cc", "void a() {}"); 635 Tool.mapVirtualFile("/b.cc", "void b() {}"); 636 637 std::vector<std::unique_ptr<ASTUnit>> ASTs; 638 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 639 EXPECT_EQ(2u, ASTs.size()); 640 } 641 642 struct TestDiagnosticConsumer : public DiagnosticConsumer { 643 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 644 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 645 const Diagnostic &Info) override { 646 ++NumDiagnosticsSeen; 647 } 648 unsigned NumDiagnosticsSeen; 649 }; 650 651 TEST(ClangToolTest, InjectDiagnosticConsumer) { 652 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 653 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 654 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 655 TestDiagnosticConsumer Consumer; 656 Tool.setDiagnosticConsumer(&Consumer); 657 std::unique_ptr<FrontendActionFactory> Action( 658 newFrontendActionFactory<SyntaxOnlyAction>()); 659 Tool.run(Action.get()); 660 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 661 } 662 663 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 664 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 665 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 666 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 667 TestDiagnosticConsumer Consumer; 668 Tool.setDiagnosticConsumer(&Consumer); 669 std::vector<std::unique_ptr<ASTUnit>> ASTs; 670 Tool.buildASTs(ASTs); 671 EXPECT_EQ(1u, ASTs.size()); 672 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 673 } 674 #endif 675 676 TEST(runToolOnCode, TestResetDiagnostics) { 677 // This is a tool that resets the diagnostic during the compilation. 678 struct ResetDiagnosticAction : public clang::ASTFrontendAction { 679 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 680 StringRef) override { 681 struct Consumer : public clang::ASTConsumer { 682 bool HandleTopLevelDecl(clang::DeclGroupRef D) override { 683 auto &Diags = (*D.begin())->getASTContext().getDiagnostics(); 684 // Ignore any error 685 Diags.Reset(); 686 // Disable warnings because computing the CFG might crash. 687 Diags.setIgnoreAllWarnings(true); 688 return true; 689 } 690 }; 691 return std::make_unique<Consumer>(); 692 } 693 }; 694 695 // Should not crash 696 EXPECT_FALSE( 697 runToolOnCode(std::make_unique<ResetDiagnosticAction>(), 698 "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };" 699 "void func() { long x; Foo f(x); }")); 700 } 701 702 } // end namespace tooling 703 } // end namespace clang 704