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     NamedDecl *F = dyn_cast<NamedDecl>(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   std::vector<std::string> Args = {"-std=c++11"};
    259   std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
    260 
    261   EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
    262                             "int skipMe() { an_error_here }"));
    263   EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
    264                              "int skipMeNot() { an_error_here }"));
    265 
    266   // Test constructors with initializers
    267   EXPECT_TRUE(runToolOnCodeWithArgs(
    268       new SkipBodyAction,
    269       "struct skipMe { skipMe() : an_error() { more error } };", Args));
    270   EXPECT_TRUE(runToolOnCodeWithArgs(
    271       new SkipBodyAction, "struct skipMe { skipMe(); };"
    272                           "skipMe::skipMe() : an_error([](){;}) { more error }",
    273       Args));
    274   EXPECT_TRUE(runToolOnCodeWithArgs(
    275       new SkipBodyAction, "struct skipMe { skipMe(); };"
    276                           "skipMe::skipMe() : an_error{[](){;}} { more error }",
    277       Args));
    278   EXPECT_TRUE(runToolOnCodeWithArgs(
    279       new SkipBodyAction,
    280       "struct skipMe { skipMe(); };"
    281       "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
    282       Args));
    283   EXPECT_TRUE(runToolOnCodeWithArgs(
    284       new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };",
    285       Args));
    286 
    287   EXPECT_FALSE(runToolOnCodeWithArgs(
    288       new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };",
    289       Args));
    290   EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction,
    291                                      "struct skipMeNot { skipMeNot(); };"
    292                                      "skipMeNot::skipMeNot() : an_error() { }",
    293                                      Args));
    294 
    295   // Try/catch
    296   EXPECT_TRUE(runToolOnCode(
    297       new SkipBodyAction,
    298       "void skipMe() try { an_error() } catch(error) { error };"));
    299   EXPECT_TRUE(runToolOnCode(
    300       new SkipBodyAction,
    301       "struct S { void skipMe() try { an_error() } catch(error) { error } };"));
    302   EXPECT_TRUE(
    303       runToolOnCode(new SkipBodyAction,
    304                     "void skipMe() try { an_error() } catch(error) { error; }"
    305                     "catch(error) { error } catch (error) { }"));
    306   EXPECT_FALSE(runToolOnCode(
    307       new SkipBodyAction,
    308       "void skipMe() try something;")); // don't crash while parsing
    309 
    310   // Template
    311   EXPECT_TRUE(runToolOnCode(
    312       new SkipBodyAction, "template<typename T> int skipMe() { an_error_here }"
    313                           "int x = skipMe<int>();"));
    314   EXPECT_FALSE(runToolOnCodeWithArgs(
    315       new SkipBodyAction,
    316       "template<typename T> int skipMeNot() { an_error_here }", Args2));
    317 }
    318 
    319 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
    320   llvm::SmallString<32> DepFilePath;
    321   ASSERT_FALSE(
    322       llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath));
    323   std::vector<std::string> Args;
    324   Args.push_back("-MMD");
    325   Args.push_back("-MT");
    326   Args.push_back(DepFilePath.str());
    327   Args.push_back("-MF");
    328   Args.push_back(DepFilePath.str());
    329   EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args));
    330   EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
    331   EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
    332 }
    333 
    334 TEST(ClangToolTest, ArgumentAdjusters) {
    335   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    336 
    337   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
    338   Tool.mapVirtualFile("/a.cc", "void a() {}");
    339 
    340   std::unique_ptr<FrontendActionFactory> Action(
    341       newFrontendActionFactory<SyntaxOnlyAction>());
    342 
    343   bool Found = false;
    344   bool Ran = false;
    345   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
    346       [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
    347     Ran = true;
    348     if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end())
    349       Found = true;
    350     return Args;
    351   };
    352   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
    353   Tool.run(Action.get());
    354   EXPECT_TRUE(Ran);
    355   EXPECT_TRUE(Found);
    356 
    357   Ran = Found = false;
    358   Tool.clearArgumentsAdjusters();
    359   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
    360   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
    361   Tool.run(Action.get());
    362   EXPECT_TRUE(Ran);
    363   EXPECT_FALSE(Found);
    364 }
    365 
    366 namespace {
    367 /// Find a target name such that looking for it in TargetRegistry by that name
    368 /// returns the same target. We expect that there is at least one target
    369 /// configured with this property.
    370 std::string getAnyTarget() {
    371   llvm::InitializeAllTargets();
    372   for (const auto &Target : llvm::TargetRegistry::targets()) {
    373     std::string Error;
    374     StringRef TargetName(Target.getName());
    375     if (TargetName == "x86-64")
    376       TargetName = "x86_64";
    377     if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) {
    378       return TargetName;
    379     }
    380   }
    381   return "";
    382 }
    383 }
    384 
    385 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
    386   std::string Target = getAnyTarget();
    387   ASSERT_FALSE(Target.empty());
    388 
    389   std::vector<std::string> Args = {"clang", "-foo"};
    390   addTargetAndModeForProgramName(Args, "");
    391   EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
    392   addTargetAndModeForProgramName(Args, Target + "-g++");
    393   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
    394                                       "--driver-mode=g++", "-foo"}),
    395             Args);
    396 }
    397 
    398 TEST(addTargetAndModeForProgramName, PathIgnored) {
    399   std::string Target = getAnyTarget();
    400   ASSERT_FALSE(Target.empty());
    401 
    402   SmallString<32> ToolPath;
    403   llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
    404 
    405   std::vector<std::string> Args = {"clang", "-foo"};
    406   addTargetAndModeForProgramName(Args, ToolPath);
    407   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
    408                                       "--driver-mode=g++", "-foo"}),
    409             Args);
    410 }
    411 
    412 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
    413   std::string Target = getAnyTarget();
    414   ASSERT_FALSE(Target.empty());
    415 
    416   std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
    417   addTargetAndModeForProgramName(Args, Target + "-g++");
    418   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
    419                                       "-target", "something"}),
    420             Args);
    421 
    422   std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"};
    423   addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
    424   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
    425                                       "-target=something"}),
    426             ArgsAlt);
    427 }
    428 
    429 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
    430   std::string Target = getAnyTarget();
    431   ASSERT_FALSE(Target.empty());
    432 
    433   std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
    434   addTargetAndModeForProgramName(Args, Target + "-g++");
    435   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
    436                                       "--driver-mode=abc"}),
    437             Args);
    438 
    439   std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"};
    440   addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
    441   EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
    442                                       "--driver-mode", "abc"}),
    443             ArgsAlt);
    444 }
    445 
    446 #ifndef LLVM_ON_WIN32
    447 TEST(ClangToolTest, BuildASTs) {
    448   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    449 
    450   std::vector<std::string> Sources;
    451   Sources.push_back("/a.cc");
    452   Sources.push_back("/b.cc");
    453   ClangTool Tool(Compilations, Sources);
    454 
    455   Tool.mapVirtualFile("/a.cc", "void a() {}");
    456   Tool.mapVirtualFile("/b.cc", "void b() {}");
    457 
    458   std::vector<std::unique_ptr<ASTUnit>> ASTs;
    459   EXPECT_EQ(0, Tool.buildASTs(ASTs));
    460   EXPECT_EQ(2u, ASTs.size());
    461 }
    462 
    463 struct TestDiagnosticConsumer : public DiagnosticConsumer {
    464   TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
    465   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
    466                         const Diagnostic &Info) override {
    467     ++NumDiagnosticsSeen;
    468   }
    469   unsigned NumDiagnosticsSeen;
    470 };
    471 
    472 TEST(ClangToolTest, InjectDiagnosticConsumer) {
    473   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    474   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
    475   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
    476   TestDiagnosticConsumer Consumer;
    477   Tool.setDiagnosticConsumer(&Consumer);
    478   std::unique_ptr<FrontendActionFactory> Action(
    479       newFrontendActionFactory<SyntaxOnlyAction>());
    480   Tool.run(Action.get());
    481   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
    482 }
    483 
    484 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
    485   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
    486   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
    487   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
    488   TestDiagnosticConsumer Consumer;
    489   Tool.setDiagnosticConsumer(&Consumer);
    490   std::vector<std::unique_ptr<ASTUnit>> ASTs;
    491   Tool.buildASTs(ASTs);
    492   EXPECT_EQ(1u, ASTs.size());
    493   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
    494 }
    495 #endif
    496 
    497 } // end namespace tooling
    498 } // end namespace clang
    499