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