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 DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer { 225 bool SawSourceManager; 226 227 DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {} 228 229 void HandleDiagnostic(clang::DiagnosticsEngine::Level, 230 const clang::Diagnostic &info) override { 231 SawSourceManager = info.hasSourceManager(); 232 } 233 }; 234 235 TEST(ToolInvocation, DiagConsumerExpectingSourceManager) { 236 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 237 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 238 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 239 new llvm::vfs::InMemoryFileSystem); 240 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 241 llvm::IntrusiveRefCntPtr<FileManager> Files( 242 new FileManager(FileSystemOptions(), OverlayFileSystem)); 243 std::vector<std::string> Args; 244 Args.push_back("tool-executable"); 245 // Note: intentional error; user probably meant -ferror-limit=0. 246 Args.push_back("-ferror-limit=-1"); 247 Args.push_back("-fsyntax-only"); 248 Args.push_back("test.cpp"); 249 clang::tooling::ToolInvocation Invocation( 250 Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); 251 InMemoryFileSystem->addFile( 252 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}\n")); 253 254 DiagnosticConsumerExpectingSourceManager Consumer; 255 Invocation.setDiagnosticConsumer(&Consumer); 256 257 EXPECT_TRUE(Invocation.run()); 258 EXPECT_TRUE(Consumer.SawSourceManager); 259 } 260 261 struct VerifyEndCallback : public SourceFileCallbacks { 262 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 263 bool handleBeginSource(CompilerInstance &CI) override { 264 ++BeginCalled; 265 return true; 266 } 267 void handleEndSource() override { ++EndCalled; } 268 std::unique_ptr<ASTConsumer> newASTConsumer() { 269 return std::make_unique<FindTopLevelDeclConsumer>(&Matched); 270 } 271 unsigned BeginCalled; 272 unsigned EndCalled; 273 bool Matched; 274 }; 275 276 #if !defined(_WIN32) 277 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 278 VerifyEndCallback EndCallback; 279 280 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 281 std::vector<std::string> Sources; 282 Sources.push_back("/a.cc"); 283 Sources.push_back("/b.cc"); 284 ClangTool Tool(Compilations, Sources); 285 286 Tool.mapVirtualFile("/a.cc", "void a() {}"); 287 Tool.mapVirtualFile("/b.cc", "void b() {}"); 288 289 std::unique_ptr<FrontendActionFactory> Action( 290 newFrontendActionFactory(&EndCallback, &EndCallback)); 291 Tool.run(Action.get()); 292 293 EXPECT_TRUE(EndCallback.Matched); 294 EXPECT_EQ(2u, EndCallback.BeginCalled); 295 EXPECT_EQ(2u, EndCallback.EndCalled); 296 } 297 #endif 298 299 struct SkipBodyConsumer : public clang::ASTConsumer { 300 /// Skip the 'skipMe' function. 301 bool shouldSkipFunctionBody(Decl *D) override { 302 NamedDecl *F = dyn_cast<NamedDecl>(D); 303 return F && F->getNameAsString() == "skipMe"; 304 } 305 }; 306 307 struct SkipBodyAction : public clang::ASTFrontendAction { 308 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 309 StringRef) override { 310 Compiler.getFrontendOpts().SkipFunctionBodies = true; 311 return std::make_unique<SkipBodyConsumer>(); 312 } 313 }; 314 315 TEST(runToolOnCode, TestSkipFunctionBody) { 316 std::vector<std::string> Args = {"-std=c++11"}; 317 std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"}; 318 319 EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(), 320 "int skipMe() { an_error_here }")); 321 EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(), 322 "int skipMeNot() { an_error_here }")); 323 324 // Test constructors with initializers 325 EXPECT_TRUE(runToolOnCodeWithArgs( 326 std::make_unique<SkipBodyAction>(), 327 "struct skipMe { skipMe() : an_error() { more error } };", Args)); 328 EXPECT_TRUE(runToolOnCodeWithArgs( 329 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" 330 "skipMe::skipMe() : an_error([](){;}) { more error }", 331 Args)); 332 EXPECT_TRUE(runToolOnCodeWithArgs( 333 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" 334 "skipMe::skipMe() : an_error{[](){;}} { more error }", 335 Args)); 336 EXPECT_TRUE(runToolOnCodeWithArgs( 337 std::make_unique<SkipBodyAction>(), 338 "struct skipMe { skipMe(); };" 339 "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }", 340 Args)); 341 EXPECT_TRUE(runToolOnCodeWithArgs( 342 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };", 343 Args)); 344 345 EXPECT_FALSE(runToolOnCodeWithArgs( 346 std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };", 347 Args)); 348 EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), 349 "struct skipMeNot { skipMeNot(); };" 350 "skipMeNot::skipMeNot() : an_error() { }", 351 Args)); 352 353 // Try/catch 354 EXPECT_TRUE(runToolOnCode( 355 std::make_unique<SkipBodyAction>(), 356 "void skipMe() try { an_error() } catch(error) { error };")); 357 EXPECT_TRUE(runToolOnCode( 358 std::make_unique<SkipBodyAction>(), 359 "struct S { void skipMe() try { an_error() } catch(error) { error } };")); 360 EXPECT_TRUE( 361 runToolOnCode(std::make_unique<SkipBodyAction>(), 362 "void skipMe() try { an_error() } catch(error) { error; }" 363 "catch(error) { error } catch (error) { }")); 364 EXPECT_FALSE(runToolOnCode( 365 std::make_unique<SkipBodyAction>(), 366 "void skipMe() try something;")); // don't crash while parsing 367 368 // Template 369 EXPECT_TRUE(runToolOnCode( 370 std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }" 371 "int x = skipMe<int>();")); 372 EXPECT_FALSE(runToolOnCodeWithArgs( 373 std::make_unique<SkipBodyAction>(), 374 "template<typename T> int skipMeNot() { an_error_here }", Args2)); 375 } 376 377 TEST(runToolOnCodeWithArgs, TestNoDepFile) { 378 llvm::SmallString<32> DepFilePath; 379 ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d", 380 DepFilePath)); 381 std::vector<std::string> Args; 382 Args.push_back("-MMD"); 383 Args.push_back("-MT"); 384 Args.push_back(std::string(DepFilePath.str())); 385 Args.push_back("-MF"); 386 Args.push_back(std::string(DepFilePath.str())); 387 EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args)); 388 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); 389 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); 390 } 391 392 struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction { 393 CheckColoredDiagnosticsAction(bool ShouldShowColor) 394 : ShouldShowColor(ShouldShowColor) {} 395 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 396 StringRef) override { 397 if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor) 398 Compiler.getDiagnostics().Report( 399 Compiler.getDiagnostics().getCustomDiagID( 400 DiagnosticsEngine::Fatal, 401 "getDiagnosticOpts().ShowColors != ShouldShowColor")); 402 return std::make_unique<ASTConsumer>(); 403 } 404 405 private: 406 bool ShouldShowColor = true; 407 }; 408 409 TEST(runToolOnCodeWithArgs, DiagnosticsColor) { 410 EXPECT_TRUE(runToolOnCodeWithArgs( 411 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 412 {"-fcolor-diagnostics"})); 413 EXPECT_TRUE(runToolOnCodeWithArgs( 414 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 415 {"-fno-color-diagnostics"})); 416 EXPECT_TRUE(runToolOnCodeWithArgs( 417 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 418 {"-fno-color-diagnostics", "-fcolor-diagnostics"})); 419 EXPECT_TRUE(runToolOnCodeWithArgs( 420 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 421 {"-fcolor-diagnostics", "-fno-color-diagnostics"})); 422 EXPECT_TRUE(runToolOnCodeWithArgs( 423 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 424 {"-fno-color-diagnostics", "-fdiagnostics-color=always"})); 425 426 // Check that this test would fail if ShowColors is not what it should. 427 EXPECT_FALSE(runToolOnCodeWithArgs( 428 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 429 {"-fcolor-diagnostics"})); 430 } 431 432 TEST(ClangToolTest, ArgumentAdjusters) { 433 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 434 435 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 436 Tool.mapVirtualFile("/a.cc", "void a() {}"); 437 438 std::unique_ptr<FrontendActionFactory> Action( 439 newFrontendActionFactory<SyntaxOnlyAction>()); 440 441 bool Found = false; 442 bool Ran = false; 443 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 444 [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { 445 Ran = true; 446 if (llvm::is_contained(Args, "-fsyntax-only")) 447 Found = true; 448 return Args; 449 }; 450 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 451 Tool.run(Action.get()); 452 EXPECT_TRUE(Ran); 453 EXPECT_TRUE(Found); 454 455 Ran = Found = false; 456 Tool.clearArgumentsAdjusters(); 457 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 458 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 459 Tool.run(Action.get()); 460 EXPECT_TRUE(Ran); 461 EXPECT_FALSE(Found); 462 } 463 464 TEST(ClangToolTest, NoDoubleSyntaxOnly) { 465 FixedCompilationDatabase Compilations("/", {"-fsyntax-only"}); 466 467 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 468 Tool.mapVirtualFile("/a.cc", "void a() {}"); 469 470 std::unique_ptr<FrontendActionFactory> Action( 471 newFrontendActionFactory<SyntaxOnlyAction>()); 472 473 size_t SyntaxOnlyCount = 0; 474 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 475 [&SyntaxOnlyCount](const CommandLineArguments &Args, 476 StringRef /*unused*/) { 477 for (llvm::StringRef Arg : Args) { 478 if (Arg == "-fsyntax-only") 479 ++SyntaxOnlyCount; 480 } 481 return Args; 482 }; 483 484 Tool.clearArgumentsAdjusters(); 485 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 486 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 487 Tool.run(Action.get()); 488 EXPECT_EQ(SyntaxOnlyCount, 1U); 489 } 490 491 TEST(ClangToolTest, NoOutputCommands) { 492 FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd", 493 "--save-temps", 494 "--save-temps=somedir"}); 495 496 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 497 Tool.mapVirtualFile("/a.cc", "void a() {}"); 498 499 std::unique_ptr<FrontendActionFactory> Action( 500 newFrontendActionFactory<SyntaxOnlyAction>()); 501 502 const std::vector<llvm::StringRef> OutputCommands = {"-save-temps"}; 503 bool Ran = false; 504 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 505 [&OutputCommands, &Ran](const CommandLineArguments &Args, 506 StringRef /*unused*/) { 507 for (llvm::StringRef Arg : Args) { 508 for (llvm::StringRef OutputCommand : OutputCommands) 509 EXPECT_FALSE(Arg.contains(OutputCommand)); 510 } 511 Ran = true; 512 return Args; 513 }; 514 515 Tool.clearArgumentsAdjusters(); 516 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 517 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 518 Tool.run(Action.get()); 519 EXPECT_TRUE(Ran); 520 } 521 522 TEST(ClangToolTest, BaseVirtualFileSystemUsage) { 523 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 524 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 525 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 526 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 527 new llvm::vfs::InMemoryFileSystem); 528 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 529 530 InMemoryFileSystem->addFile( 531 "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}")); 532 533 ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"), 534 std::make_shared<PCHContainerOperations>(), OverlayFileSystem); 535 std::unique_ptr<FrontendActionFactory> Action( 536 newFrontendActionFactory<SyntaxOnlyAction>()); 537 EXPECT_EQ(0, Tool.run(Action.get())); 538 } 539 540 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD. 541 TEST(ClangToolTest, StripDependencyFileAdjuster) { 542 FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"}); 543 544 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 545 Tool.mapVirtualFile("/a.cc", "void a() {}"); 546 547 std::unique_ptr<FrontendActionFactory> Action( 548 newFrontendActionFactory<SyntaxOnlyAction>()); 549 550 CommandLineArguments FinalArgs; 551 ArgumentsAdjuster CheckFlagsAdjuster = 552 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 553 FinalArgs = Args; 554 return Args; 555 }; 556 Tool.clearArgumentsAdjusters(); 557 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 558 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 559 Tool.run(Action.get()); 560 561 auto HasFlag = [&FinalArgs](const std::string &Flag) { 562 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 563 }; 564 EXPECT_FALSE(HasFlag("-MD")); 565 EXPECT_FALSE(HasFlag("-MMD")); 566 EXPECT_TRUE(HasFlag("-c")); 567 EXPECT_TRUE(HasFlag("-w")); 568 } 569 570 // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants 571 TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) { 572 FixedCompilationDatabase Compilations( 573 "/", {"/showIncludes", "/showIncludes:user", "-showIncludes", 574 "-showIncludes:user", "-c"}); 575 576 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 577 Tool.mapVirtualFile("/a.cc", "void a() {}"); 578 579 std::unique_ptr<FrontendActionFactory> Action( 580 newFrontendActionFactory<SyntaxOnlyAction>()); 581 582 CommandLineArguments FinalArgs; 583 ArgumentsAdjuster CheckFlagsAdjuster = 584 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 585 FinalArgs = Args; 586 return Args; 587 }; 588 Tool.clearArgumentsAdjusters(); 589 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 590 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 591 Tool.run(Action.get()); 592 593 auto HasFlag = [&FinalArgs](const std::string &Flag) { 594 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 595 }; 596 EXPECT_FALSE(HasFlag("/showIncludes")); 597 EXPECT_FALSE(HasFlag("/showIncludes:user")); 598 EXPECT_FALSE(HasFlag("-showIncludes")); 599 EXPECT_FALSE(HasFlag("-showIncludes:user")); 600 EXPECT_TRUE(HasFlag("-c")); 601 } 602 603 // Check getClangStripDependencyFileAdjuster doesn't strip args when using the 604 // MSVC cl.exe driver 605 TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) { 606 FixedCompilationDatabase Compilations( 607 "/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"}); 608 609 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 610 Tool.mapVirtualFile("/a.cc", "void a() {}"); 611 612 std::unique_ptr<FrontendActionFactory> Action( 613 newFrontendActionFactory<SyntaxOnlyAction>()); 614 615 CommandLineArguments FinalArgs; 616 ArgumentsAdjuster CheckFlagsAdjuster = 617 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 618 FinalArgs = Args; 619 return Args; 620 }; 621 Tool.clearArgumentsAdjusters(); 622 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 623 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 624 Tool.run(Action.get()); 625 626 auto HasFlag = [&FinalArgs](const std::string &Flag) { 627 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 628 }; 629 EXPECT_TRUE(HasFlag("-MD")); 630 EXPECT_TRUE(HasFlag("-MDd")); 631 EXPECT_TRUE(HasFlag("-MT")); 632 EXPECT_TRUE(HasFlag("-O1")); 633 EXPECT_TRUE(HasFlag("-MTd")); 634 EXPECT_TRUE(HasFlag("-MP")); 635 } 636 637 // Check getClangStripPluginsAdjuster strips plugin related args. 638 TEST(ClangToolTest, StripPluginsAdjuster) { 639 FixedCompilationDatabase Compilations( 640 "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"}); 641 642 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 643 Tool.mapVirtualFile("/a.cc", "void a() {}"); 644 645 std::unique_ptr<FrontendActionFactory> Action( 646 newFrontendActionFactory<SyntaxOnlyAction>()); 647 648 CommandLineArguments FinalArgs; 649 ArgumentsAdjuster CheckFlagsAdjuster = 650 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 651 FinalArgs = Args; 652 return Args; 653 }; 654 Tool.clearArgumentsAdjusters(); 655 Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); 656 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 657 Tool.run(Action.get()); 658 659 auto HasFlag = [&FinalArgs](const std::string &Flag) { 660 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 661 }; 662 EXPECT_FALSE(HasFlag("-Xclang")); 663 EXPECT_FALSE(HasFlag("-add-plugin")); 664 EXPECT_FALSE(HasFlag("-random-plugin")); 665 } 666 667 namespace { 668 /// Find a target name such that looking for it in TargetRegistry by that name 669 /// returns the same target. We expect that there is at least one target 670 /// configured with this property. 671 std::string getAnyTarget() { 672 llvm::InitializeAllTargets(); 673 for (const auto &Target : llvm::TargetRegistry::targets()) { 674 std::string Error; 675 StringRef TargetName(Target.getName()); 676 if (TargetName == "x86-64") 677 TargetName = "x86_64"; 678 if (llvm::TargetRegistry::lookupTarget(std::string(TargetName), Error) == 679 &Target) { 680 return std::string(TargetName); 681 } 682 } 683 return ""; 684 } 685 } 686 687 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { 688 std::string Target = getAnyTarget(); 689 ASSERT_FALSE(Target.empty()); 690 691 std::vector<std::string> Args = {"clang", "-foo"}; 692 addTargetAndModeForProgramName(Args, ""); 693 EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args); 694 addTargetAndModeForProgramName(Args, Target + "-g++"); 695 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, 696 "--driver-mode=g++", "-foo"}), 697 Args); 698 } 699 700 TEST(addTargetAndModeForProgramName, PathIgnored) { 701 std::string Target = getAnyTarget(); 702 ASSERT_FALSE(Target.empty()); 703 704 SmallString<32> ToolPath; 705 llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++"); 706 707 std::vector<std::string> Args = {"clang", "-foo"}; 708 addTargetAndModeForProgramName(Args, ToolPath); 709 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, 710 "--driver-mode=g++", "-foo"}), 711 Args); 712 } 713 714 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { 715 std::string Target = getAnyTarget(); 716 ASSERT_FALSE(Target.empty()); 717 718 std::vector<std::string> Args = {"clang", "-foo", "-target", "something"}; 719 addTargetAndModeForProgramName(Args, Target + "-g++"); 720 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 721 "-target", "something"}), 722 Args); 723 724 std::vector<std::string> ArgsAlt = {"clang", "-foo", "--target=something"}; 725 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 726 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 727 "--target=something"}), 728 ArgsAlt); 729 } 730 731 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { 732 std::string Target = getAnyTarget(); 733 ASSERT_FALSE(Target.empty()); 734 735 std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"}; 736 addTargetAndModeForProgramName(Args, Target + "-g++"); 737 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo", 738 "--driver-mode=abc"}), 739 Args); 740 } 741 742 #ifndef _WIN32 743 TEST(ClangToolTest, BuildASTs) { 744 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 745 746 std::vector<std::string> Sources; 747 Sources.push_back("/a.cc"); 748 Sources.push_back("/b.cc"); 749 ClangTool Tool(Compilations, Sources); 750 751 Tool.mapVirtualFile("/a.cc", "void a() {}"); 752 Tool.mapVirtualFile("/b.cc", "void b() {}"); 753 754 std::vector<std::unique_ptr<ASTUnit>> ASTs; 755 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 756 EXPECT_EQ(2u, ASTs.size()); 757 } 758 759 TEST(ClangToolTest, InjectDiagnosticConsumer) { 760 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 761 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 762 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 763 TestDiagnosticConsumer Consumer; 764 Tool.setDiagnosticConsumer(&Consumer); 765 std::unique_ptr<FrontendActionFactory> Action( 766 newFrontendActionFactory<SyntaxOnlyAction>()); 767 Tool.run(Action.get()); 768 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 769 } 770 771 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 772 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 773 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 774 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 775 TestDiagnosticConsumer Consumer; 776 Tool.setDiagnosticConsumer(&Consumer); 777 std::vector<std::unique_ptr<ASTUnit>> ASTs; 778 Tool.buildASTs(ASTs); 779 EXPECT_EQ(1u, ASTs.size()); 780 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 781 } 782 #endif 783 784 TEST(runToolOnCode, TestResetDiagnostics) { 785 // This is a tool that resets the diagnostic during the compilation. 786 struct ResetDiagnosticAction : public clang::ASTFrontendAction { 787 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 788 StringRef) override { 789 struct Consumer : public clang::ASTConsumer { 790 bool HandleTopLevelDecl(clang::DeclGroupRef D) override { 791 auto &Diags = (*D.begin())->getASTContext().getDiagnostics(); 792 // Ignore any error 793 Diags.Reset(); 794 // Disable warnings because computing the CFG might crash. 795 Diags.setIgnoreAllWarnings(true); 796 return true; 797 } 798 }; 799 return std::make_unique<Consumer>(); 800 } 801 }; 802 803 // Should not crash 804 EXPECT_FALSE( 805 runToolOnCode(std::make_unique<ResetDiagnosticAction>(), 806 "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };" 807 "void func() { long x; Foo f(x); }")); 808 } 809 810 } // end namespace tooling 811 } // end namespace clang 812