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