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