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( 66 runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>( 67 &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( 106 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( 107 &FoundClassDeclX)), 108 "class X;")); 109 EXPECT_TRUE(FoundClassDeclX); 110 111 FoundClassDeclX = false; 112 EXPECT_TRUE( 113 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( 114 &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 llvm::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(Args, new SyntaxOnlyAction, 164 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(Args, new SyntaxOnlyAction, 190 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 llvm::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 llvm::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(new SkipBodyAction, 261 "int skipMe() { an_error_here }")); 262 EXPECT_FALSE(runToolOnCode(new SkipBodyAction, 263 "int skipMeNot() { an_error_here }")); 264 265 // Test constructors with initializers 266 EXPECT_TRUE(runToolOnCodeWithArgs( 267 new SkipBodyAction, 268 "struct skipMe { skipMe() : an_error() { more error } };", Args)); 269 EXPECT_TRUE(runToolOnCodeWithArgs( 270 new SkipBodyAction, "struct skipMe { skipMe(); };" 271 "skipMe::skipMe() : an_error([](){;}) { more error }", 272 Args)); 273 EXPECT_TRUE(runToolOnCodeWithArgs( 274 new SkipBodyAction, "struct skipMe { skipMe(); };" 275 "skipMe::skipMe() : an_error{[](){;}} { more error }", 276 Args)); 277 EXPECT_TRUE(runToolOnCodeWithArgs( 278 new SkipBodyAction, 279 "struct skipMe { skipMe(); };" 280 "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }", 281 Args)); 282 EXPECT_TRUE(runToolOnCodeWithArgs( 283 new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };", 284 Args)); 285 286 EXPECT_FALSE(runToolOnCodeWithArgs( 287 new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };", 288 Args)); 289 EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction, 290 "struct skipMeNot { skipMeNot(); };" 291 "skipMeNot::skipMeNot() : an_error() { }", 292 Args)); 293 294 // Try/catch 295 EXPECT_TRUE(runToolOnCode( 296 new SkipBodyAction, 297 "void skipMe() try { an_error() } catch(error) { error };")); 298 EXPECT_TRUE(runToolOnCode( 299 new SkipBodyAction, 300 "struct S { void skipMe() try { an_error() } catch(error) { error } };")); 301 EXPECT_TRUE( 302 runToolOnCode(new SkipBodyAction, 303 "void skipMe() try { an_error() } catch(error) { error; }" 304 "catch(error) { error } catch (error) { }")); 305 EXPECT_FALSE(runToolOnCode( 306 new SkipBodyAction, 307 "void skipMe() try something;")); // don't crash while parsing 308 309 // Template 310 EXPECT_TRUE(runToolOnCode( 311 new SkipBodyAction, "template<typename T> int skipMe() { an_error_here }" 312 "int x = skipMe<int>();")); 313 EXPECT_FALSE(runToolOnCodeWithArgs( 314 new 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(new 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 llvm::make_unique<ASTConsumer>(); 344 } 345 346 private: 347 bool ShouldShowColor = true; 348 }; 349 350 TEST(runToolOnCodeWithArgs, DiagnosticsColor) { 351 352 EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "", 353 {"-fcolor-diagnostics"})); 354 EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), 355 "", {"-fno-color-diagnostics"})); 356 EXPECT_TRUE( 357 runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "", 358 {"-fno-color-diagnostics", "-fcolor-diagnostics"})); 359 EXPECT_TRUE( 360 runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), "", 361 {"-fcolor-diagnostics", "-fno-color-diagnostics"})); 362 EXPECT_TRUE(runToolOnCodeWithArgs( 363 new CheckColoredDiagnosticsAction(true), "", 364 {"-fno-color-diagnostics", "-fdiagnostics-color=always"})); 365 366 // Check that this test would fail if ShowColors is not what it should. 367 EXPECT_FALSE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), 368 "", {"-fcolor-diagnostics"})); 369 } 370 371 TEST(ClangToolTest, ArgumentAdjusters) { 372 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 373 374 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 375 Tool.mapVirtualFile("/a.cc", "void a() {}"); 376 377 std::unique_ptr<FrontendActionFactory> Action( 378 newFrontendActionFactory<SyntaxOnlyAction>()); 379 380 bool Found = false; 381 bool Ran = false; 382 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 383 [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { 384 Ran = true; 385 if (llvm::is_contained(Args, "-fsyntax-only")) 386 Found = true; 387 return Args; 388 }; 389 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 390 Tool.run(Action.get()); 391 EXPECT_TRUE(Ran); 392 EXPECT_TRUE(Found); 393 394 Ran = Found = false; 395 Tool.clearArgumentsAdjusters(); 396 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 397 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 398 Tool.run(Action.get()); 399 EXPECT_TRUE(Ran); 400 EXPECT_FALSE(Found); 401 } 402 403 TEST(ClangToolTest, BaseVirtualFileSystemUsage) { 404 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 405 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 406 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 407 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 408 new llvm::vfs::InMemoryFileSystem); 409 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 410 411 InMemoryFileSystem->addFile( 412 "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}")); 413 414 ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"), 415 std::make_shared<PCHContainerOperations>(), OverlayFileSystem); 416 std::unique_ptr<FrontendActionFactory> Action( 417 newFrontendActionFactory<SyntaxOnlyAction>()); 418 EXPECT_EQ(0, Tool.run(Action.get())); 419 } 420 421 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD. 422 TEST(ClangToolTest, StripDependencyFileAdjuster) { 423 FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"}); 424 425 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 426 Tool.mapVirtualFile("/a.cc", "void a() {}"); 427 428 std::unique_ptr<FrontendActionFactory> Action( 429 newFrontendActionFactory<SyntaxOnlyAction>()); 430 431 CommandLineArguments FinalArgs; 432 ArgumentsAdjuster CheckFlagsAdjuster = 433 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 434 FinalArgs = Args; 435 return Args; 436 }; 437 Tool.clearArgumentsAdjusters(); 438 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 439 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 440 Tool.run(Action.get()); 441 442 auto HasFlag = [&FinalArgs](const std::string &Flag) { 443 return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) != 444 FinalArgs.end(); 445 }; 446 EXPECT_FALSE(HasFlag("-MD")); 447 EXPECT_FALSE(HasFlag("-MMD")); 448 EXPECT_TRUE(HasFlag("-c")); 449 EXPECT_TRUE(HasFlag("-w")); 450 } 451 452 // Check getClangStripPluginsAdjuster strips plugin related args. 453 TEST(ClangToolTest, StripPluginsAdjuster) { 454 FixedCompilationDatabase Compilations( 455 "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"}); 456 457 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 458 Tool.mapVirtualFile("/a.cc", "void a() {}"); 459 460 std::unique_ptr<FrontendActionFactory> Action( 461 newFrontendActionFactory<SyntaxOnlyAction>()); 462 463 CommandLineArguments FinalArgs; 464 ArgumentsAdjuster CheckFlagsAdjuster = 465 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 466 FinalArgs = Args; 467 return Args; 468 }; 469 Tool.clearArgumentsAdjusters(); 470 Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); 471 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 472 Tool.run(Action.get()); 473 474 auto HasFlag = [&FinalArgs](const std::string &Flag) { 475 return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) != 476 FinalArgs.end(); 477 }; 478 EXPECT_FALSE(HasFlag("-Xclang")); 479 EXPECT_FALSE(HasFlag("-add-plugin")); 480 EXPECT_FALSE(HasFlag("-random-plugin")); 481 } 482 483 namespace { 484 /// Find a target name such that looking for it in TargetRegistry by that name 485 /// returns the same target. We expect that there is at least one target 486 /// configured with this property. 487 std::string getAnyTarget() { 488 llvm::InitializeAllTargets(); 489 for (const auto &Target : llvm::TargetRegistry::targets()) { 490 std::string Error; 491 StringRef TargetName(Target.getName()); 492 if (TargetName == "x86-64") 493 TargetName = "x86_64"; 494 if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) { 495 return TargetName; 496 } 497 } 498 return ""; 499 } 500 } 501 502 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { 503 std::string Target = getAnyTarget(); 504 ASSERT_FALSE(Target.empty()); 505 506 std::vector<std::string> Args = {"clang", "-foo"}; 507 addTargetAndModeForProgramName(Args, ""); 508 EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args); 509 addTargetAndModeForProgramName(Args, Target + "-g++"); 510 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 511 "--driver-mode=g++", "-foo"}), 512 Args); 513 } 514 515 TEST(addTargetAndModeForProgramName, PathIgnored) { 516 std::string Target = getAnyTarget(); 517 ASSERT_FALSE(Target.empty()); 518 519 SmallString<32> ToolPath; 520 llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++"); 521 522 std::vector<std::string> Args = {"clang", "-foo"}; 523 addTargetAndModeForProgramName(Args, ToolPath); 524 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 525 "--driver-mode=g++", "-foo"}), 526 Args); 527 } 528 529 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { 530 std::string Target = getAnyTarget(); 531 ASSERT_FALSE(Target.empty()); 532 533 std::vector<std::string> Args = {"clang", "-foo", "-target", "something"}; 534 addTargetAndModeForProgramName(Args, Target + "-g++"); 535 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 536 "-target", "something"}), 537 Args); 538 539 std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"}; 540 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 541 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 542 "-target=something"}), 543 ArgsAlt); 544 } 545 546 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { 547 std::string Target = getAnyTarget(); 548 ASSERT_FALSE(Target.empty()); 549 550 std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"}; 551 addTargetAndModeForProgramName(Args, Target + "-g++"); 552 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 553 "--driver-mode=abc"}), 554 Args); 555 556 std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"}; 557 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 558 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 559 "--driver-mode", "abc"}), 560 ArgsAlt); 561 } 562 563 #ifndef _WIN32 564 TEST(ClangToolTest, BuildASTs) { 565 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 566 567 std::vector<std::string> Sources; 568 Sources.push_back("/a.cc"); 569 Sources.push_back("/b.cc"); 570 ClangTool Tool(Compilations, Sources); 571 572 Tool.mapVirtualFile("/a.cc", "void a() {}"); 573 Tool.mapVirtualFile("/b.cc", "void b() {}"); 574 575 std::vector<std::unique_ptr<ASTUnit>> ASTs; 576 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 577 EXPECT_EQ(2u, ASTs.size()); 578 } 579 580 struct TestDiagnosticConsumer : public DiagnosticConsumer { 581 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 582 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 583 const Diagnostic &Info) override { 584 ++NumDiagnosticsSeen; 585 } 586 unsigned NumDiagnosticsSeen; 587 }; 588 589 TEST(ClangToolTest, InjectDiagnosticConsumer) { 590 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 591 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 592 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 593 TestDiagnosticConsumer Consumer; 594 Tool.setDiagnosticConsumer(&Consumer); 595 std::unique_ptr<FrontendActionFactory> Action( 596 newFrontendActionFactory<SyntaxOnlyAction>()); 597 Tool.run(Action.get()); 598 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 599 } 600 601 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 602 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 603 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 604 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 605 TestDiagnosticConsumer Consumer; 606 Tool.setDiagnosticConsumer(&Consumer); 607 std::vector<std::unique_ptr<ASTUnit>> ASTs; 608 Tool.buildASTs(ASTs); 609 EXPECT_EQ(1u, ASTs.size()); 610 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 611 } 612 #endif 613 614 TEST(runToolOnCode, TestResetDiagnostics) { 615 // This is a tool that resets the diagnostic during the compilation. 616 struct ResetDiagnosticAction : public clang::ASTFrontendAction { 617 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 618 StringRef) override { 619 struct Consumer : public clang::ASTConsumer { 620 bool HandleTopLevelDecl(clang::DeclGroupRef D) override { 621 auto &Diags = (*D.begin())->getASTContext().getDiagnostics(); 622 // Ignore any error 623 Diags.Reset(); 624 // Disable warnings because computing the CFG might crash. 625 Diags.setIgnoreAllWarnings(true); 626 return true; 627 } 628 }; 629 return llvm::make_unique<Consumer>(); 630 } 631 }; 632 633 // Should not crash 634 EXPECT_FALSE( 635 runToolOnCode(new ResetDiagnosticAction, 636 "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };" 637 "void func() { long x; Foo f(x); }")); 638 } 639 640 } // end namespace tooling 641 } // end namespace clang 642