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