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/Driver/Compilation.h" 13 #include "clang/Driver/Driver.h" 14 #include "clang/Frontend/ASTUnit.h" 15 #include "clang/Frontend/CompilerInstance.h" 16 #include "clang/Frontend/FrontendAction.h" 17 #include "clang/Frontend/FrontendActions.h" 18 #include "clang/Tooling/ArgumentsAdjusters.h" 19 #include "clang/Tooling/CompilationDatabase.h" 20 #include "clang/Tooling/Tooling.h" 21 #include "llvm/ADT/STLExtras.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/Support/Host.h" 24 #include "llvm/Support/Path.h" 25 #include "llvm/Support/TargetRegistry.h" 26 #include "llvm/Support/TargetSelect.h" 27 #include "gtest/gtest.h" 28 #include <algorithm> 29 #include <string> 30 #include <vector> 31 32 namespace clang { 33 namespace tooling { 34 35 namespace { 36 /// Takes an ast consumer and returns it from CreateASTConsumer. This only 37 /// works with single translation unit compilations. 38 class TestAction : public clang::ASTFrontendAction { 39 public: 40 /// Takes ownership of TestConsumer. 41 explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer) 42 : TestConsumer(std::move(TestConsumer)) {} 43 44 protected: 45 std::unique_ptr<clang::ASTConsumer> 46 CreateASTConsumer(clang::CompilerInstance &compiler, 47 StringRef dummy) override { 48 /// TestConsumer will be deleted by the framework calling us. 49 return std::move(TestConsumer); 50 } 51 52 private: 53 std::unique_ptr<clang::ASTConsumer> TestConsumer; 54 }; 55 56 class FindTopLevelDeclConsumer : public clang::ASTConsumer { 57 public: 58 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) 59 : FoundTopLevelDecl(FoundTopLevelDecl) {} 60 bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override { 61 *FoundTopLevelDecl = true; 62 return true; 63 } 64 private: 65 bool * const FoundTopLevelDecl; 66 }; 67 } // end namespace 68 69 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 70 bool FoundTopLevelDecl = false; 71 EXPECT_TRUE(runToolOnCode( 72 std::make_unique<TestAction>( 73 std::make_unique<FindTopLevelDeclConsumer>(&FoundTopLevelDecl)), 74 "")); 75 EXPECT_FALSE(FoundTopLevelDecl); 76 } 77 78 namespace { 79 class FindClassDeclXConsumer : public clang::ASTConsumer { 80 public: 81 FindClassDeclXConsumer(bool *FoundClassDeclX) 82 : FoundClassDeclX(FoundClassDeclX) {} 83 bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override { 84 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( 85 *GroupRef.begin())) { 86 if (Record->getName() == "X") { 87 *FoundClassDeclX = true; 88 } 89 } 90 return true; 91 } 92 private: 93 bool *FoundClassDeclX; 94 }; 95 bool FindClassDeclX(ASTUnit *AST) { 96 for (std::vector<Decl *>::iterator i = AST->top_level_begin(), 97 e = AST->top_level_end(); 98 i != e; ++i) { 99 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) { 100 if (Record->getName() == "X") { 101 return true; 102 } 103 } 104 } 105 return false; 106 } 107 108 struct TestDiagnosticConsumer : public DiagnosticConsumer { 109 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 110 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 111 const Diagnostic &Info) override { 112 ++NumDiagnosticsSeen; 113 } 114 unsigned NumDiagnosticsSeen; 115 }; 116 } // end namespace 117 118 TEST(runToolOnCode, FindsClassDecl) { 119 bool FoundClassDeclX = false; 120 EXPECT_TRUE(runToolOnCode( 121 std::make_unique<TestAction>( 122 std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)), 123 "class X;")); 124 EXPECT_TRUE(FoundClassDeclX); 125 126 FoundClassDeclX = false; 127 EXPECT_TRUE(runToolOnCode( 128 std::make_unique<TestAction>( 129 std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)), 130 "class Y;")); 131 EXPECT_FALSE(FoundClassDeclX); 132 } 133 134 TEST(buildASTFromCode, FindsClassDecl) { 135 std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;"); 136 ASSERT_TRUE(AST.get()); 137 EXPECT_TRUE(FindClassDeclX(AST.get())); 138 139 AST = buildASTFromCode("class Y;"); 140 ASSERT_TRUE(AST.get()); 141 EXPECT_FALSE(FindClassDeclX(AST.get())); 142 } 143 144 TEST(buildASTFromCode, ReportsErrors) { 145 TestDiagnosticConsumer Consumer; 146 std::unique_ptr<ASTUnit> AST = buildASTFromCodeWithArgs( 147 "int x = \"A\";", {}, "input.cc", "clang-tool", 148 std::make_shared<PCHContainerOperations>(), 149 getClangStripDependencyFileAdjuster(), FileContentMappings(), &Consumer); 150 EXPECT_TRUE(AST.get()); 151 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 152 } 153 154 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 155 std::unique_ptr<FrontendActionFactory> Factory( 156 newFrontendActionFactory<SyntaxOnlyAction>()); 157 std::unique_ptr<FrontendAction> Action(Factory->create()); 158 EXPECT_TRUE(Action.get() != nullptr); 159 } 160 161 struct IndependentFrontendActionCreator { 162 std::unique_ptr<ASTConsumer> newASTConsumer() { 163 return std::make_unique<FindTopLevelDeclConsumer>(nullptr); 164 } 165 }; 166 167 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { 168 IndependentFrontendActionCreator Creator; 169 std::unique_ptr<FrontendActionFactory> Factory( 170 newFrontendActionFactory(&Creator)); 171 std::unique_ptr<FrontendAction> Action(Factory->create()); 172 EXPECT_TRUE(Action.get() != nullptr); 173 } 174 175 TEST(ToolInvocation, TestMapVirtualFile) { 176 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 177 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 178 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 179 new llvm::vfs::InMemoryFileSystem); 180 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 181 llvm::IntrusiveRefCntPtr<FileManager> Files( 182 new FileManager(FileSystemOptions(), OverlayFileSystem)); 183 std::vector<std::string> Args; 184 Args.push_back("tool-executable"); 185 Args.push_back("-Idef"); 186 Args.push_back("-fsyntax-only"); 187 Args.push_back("test.cpp"); 188 clang::tooling::ToolInvocation Invocation( 189 Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); 190 InMemoryFileSystem->addFile( 191 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 192 InMemoryFileSystem->addFile("def/abc", 0, 193 llvm::MemoryBuffer::getMemBuffer("\n")); 194 EXPECT_TRUE(Invocation.run()); 195 } 196 197 TEST(ToolInvocation, TestVirtualModulesCompilation) { 198 // FIXME: Currently, this only tests that we don't exit with an error if a 199 // mapped module.map is found on the include path. In the future, expand this 200 // test to run a full modules enabled compilation, so we make sure we can 201 // rerun modules compilations with a virtual file system. 202 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 203 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 204 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 205 new llvm::vfs::InMemoryFileSystem); 206 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 207 llvm::IntrusiveRefCntPtr<FileManager> Files( 208 new FileManager(FileSystemOptions(), OverlayFileSystem)); 209 std::vector<std::string> Args; 210 Args.push_back("tool-executable"); 211 Args.push_back("-Idef"); 212 Args.push_back("-fsyntax-only"); 213 Args.push_back("test.cpp"); 214 clang::tooling::ToolInvocation Invocation( 215 Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); 216 InMemoryFileSystem->addFile( 217 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 218 InMemoryFileSystem->addFile("def/abc", 0, 219 llvm::MemoryBuffer::getMemBuffer("\n")); 220 // Add a module.map file in the include directory of our header, so we trigger 221 // the module.map header search logic. 222 InMemoryFileSystem->addFile("def/module.map", 0, 223 llvm::MemoryBuffer::getMemBuffer("\n")); 224 EXPECT_TRUE(Invocation.run()); 225 } 226 227 struct DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer { 228 bool SawSourceManager; 229 230 DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {} 231 232 void HandleDiagnostic(clang::DiagnosticsEngine::Level, 233 const clang::Diagnostic &info) override { 234 SawSourceManager = info.hasSourceManager(); 235 } 236 }; 237 238 TEST(ToolInvocation, DiagConsumerExpectingSourceManager) { 239 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 240 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 241 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 242 new llvm::vfs::InMemoryFileSystem); 243 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 244 llvm::IntrusiveRefCntPtr<FileManager> Files( 245 new FileManager(FileSystemOptions(), OverlayFileSystem)); 246 std::vector<std::string> Args; 247 Args.push_back("tool-executable"); 248 // Note: intentional error; user probably meant -ferror-limit=0. 249 Args.push_back("-ferror-limit=-1"); 250 Args.push_back("-fsyntax-only"); 251 Args.push_back("test.cpp"); 252 clang::tooling::ToolInvocation Invocation( 253 Args, std::make_unique<SyntaxOnlyAction>(), Files.get()); 254 InMemoryFileSystem->addFile( 255 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}\n")); 256 257 DiagnosticConsumerExpectingSourceManager Consumer; 258 Invocation.setDiagnosticConsumer(&Consumer); 259 260 EXPECT_TRUE(Invocation.run()); 261 EXPECT_TRUE(Consumer.SawSourceManager); 262 } 263 264 namespace { 265 /// Overlays the real filesystem with the given VFS and returns the result. 266 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> 267 overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { 268 auto RFS = llvm::vfs::getRealFileSystem(); 269 auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(RFS); 270 OverlayFS->pushOverlay(VFS); 271 return OverlayFS; 272 } 273 274 struct CommandLineExtractorTest : public ::testing::Test { 275 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS; 276 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags; 277 driver::Driver Driver; 278 279 public: 280 CommandLineExtractorTest() 281 : InMemoryFS(new llvm::vfs::InMemoryFileSystem), 282 Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)), 283 Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags, 284 "clang LLVM compiler", overlayRealFS(InMemoryFS)) {} 285 286 void addFile(StringRef Name, StringRef Content) { 287 InMemoryFS->addFile(Name, 0, llvm::MemoryBuffer::getMemBuffer(Content)); 288 } 289 290 const llvm::opt::ArgStringList * 291 extractCC1Arguments(llvm::ArrayRef<const char *> Argv) { 292 const std::unique_ptr<driver::Compilation> Compilation( 293 Driver.BuildCompilation(llvm::makeArrayRef(Argv))); 294 295 return getCC1Arguments(Diags.get(), Compilation.get()); 296 } 297 }; 298 } // namespace 299 300 TEST_F(CommandLineExtractorTest, AcceptOffloading) { 301 addFile("test.c", "int main() {}\n"); 302 const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", 303 "-x", "hip", "test.c", 304 "-nogpulib", "-nogpuinc"}; 305 EXPECT_NE(extractCC1Arguments(Args), nullptr); 306 } 307 308 TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) { 309 addFile("test.c", "int main() {}\n"); 310 const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", 311 "-c", "-x", "hip", 312 "test.c", "-nogpulib", "-nogpuinc"}; 313 EXPECT_NE(extractCC1Arguments(Args), nullptr); 314 } 315 316 TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) { 317 addFile("test.c", "int main() {}\n"); 318 const char *Args[] = { 319 "clang", "-target", "arm64-apple-macosx11.0.0", 320 "-fsyntax-only", "-x", "hip", 321 "test.c", "-nogpulib", "-nogpuinc"}; 322 EXPECT_NE(extractCC1Arguments(Args), nullptr); 323 } 324 325 TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) { 326 addFile("test.c", "int main() {}\n"); 327 const char *Args[] = { 328 "clang", "-target", "arm64-apple-macosx11.0.0", "-fno-integrated-as", 329 "-c", "test.c"}; 330 EXPECT_NE(extractCC1Arguments(Args), nullptr); 331 } 332 333 TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) { 334 addFile("test.c", "int main() {}\n"); 335 const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", 336 "-c", "-fembed-bitcode", "test.c"}; 337 EXPECT_NE(extractCC1Arguments(Args), nullptr); 338 } 339 340 TEST_F(CommandLineExtractorTest, AcceptSaveTemps) { 341 addFile("test.c", "int main() {}\n"); 342 const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", 343 "-c", "-save-temps", "test.c"}; 344 EXPECT_NE(extractCC1Arguments(Args), nullptr); 345 } 346 347 TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) { 348 addFile("test.c", "int main() {}\n"); 349 const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", 350 "-arch", "x86_64", "-arch", 351 "arm64", "-c", "test.c"}; 352 EXPECT_EQ(extractCC1Arguments(Args), nullptr); 353 } 354 355 TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) { 356 addFile("one.c", "void one() {}\n"); 357 addFile("two.c", "void two() {}\n"); 358 const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0", 359 "-c", "one.c", "two.c"}; 360 EXPECT_EQ(extractCC1Arguments(Args), nullptr); 361 } 362 363 struct VerifyEndCallback : public SourceFileCallbacks { 364 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 365 bool handleBeginSource(CompilerInstance &CI) override { 366 ++BeginCalled; 367 return true; 368 } 369 void handleEndSource() override { ++EndCalled; } 370 std::unique_ptr<ASTConsumer> newASTConsumer() { 371 return std::make_unique<FindTopLevelDeclConsumer>(&Matched); 372 } 373 unsigned BeginCalled; 374 unsigned EndCalled; 375 bool Matched; 376 }; 377 378 #if !defined(_WIN32) 379 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 380 VerifyEndCallback EndCallback; 381 382 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 383 std::vector<std::string> Sources; 384 Sources.push_back("/a.cc"); 385 Sources.push_back("/b.cc"); 386 ClangTool Tool(Compilations, Sources); 387 388 Tool.mapVirtualFile("/a.cc", "void a() {}"); 389 Tool.mapVirtualFile("/b.cc", "void b() {}"); 390 391 std::unique_ptr<FrontendActionFactory> Action( 392 newFrontendActionFactory(&EndCallback, &EndCallback)); 393 Tool.run(Action.get()); 394 395 EXPECT_TRUE(EndCallback.Matched); 396 EXPECT_EQ(2u, EndCallback.BeginCalled); 397 EXPECT_EQ(2u, EndCallback.EndCalled); 398 } 399 #endif 400 401 struct SkipBodyConsumer : public clang::ASTConsumer { 402 /// Skip the 'skipMe' function. 403 bool shouldSkipFunctionBody(Decl *D) override { 404 NamedDecl *F = dyn_cast<NamedDecl>(D); 405 return F && F->getNameAsString() == "skipMe"; 406 } 407 }; 408 409 struct SkipBodyAction : public clang::ASTFrontendAction { 410 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 411 StringRef) override { 412 Compiler.getFrontendOpts().SkipFunctionBodies = true; 413 return std::make_unique<SkipBodyConsumer>(); 414 } 415 }; 416 417 TEST(runToolOnCode, TestSkipFunctionBody) { 418 std::vector<std::string> Args = {"-std=c++11"}; 419 std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"}; 420 421 EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(), 422 "int skipMe() { an_error_here }")); 423 EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(), 424 "int skipMeNot() { an_error_here }")); 425 426 // Test constructors with initializers 427 EXPECT_TRUE(runToolOnCodeWithArgs( 428 std::make_unique<SkipBodyAction>(), 429 "struct skipMe { skipMe() : an_error() { more error } };", Args)); 430 EXPECT_TRUE(runToolOnCodeWithArgs( 431 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" 432 "skipMe::skipMe() : an_error([](){;}) { more error }", 433 Args)); 434 EXPECT_TRUE(runToolOnCodeWithArgs( 435 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };" 436 "skipMe::skipMe() : an_error{[](){;}} { more error }", 437 Args)); 438 EXPECT_TRUE(runToolOnCodeWithArgs( 439 std::make_unique<SkipBodyAction>(), 440 "struct skipMe { skipMe(); };" 441 "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }", 442 Args)); 443 EXPECT_TRUE(runToolOnCodeWithArgs( 444 std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };", 445 Args)); 446 447 EXPECT_FALSE(runToolOnCodeWithArgs( 448 std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };", 449 Args)); 450 EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), 451 "struct skipMeNot { skipMeNot(); };" 452 "skipMeNot::skipMeNot() : an_error() { }", 453 Args)); 454 455 // Try/catch 456 EXPECT_TRUE(runToolOnCode( 457 std::make_unique<SkipBodyAction>(), 458 "void skipMe() try { an_error() } catch(error) { error };")); 459 EXPECT_TRUE(runToolOnCode( 460 std::make_unique<SkipBodyAction>(), 461 "struct S { void skipMe() try { an_error() } catch(error) { error } };")); 462 EXPECT_TRUE( 463 runToolOnCode(std::make_unique<SkipBodyAction>(), 464 "void skipMe() try { an_error() } catch(error) { error; }" 465 "catch(error) { error } catch (error) { }")); 466 EXPECT_FALSE(runToolOnCode( 467 std::make_unique<SkipBodyAction>(), 468 "void skipMe() try something;")); // don't crash while parsing 469 470 // Template 471 EXPECT_TRUE(runToolOnCode( 472 std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }" 473 "int x = skipMe<int>();")); 474 EXPECT_FALSE(runToolOnCodeWithArgs( 475 std::make_unique<SkipBodyAction>(), 476 "template<typename T> int skipMeNot() { an_error_here }", Args2)); 477 } 478 479 TEST(runToolOnCodeWithArgs, TestNoDepFile) { 480 llvm::SmallString<32> DepFilePath; 481 ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d", 482 DepFilePath)); 483 std::vector<std::string> Args; 484 Args.push_back("-MMD"); 485 Args.push_back("-MT"); 486 Args.push_back(std::string(DepFilePath.str())); 487 Args.push_back("-MF"); 488 Args.push_back(std::string(DepFilePath.str())); 489 EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args)); 490 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); 491 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); 492 } 493 494 struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction { 495 CheckColoredDiagnosticsAction(bool ShouldShowColor) 496 : ShouldShowColor(ShouldShowColor) {} 497 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 498 StringRef) override { 499 if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor) 500 Compiler.getDiagnostics().Report( 501 Compiler.getDiagnostics().getCustomDiagID( 502 DiagnosticsEngine::Fatal, 503 "getDiagnosticOpts().ShowColors != ShouldShowColor")); 504 return std::make_unique<ASTConsumer>(); 505 } 506 507 private: 508 bool ShouldShowColor = true; 509 }; 510 511 TEST(runToolOnCodeWithArgs, DiagnosticsColor) { 512 EXPECT_TRUE(runToolOnCodeWithArgs( 513 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 514 {"-fcolor-diagnostics"})); 515 EXPECT_TRUE(runToolOnCodeWithArgs( 516 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 517 {"-fno-color-diagnostics"})); 518 EXPECT_TRUE(runToolOnCodeWithArgs( 519 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 520 {"-fno-color-diagnostics", "-fcolor-diagnostics"})); 521 EXPECT_TRUE(runToolOnCodeWithArgs( 522 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 523 {"-fcolor-diagnostics", "-fno-color-diagnostics"})); 524 EXPECT_TRUE(runToolOnCodeWithArgs( 525 std::make_unique<CheckColoredDiagnosticsAction>(true), "", 526 {"-fno-color-diagnostics", "-fdiagnostics-color=always"})); 527 528 // Check that this test would fail if ShowColors is not what it should. 529 EXPECT_FALSE(runToolOnCodeWithArgs( 530 std::make_unique<CheckColoredDiagnosticsAction>(false), "", 531 {"-fcolor-diagnostics"})); 532 } 533 534 TEST(ClangToolTest, ArgumentAdjusters) { 535 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 536 537 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 538 Tool.mapVirtualFile("/a.cc", "void a() {}"); 539 540 std::unique_ptr<FrontendActionFactory> Action( 541 newFrontendActionFactory<SyntaxOnlyAction>()); 542 543 bool Found = false; 544 bool Ran = false; 545 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 546 [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { 547 Ran = true; 548 if (llvm::is_contained(Args, "-fsyntax-only")) 549 Found = true; 550 return Args; 551 }; 552 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 553 Tool.run(Action.get()); 554 EXPECT_TRUE(Ran); 555 EXPECT_TRUE(Found); 556 557 Ran = Found = false; 558 Tool.clearArgumentsAdjusters(); 559 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 560 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 561 Tool.run(Action.get()); 562 EXPECT_TRUE(Ran); 563 EXPECT_FALSE(Found); 564 } 565 566 TEST(ClangToolTest, NoDoubleSyntaxOnly) { 567 FixedCompilationDatabase Compilations("/", {"-fsyntax-only"}); 568 569 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 570 Tool.mapVirtualFile("/a.cc", "void a() {}"); 571 572 std::unique_ptr<FrontendActionFactory> Action( 573 newFrontendActionFactory<SyntaxOnlyAction>()); 574 575 size_t SyntaxOnlyCount = 0; 576 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 577 [&SyntaxOnlyCount](const CommandLineArguments &Args, 578 StringRef /*unused*/) { 579 for (llvm::StringRef Arg : Args) { 580 if (Arg == "-fsyntax-only") 581 ++SyntaxOnlyCount; 582 } 583 return Args; 584 }; 585 586 Tool.clearArgumentsAdjusters(); 587 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 588 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 589 Tool.run(Action.get()); 590 EXPECT_EQ(SyntaxOnlyCount, 1U); 591 } 592 593 TEST(ClangToolTest, NoOutputCommands) { 594 FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd", 595 "--save-temps", 596 "--save-temps=somedir"}); 597 598 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 599 Tool.mapVirtualFile("/a.cc", "void a() {}"); 600 601 std::unique_ptr<FrontendActionFactory> Action( 602 newFrontendActionFactory<SyntaxOnlyAction>()); 603 604 const std::vector<llvm::StringRef> OutputCommands = {"-save-temps"}; 605 bool Ran = false; 606 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 607 [&OutputCommands, &Ran](const CommandLineArguments &Args, 608 StringRef /*unused*/) { 609 for (llvm::StringRef Arg : Args) { 610 for (llvm::StringRef OutputCommand : OutputCommands) 611 EXPECT_FALSE(Arg.contains(OutputCommand)); 612 } 613 Ran = true; 614 return Args; 615 }; 616 617 Tool.clearArgumentsAdjusters(); 618 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 619 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 620 Tool.run(Action.get()); 621 EXPECT_TRUE(Ran); 622 } 623 624 TEST(ClangToolTest, BaseVirtualFileSystemUsage) { 625 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 626 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( 627 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); 628 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 629 new llvm::vfs::InMemoryFileSystem); 630 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 631 632 InMemoryFileSystem->addFile( 633 "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}")); 634 635 ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"), 636 std::make_shared<PCHContainerOperations>(), OverlayFileSystem); 637 std::unique_ptr<FrontendActionFactory> Action( 638 newFrontendActionFactory<SyntaxOnlyAction>()); 639 EXPECT_EQ(0, Tool.run(Action.get())); 640 } 641 642 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD. 643 TEST(ClangToolTest, StripDependencyFileAdjuster) { 644 FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"}); 645 646 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 647 Tool.mapVirtualFile("/a.cc", "void a() {}"); 648 649 std::unique_ptr<FrontendActionFactory> Action( 650 newFrontendActionFactory<SyntaxOnlyAction>()); 651 652 CommandLineArguments FinalArgs; 653 ArgumentsAdjuster CheckFlagsAdjuster = 654 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 655 FinalArgs = Args; 656 return Args; 657 }; 658 Tool.clearArgumentsAdjusters(); 659 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 660 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 661 Tool.run(Action.get()); 662 663 auto HasFlag = [&FinalArgs](const std::string &Flag) { 664 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 665 }; 666 EXPECT_FALSE(HasFlag("-MD")); 667 EXPECT_FALSE(HasFlag("-MMD")); 668 EXPECT_TRUE(HasFlag("-c")); 669 EXPECT_TRUE(HasFlag("-w")); 670 } 671 672 // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants 673 TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) { 674 FixedCompilationDatabase Compilations( 675 "/", {"/showIncludes", "/showIncludes:user", "-showIncludes", 676 "-showIncludes:user", "-c"}); 677 678 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 679 Tool.mapVirtualFile("/a.cc", "void a() {}"); 680 681 std::unique_ptr<FrontendActionFactory> Action( 682 newFrontendActionFactory<SyntaxOnlyAction>()); 683 684 CommandLineArguments FinalArgs; 685 ArgumentsAdjuster CheckFlagsAdjuster = 686 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 687 FinalArgs = Args; 688 return Args; 689 }; 690 Tool.clearArgumentsAdjusters(); 691 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 692 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 693 Tool.run(Action.get()); 694 695 auto HasFlag = [&FinalArgs](const std::string &Flag) { 696 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 697 }; 698 EXPECT_FALSE(HasFlag("/showIncludes")); 699 EXPECT_FALSE(HasFlag("/showIncludes:user")); 700 EXPECT_FALSE(HasFlag("-showIncludes")); 701 EXPECT_FALSE(HasFlag("-showIncludes:user")); 702 EXPECT_TRUE(HasFlag("-c")); 703 } 704 705 // Check getClangStripDependencyFileAdjuster doesn't strip args when using the 706 // MSVC cl.exe driver 707 TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) { 708 FixedCompilationDatabase Compilations( 709 "/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"}); 710 711 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 712 Tool.mapVirtualFile("/a.cc", "void a() {}"); 713 714 std::unique_ptr<FrontendActionFactory> Action( 715 newFrontendActionFactory<SyntaxOnlyAction>()); 716 717 CommandLineArguments FinalArgs; 718 ArgumentsAdjuster CheckFlagsAdjuster = 719 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 720 FinalArgs = Args; 721 return Args; 722 }; 723 Tool.clearArgumentsAdjusters(); 724 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); 725 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 726 Tool.run(Action.get()); 727 728 auto HasFlag = [&FinalArgs](const std::string &Flag) { 729 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 730 }; 731 EXPECT_TRUE(HasFlag("-MD")); 732 EXPECT_TRUE(HasFlag("-MDd")); 733 EXPECT_TRUE(HasFlag("-MT")); 734 EXPECT_TRUE(HasFlag("-O1")); 735 EXPECT_TRUE(HasFlag("-MTd")); 736 EXPECT_TRUE(HasFlag("-MP")); 737 } 738 739 // Check getClangStripPluginsAdjuster strips plugin related args. 740 TEST(ClangToolTest, StripPluginsAdjuster) { 741 FixedCompilationDatabase Compilations( 742 "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"}); 743 744 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 745 Tool.mapVirtualFile("/a.cc", "void a() {}"); 746 747 std::unique_ptr<FrontendActionFactory> Action( 748 newFrontendActionFactory<SyntaxOnlyAction>()); 749 750 CommandLineArguments FinalArgs; 751 ArgumentsAdjuster CheckFlagsAdjuster = 752 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { 753 FinalArgs = Args; 754 return Args; 755 }; 756 Tool.clearArgumentsAdjusters(); 757 Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); 758 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); 759 Tool.run(Action.get()); 760 761 auto HasFlag = [&FinalArgs](const std::string &Flag) { 762 return llvm::find(FinalArgs, Flag) != FinalArgs.end(); 763 }; 764 EXPECT_FALSE(HasFlag("-Xclang")); 765 EXPECT_FALSE(HasFlag("-add-plugin")); 766 EXPECT_FALSE(HasFlag("-random-plugin")); 767 } 768 769 namespace { 770 /// Find a target name such that looking for it in TargetRegistry by that name 771 /// returns the same target. We expect that there is at least one target 772 /// configured with this property. 773 std::string getAnyTarget() { 774 llvm::InitializeAllTargets(); 775 for (const auto &Target : llvm::TargetRegistry::targets()) { 776 std::string Error; 777 StringRef TargetName(Target.getName()); 778 if (TargetName == "x86-64") 779 TargetName = "x86_64"; 780 if (llvm::TargetRegistry::lookupTarget(std::string(TargetName), Error) == 781 &Target) { 782 return std::string(TargetName); 783 } 784 } 785 return ""; 786 } 787 } 788 789 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { 790 std::string Target = getAnyTarget(); 791 ASSERT_FALSE(Target.empty()); 792 793 std::vector<std::string> Args = {"clang", "-foo"}; 794 addTargetAndModeForProgramName(Args, ""); 795 EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args); 796 addTargetAndModeForProgramName(Args, Target + "-g++"); 797 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, 798 "--driver-mode=g++", "-foo"}), 799 Args); 800 } 801 802 TEST(addTargetAndModeForProgramName, PathIgnored) { 803 std::string Target = getAnyTarget(); 804 ASSERT_FALSE(Target.empty()); 805 806 SmallString<32> ToolPath; 807 llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++"); 808 809 std::vector<std::string> Args = {"clang", "-foo"}; 810 addTargetAndModeForProgramName(Args, ToolPath); 811 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, 812 "--driver-mode=g++", "-foo"}), 813 Args); 814 } 815 816 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { 817 std::string Target = getAnyTarget(); 818 ASSERT_FALSE(Target.empty()); 819 820 std::vector<std::string> Args = {"clang", "-foo", "-target", "something"}; 821 addTargetAndModeForProgramName(Args, Target + "-g++"); 822 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 823 "-target", "something"}), 824 Args); 825 826 std::vector<std::string> ArgsAlt = {"clang", "-foo", "--target=something"}; 827 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 828 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 829 "--target=something"}), 830 ArgsAlt); 831 } 832 833 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { 834 std::string Target = getAnyTarget(); 835 ASSERT_FALSE(Target.empty()); 836 837 std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"}; 838 addTargetAndModeForProgramName(Args, Target + "-g++"); 839 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo", 840 "--driver-mode=abc"}), 841 Args); 842 } 843 844 #ifndef _WIN32 845 TEST(ClangToolTest, BuildASTs) { 846 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 847 848 std::vector<std::string> Sources; 849 Sources.push_back("/a.cc"); 850 Sources.push_back("/b.cc"); 851 ClangTool Tool(Compilations, Sources); 852 853 Tool.mapVirtualFile("/a.cc", "void a() {}"); 854 Tool.mapVirtualFile("/b.cc", "void b() {}"); 855 856 std::vector<std::unique_ptr<ASTUnit>> ASTs; 857 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 858 EXPECT_EQ(2u, ASTs.size()); 859 } 860 861 TEST(ClangToolTest, InjectDiagnosticConsumer) { 862 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 863 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 864 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 865 TestDiagnosticConsumer Consumer; 866 Tool.setDiagnosticConsumer(&Consumer); 867 std::unique_ptr<FrontendActionFactory> Action( 868 newFrontendActionFactory<SyntaxOnlyAction>()); 869 Tool.run(Action.get()); 870 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 871 } 872 873 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 874 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 875 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 876 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 877 TestDiagnosticConsumer Consumer; 878 Tool.setDiagnosticConsumer(&Consumer); 879 std::vector<std::unique_ptr<ASTUnit>> ASTs; 880 Tool.buildASTs(ASTs); 881 EXPECT_EQ(1u, ASTs.size()); 882 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 883 } 884 #endif 885 886 TEST(runToolOnCode, TestResetDiagnostics) { 887 // This is a tool that resets the diagnostic during the compilation. 888 struct ResetDiagnosticAction : public clang::ASTFrontendAction { 889 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 890 StringRef) override { 891 struct Consumer : public clang::ASTConsumer { 892 bool HandleTopLevelDecl(clang::DeclGroupRef D) override { 893 auto &Diags = (*D.begin())->getASTContext().getDiagnostics(); 894 // Ignore any error 895 Diags.Reset(); 896 // Disable warnings because computing the CFG might crash. 897 Diags.setIgnoreAllWarnings(true); 898 return true; 899 } 900 }; 901 return std::make_unique<Consumer>(); 902 } 903 }; 904 905 // Should not crash 906 EXPECT_FALSE( 907 runToolOnCode(std::make_unique<ResetDiagnosticAction>(), 908 "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };" 909 "void func() { long x; Foo f(x); }")); 910 } 911 912 } // end namespace tooling 913 } // end namespace clang 914