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