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