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