brintos

brintos / llvm-project-archived public Read only

0
0
Text · 38.4 KiB · 25e1d67 Raw
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