• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gtest/gtest.h"
22 #include <algorithm>
23 #include <string>
24 
25 namespace clang {
26 namespace tooling {
27 
28 namespace {
29 /// Takes an ast consumer and returns it from CreateASTConsumer. This only
30 /// works with single translation unit compilations.
31 class TestAction : public clang::ASTFrontendAction {
32 public:
33   /// Takes ownership of TestConsumer.
TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)34   explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
35       : TestConsumer(std::move(TestConsumer)) {}
36 
37 protected:
38   std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance & compiler,StringRef dummy)39   CreateASTConsumer(clang::CompilerInstance &compiler,
40                     StringRef dummy) override {
41     /// TestConsumer will be deleted by the framework calling us.
42     return std::move(TestConsumer);
43   }
44 
45 private:
46   std::unique_ptr<clang::ASTConsumer> TestConsumer;
47 };
48 
49 class FindTopLevelDeclConsumer : public clang::ASTConsumer {
50  public:
FindTopLevelDeclConsumer(bool * FoundTopLevelDecl)51   explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
52       : FoundTopLevelDecl(FoundTopLevelDecl) {}
HandleTopLevelDecl(clang::DeclGroupRef DeclGroup)53   bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
54     *FoundTopLevelDecl = true;
55     return true;
56   }
57  private:
58   bool * const FoundTopLevelDecl;
59 };
60 } // end namespace
61 
TEST(runToolOnCode,FindsNoTopLevelDeclOnEmptyCode)62 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
63   bool FoundTopLevelDecl = false;
64   EXPECT_TRUE(
65       runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>(
66                         &FoundTopLevelDecl)),
67                     ""));
68   EXPECT_FALSE(FoundTopLevelDecl);
69 }
70 
71 namespace {
72 class FindClassDeclXConsumer : public clang::ASTConsumer {
73  public:
FindClassDeclXConsumer(bool * FoundClassDeclX)74   FindClassDeclXConsumer(bool *FoundClassDeclX)
75       : FoundClassDeclX(FoundClassDeclX) {}
HandleTopLevelDecl(clang::DeclGroupRef GroupRef)76   bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
77     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
78             *GroupRef.begin())) {
79       if (Record->getName() == "X") {
80         *FoundClassDeclX = true;
81       }
82     }
83     return true;
84   }
85  private:
86   bool *FoundClassDeclX;
87 };
FindClassDeclX(ASTUnit * AST)88 bool FindClassDeclX(ASTUnit *AST) {
89   for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
90                                      e = AST->top_level_end();
91        i != e; ++i) {
92     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
93       if (Record->getName() == "X") {
94         return true;
95       }
96     }
97   }
98   return false;
99 }
100 } // end namespace
101 
TEST(runToolOnCode,FindsClassDecl)102 TEST(runToolOnCode, FindsClassDecl) {
103   bool FoundClassDeclX = false;
104   EXPECT_TRUE(
105       runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
106                         &FoundClassDeclX)),
107                     "class X;"));
108   EXPECT_TRUE(FoundClassDeclX);
109 
110   FoundClassDeclX = false;
111   EXPECT_TRUE(
112       runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
113                         &FoundClassDeclX)),
114                     "class Y;"));
115   EXPECT_FALSE(FoundClassDeclX);
116 }
117 
TEST(buildASTFromCode,FindsClassDecl)118 TEST(buildASTFromCode, FindsClassDecl) {
119   std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
120   ASSERT_TRUE(AST.get());
121   EXPECT_TRUE(FindClassDeclX(AST.get()));
122 
123   AST = buildASTFromCode("class Y;");
124   ASSERT_TRUE(AST.get());
125   EXPECT_FALSE(FindClassDeclX(AST.get()));
126 }
127 
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromType)128 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
129   std::unique_ptr<FrontendActionFactory> Factory(
130       newFrontendActionFactory<SyntaxOnlyAction>());
131   std::unique_ptr<FrontendAction> Action(Factory->create());
132   EXPECT_TRUE(Action.get() != nullptr);
133 }
134 
135 struct IndependentFrontendActionCreator {
newASTConsumerclang::tooling::IndependentFrontendActionCreator136   std::unique_ptr<ASTConsumer> newASTConsumer() {
137     return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr);
138   }
139 };
140 
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromFactoryType)141 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
142   IndependentFrontendActionCreator Creator;
143   std::unique_ptr<FrontendActionFactory> Factory(
144       newFrontendActionFactory(&Creator));
145   std::unique_ptr<FrontendAction> Action(Factory->create());
146   EXPECT_TRUE(Action.get() != nullptr);
147 }
148 
TEST(ToolInvocation,TestMapVirtualFile)149 TEST(ToolInvocation, TestMapVirtualFile) {
150   IntrusiveRefCntPtr<clang::FileManager> Files(
151       new clang::FileManager(clang::FileSystemOptions()));
152   std::vector<std::string> Args;
153   Args.push_back("tool-executable");
154   Args.push_back("-Idef");
155   Args.push_back("-fsyntax-only");
156   Args.push_back("test.cpp");
157   clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
158                                             Files.get());
159   Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
160   Invocation.mapVirtualFile("def/abc", "\n");
161   EXPECT_TRUE(Invocation.run());
162 }
163 
TEST(ToolInvocation,TestVirtualModulesCompilation)164 TEST(ToolInvocation, TestVirtualModulesCompilation) {
165   // FIXME: Currently, this only tests that we don't exit with an error if a
166   // mapped module.map is found on the include path. In the future, expand this
167   // test to run a full modules enabled compilation, so we make sure we can
168   // rerun modules compilations with a virtual file system.
169   IntrusiveRefCntPtr<clang::FileManager> Files(
170       new clang::FileManager(clang::FileSystemOptions()));
171   std::vector<std::string> Args;
172   Args.push_back("tool-executable");
173   Args.push_back("-Idef");
174   Args.push_back("-fsyntax-only");
175   Args.push_back("test.cpp");
176   clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
177                                             Files.get());
178   Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
179   Invocation.mapVirtualFile("def/abc", "\n");
180   // Add a module.map file in the include directory of our header, so we trigger
181   // the module.map header search logic.
182   Invocation.mapVirtualFile("def/module.map", "\n");
183   EXPECT_TRUE(Invocation.run());
184 }
185 
186 struct VerifyEndCallback : public SourceFileCallbacks {
VerifyEndCallbackclang::tooling::VerifyEndCallback187   VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
handleBeginSourceclang::tooling::VerifyEndCallback188   bool handleBeginSource(CompilerInstance &CI, StringRef Filename) override {
189     ++BeginCalled;
190     return true;
191   }
handleEndSourceclang::tooling::VerifyEndCallback192   void handleEndSource() override { ++EndCalled; }
newASTConsumerclang::tooling::VerifyEndCallback193   std::unique_ptr<ASTConsumer> newASTConsumer() {
194     return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched);
195   }
196   unsigned BeginCalled;
197   unsigned EndCalled;
198   bool Matched;
199 };
200 
201 #if !defined(LLVM_ON_WIN32)
TEST(newFrontendActionFactory,InjectsSourceFileCallbacks)202 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
203   VerifyEndCallback EndCallback;
204 
205   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
206   std::vector<std::string> Sources;
207   Sources.push_back("/a.cc");
208   Sources.push_back("/b.cc");
209   ClangTool Tool(Compilations, Sources);
210 
211   Tool.mapVirtualFile("/a.cc", "void a() {}");
212   Tool.mapVirtualFile("/b.cc", "void b() {}");
213 
214   std::unique_ptr<FrontendActionFactory> Action(
215       newFrontendActionFactory(&EndCallback, &EndCallback));
216   Tool.run(Action.get());
217 
218   EXPECT_TRUE(EndCallback.Matched);
219   EXPECT_EQ(2u, EndCallback.BeginCalled);
220   EXPECT_EQ(2u, EndCallback.EndCalled);
221 }
222 #endif
223 
224 struct SkipBodyConsumer : public clang::ASTConsumer {
225   /// Skip the 'skipMe' function.
shouldSkipFunctionBodyclang::tooling::SkipBodyConsumer226   bool shouldSkipFunctionBody(Decl *D) override {
227     FunctionDecl *F = dyn_cast<FunctionDecl>(D);
228     return F && F->getNameAsString() == "skipMe";
229   }
230 };
231 
232 struct SkipBodyAction : public clang::ASTFrontendAction {
CreateASTConsumerclang::tooling::SkipBodyAction233   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
234                                                  StringRef) override {
235     Compiler.getFrontendOpts().SkipFunctionBodies = true;
236     return llvm::make_unique<SkipBodyConsumer>();
237   }
238 };
239 
TEST(runToolOnCode,TestSkipFunctionBody)240 TEST(runToolOnCode, TestSkipFunctionBody) {
241   EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
242                             "int skipMe() { an_error_here }"));
243   EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
244                              "int skipMeNot() { an_error_here }"));
245 }
246 
TEST(runToolOnCodeWithArgs,TestNoDepFile)247 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
248   llvm::SmallString<32> DepFilePath;
249   ASSERT_FALSE(
250       llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath));
251   std::vector<std::string> Args;
252   Args.push_back("-MMD");
253   Args.push_back("-MT");
254   Args.push_back(DepFilePath.str());
255   Args.push_back("-MF");
256   Args.push_back(DepFilePath.str());
257   EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args));
258   EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
259   EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
260 }
261 
TEST(ClangToolTest,ArgumentAdjusters)262 TEST(ClangToolTest, ArgumentAdjusters) {
263   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
264 
265   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
266   Tool.mapVirtualFile("/a.cc", "void a() {}");
267 
268   std::unique_ptr<FrontendActionFactory> Action(
269       newFrontendActionFactory<SyntaxOnlyAction>());
270 
271   bool Found = false;
272   bool Ran = false;
273   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
274       [&Found, &Ran](const CommandLineArguments &Args) {
275     Ran = true;
276     if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end())
277       Found = true;
278     return Args;
279   };
280   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
281   Tool.run(Action.get());
282   EXPECT_TRUE(Ran);
283   EXPECT_TRUE(Found);
284 
285   Ran = Found = false;
286   Tool.clearArgumentsAdjusters();
287   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
288   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
289   Tool.run(Action.get());
290   EXPECT_TRUE(Ran);
291   EXPECT_FALSE(Found);
292 }
293 
294 #ifndef LLVM_ON_WIN32
TEST(ClangToolTest,BuildASTs)295 TEST(ClangToolTest, BuildASTs) {
296   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
297 
298   std::vector<std::string> Sources;
299   Sources.push_back("/a.cc");
300   Sources.push_back("/b.cc");
301   ClangTool Tool(Compilations, Sources);
302 
303   Tool.mapVirtualFile("/a.cc", "void a() {}");
304   Tool.mapVirtualFile("/b.cc", "void b() {}");
305 
306   std::vector<std::unique_ptr<ASTUnit>> ASTs;
307   EXPECT_EQ(0, Tool.buildASTs(ASTs));
308   EXPECT_EQ(2u, ASTs.size());
309 }
310 
311 struct TestDiagnosticConsumer : public DiagnosticConsumer {
TestDiagnosticConsumerclang::tooling::TestDiagnosticConsumer312   TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
HandleDiagnosticclang::tooling::TestDiagnosticConsumer313   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
314                         const Diagnostic &Info) override {
315     ++NumDiagnosticsSeen;
316   }
317   unsigned NumDiagnosticsSeen;
318 };
319 
TEST(ClangToolTest,InjectDiagnosticConsumer)320 TEST(ClangToolTest, InjectDiagnosticConsumer) {
321   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
322   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
323   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
324   TestDiagnosticConsumer Consumer;
325   Tool.setDiagnosticConsumer(&Consumer);
326   std::unique_ptr<FrontendActionFactory> Action(
327       newFrontendActionFactory<SyntaxOnlyAction>());
328   Tool.run(Action.get());
329   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
330 }
331 
TEST(ClangToolTest,InjectDiagnosticConsumerInBuildASTs)332 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
333   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
334   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
335   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
336   TestDiagnosticConsumer Consumer;
337   Tool.setDiagnosticConsumer(&Consumer);
338   std::vector<std::unique_ptr<ASTUnit>> ASTs;
339   Tool.buildASTs(ASTs);
340   EXPECT_EQ(1u, ASTs.size());
341   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
342 }
343 #endif
344 
345 } // end namespace tooling
346 } // end namespace clang
347