Home | History | Annotate | Download | only in Tooling
      1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/AST/ASTConsumer.h"
     11 #include "clang/AST/DeclCXX.h"
     12 #include "clang/AST/DeclGroup.h"
     13 #include "clang/Frontend/ASTUnit.h"
     14 #include "clang/Frontend/CompilerInstance.h"
     15 #include "clang/Frontend/FrontendAction.h"
     16 #include "clang/Frontend/FrontendActions.h"
     17 #include "clang/Tooling/CompilationDatabase.h"
     18 #include "clang/Tooling/Tooling.h"
     19 #include "llvm/ADT/STLExtras.h"
     20 #include "llvm/Config/llvm-config.h"
     21 #include "llvm/Support/TargetSelect.h"
     22 #include "llvm/Support/TargetRegistry.h"
     23 #include "gtest/gtest.h"
     24 #include <algorithm>
     25 #include <string>
     26 
     27 namespace clang {
     28 namespace tooling {
     29 
     30 namespace {
     31 /// Takes an ast consumer and returns it from CreateASTConsumer. This only
     32 /// works with single translation unit compilations.
     33 class TestAction : public clang::ASTFrontendAction {
     34 public:
     35   /// Takes ownership of TestConsumer.
     36   explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
     37       : TestConsumer(std::move(TestConsumer)) {}
     38 
     39 protected:
     40   std::unique_ptr<clang::ASTConsumer>
     41   CreateASTConsumer(clang::CompilerInstance &compiler,
     42                     StringRef dummy) override {
     43     /// TestConsumer will be deleted by the framework calling us.
     44     return std::move(TestConsumer);
     45   }
     46 
     47 private:
     48   std::unique_ptr<clang::ASTConsumer> TestConsumer;
     49 };
     50 
     51 class FindTopLevelDeclConsumer : public clang::ASTConsumer {
     52  public:
     53   explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
     54       : FoundTopLevelDecl(FoundTopLevelDecl) {}
     55   bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
     56     *FoundTopLevelDecl = true;
     57     return true;
     58   }
     59  private:
     60   bool * const FoundTopLevelDecl;
     61 };
     62 } // end namespace
     63 
     64 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
     65   bool FoundTopLevelDecl = false;
     66   EXPECT_TRUE(
     67       runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>(
     68                         &FoundTopLevelDecl)),
     69                     ""));
     70   EXPECT_FALSE(FoundTopLevelDecl);
     71 }
     72 
     73 namespace {
     74 class FindClassDeclXConsumer : public clang::ASTConsumer {
     75  public:
     76   FindClassDeclXConsumer(bool *FoundClassDeclX)
     77       : FoundClassDeclX(FoundClassDeclX) {}
     78   bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
     79     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
     80             *GroupRef.begin())) {
     81       if (Record->getName() == "X") {
     82         *FoundClassDeclX = true;
     83       }
     84     }
     85     return true;
     86   }
     87  private:
     88   bool *FoundClassDeclX;
     89 };
     90 bool FindClassDeclX(ASTUnit *AST) {
     91   for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
     92                                      e = AST->top_level_end();
     93        i != e; ++i) {
     94     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
     95       if (Record->getName() == "X") {
     96         return true;
     97       }
     98     }
     99   }
    100   return false;
    101 }
    102 } // end namespace
    103 
    104 TEST(runToolOnCode, FindsClassDecl) {
    105   bool FoundClassDeclX = false;
    106   EXPECT_TRUE(
    107       runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
    108                         &FoundClassDeclX)),
    109                     "class X;"));
    110   EXPECT_TRUE(FoundClassDeclX);
    111 
    112   FoundClassDeclX = false;
    113   EXPECT_TRUE(
    114       runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
    115                         &FoundClassDeclX)),
    116                     "class Y;"));
    117   EXPECT_FALSE(FoundClassDeclX);
    118 }
    119 
    120 TEST(buildASTFromCode, FindsClassDecl) {
    121   std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
    122   ASSERT_TRUE(AST.get());
    123   EXPECT_TRUE(FindClassDeclX(AST.get()));
    124 
    125   AST = buildASTFromCode("class Y;");
    126   ASSERT_TRUE(AST.get());
    127   EXPECT_FALSE(FindClassDeclX(AST.get()));
    128 }
    129 
    130 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
    131   std::unique_ptr<FrontendActionFactory> Factory(
    132       newFrontendActionFactory<SyntaxOnlyAction>());
    133   std::unique_ptr<FrontendAction> Action(Factory->create());
    134   EXPECT_TRUE(Action.get() != nullptr);
    135 }
    136 
    137 struct IndependentFrontendActionCreator {
    138   std::unique_ptr<ASTConsumer> newASTConsumer() {
    139     return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr);
    140   }
    141 };
    142 
    143 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
    144   IndependentFrontendActionCreator Creator;
    145   std::unique_ptr<FrontendActionFactory> Factory(
    146       newFrontendActionFactory(&Creator));
    147   std::unique_ptr<FrontendAction> Action(Factory->create());
    148   EXPECT_TRUE(Action.get() != nullptr);
    149 }
    150 
    151 TEST(ToolInvocation, TestMapVirtualFile) {
    152   llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem(
    153       new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
    154   llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
    155       new vfs::InMemoryFileSystem);
    156   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
    157   llvm::IntrusiveRefCntPtr<FileManager> Files(
    158       new FileManager(FileSystemOptions(), OverlayFileSystem));
    159   std::vector<std::string> Args;
    160   Args.push_back("tool-executable");
    161   Args.push_back("-Idef");
    162   Args.push_back("-fsyntax-only");
    163   Args.push_back("test.cpp");
    164   clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
    165                                             Files.get());
    166   InMemoryFileSystem->addFile(
    167       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
    168   InMemoryFileSystem->addFile("def/abc", 0,
    169                               llvm::MemoryBuffer::getMemBuffer("\n"));
    170   EXPECT_TRUE(Invocation.run());
    171 }
    172 
    173 TEST(ToolInvocation, TestVirtualModulesCompilation) {
    174   // FIXME: Currently, this only tests that we don't exit with an error if a
    175   // mapped module.map is found on the include path. In the future, expand this
    176   // test to run a full modules enabled compilation, so we make sure we can
    177   // rerun modules compilations with a virtual file system.
    178   llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem(
    179       new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
    180   llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
    181       new vfs::InMemoryFileSystem);
    182   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
    183   llvm::IntrusiveRefCntPtr<FileManager> Files(
    184       new FileManager(FileSystemOptions(), OverlayFileSystem));
    185   std::vector<std::string> Args;
    186   Args.push_back("tool-executable");
    187   Args.push_back("-Idef");
    188   Args.push_back("-fsyntax-only");
    189   Args.push_back("test.cpp");
    190   clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
    191                                             Files.get());
    192   InMemoryFileSystem->addFile(
    193       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
    194   InMemoryFileSystem->addFile("def/abc", 0,
    195                               llvm::MemoryBuffer::getMemBuffer("\n"));
    196   // Add a module.map file in the include directory of our header, so we trigger
    197   // the module.map header search logic.
    198   InMemoryFileSystem->addFile("def/module.map", 0,
    199                               llvm::MemoryBuffer::getMemBuffer("\n"));
    200   EXPECT_TRUE(Invocation.run());
    201 }
    202 
    203 struct VerifyEndCallback : public SourceFileCallbacks {
    204   VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
    205   bool handleBeginSource(CompilerInstance &CI, StringRef Filename) override {
    206     ++BeginCalled;
    207     return true;
    208   }
    209   void handleEndSource() override { ++EndCalled; }
    210   std::unique_ptr<ASTConsumer> newASTConsumer() {
    211     return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched);
    212   }
    213   unsigned BeginCalled;
    214   unsigned EndCalled;
    215   bool Matched;
    216 };
    217 
    218 #if !defined(LLVM_ON_WIN32)
    219 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
    220   VerifyEndCallback EndCallback;
    221 
    222   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    223   std::vector<std::string> Sources;
    224   Sources.push_back("/a.cc");
    225   Sources.push_back("/b.cc");
    226   ClangTool Tool(Compilations, Sources);
    227 
    228   Tool.mapVirtualFile("/a.cc", "void a() {}");
    229   Tool.mapVirtualFile("/b.cc", "void b() {}");
    230 
    231   std::unique_ptr<FrontendActionFactory> Action(
    232       newFrontendActionFactory(&EndCallback, &EndCallback));
    233   Tool.run(Action.get());
    234 
    235   EXPECT_TRUE(EndCallback.Matched);
    236   EXPECT_EQ(2u, EndCallback.BeginCalled);
    237   EXPECT_EQ(2u, EndCallback.EndCalled);
    238 }
    239 #endif
    240 
    241 struct SkipBodyConsumer : public clang::ASTConsumer {
    242   /// Skip the 'skipMe' function.
    243   bool shouldSkipFunctionBody(Decl *D) override {
    244     FunctionDecl *F = dyn_cast<FunctionDecl>(D);
    245     return F && F->getNameAsString() == "skipMe";
    246   }
    247 };
    248 
    249 struct SkipBodyAction : public clang::ASTFrontendAction {
    250   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
    251                                                  StringRef) override {
    252     Compiler.getFrontendOpts().SkipFunctionBodies = true;
    253     return llvm::make_unique<SkipBodyConsumer>();
    254   }
    255 };
    256 
    257 TEST(runToolOnCode, TestSkipFunctionBody) {
    258   EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
    259                             "int skipMe() { an_error_here }"));
    260   EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
    261                              "int skipMeNot() { an_error_here }"));
    262 }
    263 
    264 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
    265   llvm::SmallString<32> DepFilePath;
    266   ASSERT_FALSE(
    267       llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath));
    268   std::vector<std::string> Args;
    269   Args.push_back("-MMD");
    270   Args.push_back("-MT");
    271   Args.push_back(DepFilePath.str());
    272   Args.push_back("-MF");
    273   Args.push_back(DepFilePath.str());
    274   EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args));
    275   EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
    276   EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
    277 }
    278 
    279 TEST(ClangToolTest, ArgumentAdjusters) {
    280   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    281 
    282   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
    283   Tool.mapVirtualFile("/a.cc", "void a() {}");
    284 
    285   std::unique_ptr<FrontendActionFactory> Action(
    286       newFrontendActionFactory<SyntaxOnlyAction>());
    287 
    288   bool Found = false;
    289   bool Ran = false;
    290   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
    291       [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
    292     Ran = true;
    293     if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end())
    294       Found = true;
    295     return Args;
    296   };
    297   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
    298   Tool.run(Action.get());
    299   EXPECT_TRUE(Ran);
    300   EXPECT_TRUE(Found);
    301 
    302   Ran = Found = false;
    303   Tool.clearArgumentsAdjusters();
    304   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
    305   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
    306   Tool.run(Action.get());
    307   EXPECT_TRUE(Ran);
    308   EXPECT_FALSE(Found);
    309 }
    310 
    311 namespace {
    312 /// Find a target name such that looking for it in TargetRegistry by that name
    313 /// returns the same target. We expect that there is at least one target
    314 /// configured with this property.
    315 std::string getAnyTarget() {
    316   llvm::InitializeAllTargets();
    317   for (const auto &Target : llvm::TargetRegistry::targets()) {
    318     std::string Error;
    319     StringRef TargetName(Target.getName());
    320     if (TargetName == "x86-64")
    321       TargetName = "x86_64";
    322     if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) {
    323       return TargetName;
    324     }
    325   }
    326   return "";
    327 }
    328 }
    329 
    330 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
    331   std::string Target = getAnyTarget();
    332   ASSERT_FALSE(Target.empty());
    333 
    334   std::vector<std::string> Args = {"clang", "-foo"};
    335   addTargetAndModeForProgramName(Args, "");
    336   EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
    337   addTargetAndModeForProgramName(Args, Target + "-g++");
    338   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
    339                                       "--driver-mode=g++", "-foo"}),
    340             Args);
    341 }
    342 
    343 TEST(addTargetAndModeForProgramName, PathIgnored) {
    344   std::string Target = getAnyTarget();
    345   ASSERT_FALSE(Target.empty());
    346 
    347   SmallString<32> ToolPath;
    348   llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
    349 
    350   std::vector<std::string> Args = {"clang", "-foo"};
    351   addTargetAndModeForProgramName(Args, ToolPath);
    352   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
    353                                       "--driver-mode=g++", "-foo"}),
    354             Args);
    355 }
    356 
    357 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
    358   std::string Target = getAnyTarget();
    359   ASSERT_FALSE(Target.empty());
    360 
    361   std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
    362   addTargetAndModeForProgramName(Args, Target + "-g++");
    363   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
    364                                       "-target", "something"}),
    365             Args);
    366 
    367   std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"};
    368   addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
    369   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
    370                                       "-target=something"}),
    371             ArgsAlt);
    372 }
    373 
    374 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
    375   std::string Target = getAnyTarget();
    376   ASSERT_FALSE(Target.empty());
    377 
    378   std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
    379   addTargetAndModeForProgramName(Args, Target + "-g++");
    380   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
    381                                       "--driver-mode=abc"}),
    382             Args);
    383 
    384   std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"};
    385   addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
    386   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
    387                                       "--driver-mode", "abc"}),
    388             ArgsAlt);
    389 }
    390 
    391 #ifndef LLVM_ON_WIN32
    392 TEST(ClangToolTest, BuildASTs) {
    393   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    394 
    395   std::vector<std::string> Sources;
    396   Sources.push_back("/a.cc");
    397   Sources.push_back("/b.cc");
    398   ClangTool Tool(Compilations, Sources);
    399 
    400   Tool.mapVirtualFile("/a.cc", "void a() {}");
    401   Tool.mapVirtualFile("/b.cc", "void b() {}");
    402 
    403   std::vector<std::unique_ptr<ASTUnit>> ASTs;
    404   EXPECT_EQ(0, Tool.buildASTs(ASTs));
    405   EXPECT_EQ(2u, ASTs.size());
    406 }
    407 
    408 struct TestDiagnosticConsumer : public DiagnosticConsumer {
    409   TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
    410   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
    411                         const Diagnostic &Info) override {
    412     ++NumDiagnosticsSeen;
    413   }
    414   unsigned NumDiagnosticsSeen;
    415 };
    416 
    417 TEST(ClangToolTest, InjectDiagnosticConsumer) {
    418   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    419   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
    420   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
    421   TestDiagnosticConsumer Consumer;
    422   Tool.setDiagnosticConsumer(&Consumer);
    423   std::unique_ptr<FrontendActionFactory> Action(
    424       newFrontendActionFactory<SyntaxOnlyAction>());
    425   Tool.run(Action.get());
    426   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
    427 }
    428 
    429 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
    430   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    431   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
    432   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
    433   TestDiagnosticConsumer Consumer;
    434   Tool.setDiagnosticConsumer(&Consumer);
    435   std::vector<std::unique_ptr<ASTUnit>> ASTs;
    436   Tool.buildASTs(ASTs);
    437   EXPECT_EQ(1u, ASTs.size());
    438   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
    439 }
    440 #endif
    441 
    442 } // end namespace tooling
    443 } // end namespace clang
    444