blob: a75710e86c22251ec72cd67af113a5d54e9f09e4 [file] [log] [blame]
Manuel Klimek47c245a2012-04-04 12:07:46 +00001//===- 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"
Peter Collingbournec689ee72013-11-06 20:12:45 +000013#include "clang/Frontend/ASTUnit.h"
Richard Smith9219d1b2012-11-27 21:31:01 +000014#include "clang/Frontend/CompilerInstance.h"
Manuel Klimek47c245a2012-04-04 12:07:46 +000015#include "clang/Frontend/FrontendAction.h"
16#include "clang/Frontend/FrontendActions.h"
17#include "clang/Tooling/CompilationDatabase.h"
18#include "clang/Tooling/Tooling.h"
Peter Collingbournec689ee72013-11-06 20:12:45 +000019#include "llvm/ADT/STLExtras.h"
Chandler Carruth5553d0d2014-01-07 11:51:46 +000020#include "gtest/gtest.h"
Alexander Kornienko55f2ca92012-06-01 14:50:43 +000021#include <string>
Manuel Klimek47c245a2012-04-04 12:07:46 +000022
23namespace clang {
24namespace tooling {
25
26namespace {
27/// Takes an ast consumer and returns it from CreateASTConsumer. This only
28/// works with single translation unit compilations.
29class 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
46class 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
Meador Inge5d3fb222012-06-16 03:34:49 +000059TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
Manuel Klimek47c245a2012-04-04 12:07:46 +000060 bool FoundTopLevelDecl = false;
61 EXPECT_TRUE(runToolOnCode(
62 new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), ""));
Meador Inge5d3fb222012-06-16 03:34:49 +000063 EXPECT_FALSE(FoundTopLevelDecl);
Manuel Klimek47c245a2012-04-04 12:07:46 +000064}
65
66namespace {
67class 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};
Peter Collingbournec689ee72013-11-06 20:12:45 +000083bool 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}
Manuel Klimek47c245a2012-04-04 12:07:46 +000095} // end namespace
96
97TEST(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
Peter Collingbournec689ee72013-11-06 20:12:45 +0000109TEST(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
Manuel Klimek47c245a2012-04-04 12:07:46 +0000119TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000120 OwningPtr<FrontendActionFactory> Factory(
121 newFrontendActionFactory<SyntaxOnlyAction>());
122 OwningPtr<FrontendAction> Action(Factory->create());
Manuel Klimek47c245a2012-04-04 12:07:46 +0000123 EXPECT_TRUE(Action.get() != NULL);
124}
125
126struct IndependentFrontendActionCreator {
Manuel Klimek5da9dcb2012-07-05 18:13:01 +0000127 ASTConsumer *newASTConsumer() {
128 return new FindTopLevelDeclConsumer(NULL);
129 }
Manuel Klimek47c245a2012-04-04 12:07:46 +0000130};
131
132TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
133 IndependentFrontendActionCreator Creator;
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000134 OwningPtr<FrontendActionFactory> Factory(
135 newFrontendActionFactory(&Creator));
136 OwningPtr<FrontendAction> Action(Factory->create());
Manuel Klimek47c245a2012-04-04 12:07:46 +0000137 EXPECT_TRUE(Action.get() != NULL);
138}
139
Alexander Kornienko55f2ca92012-06-01 14:50:43 +0000140TEST(ToolInvocation, TestMapVirtualFile) {
Peter Collingbournec689ee72013-11-06 20:12:45 +0000141 IntrusiveRefCntPtr<clang::FileManager> Files(
142 new clang::FileManager(clang::FileSystemOptions()));
Alexander Kornienko55f2ca92012-06-01 14:50:43 +0000143 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");
Peter Collingbournec689ee72013-11-06 20:12:45 +0000148 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
149 Files.getPtr());
Alexander Kornienko55f2ca92012-06-01 14:50:43 +0000150 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
151 Invocation.mapVirtualFile("def/abc", "\n");
152 EXPECT_TRUE(Invocation.run());
153}
154
Manuel Klimek1f76c4e2013-10-24 07:51:24 +0000155TEST(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.
Peter Collingbournec689ee72013-11-06 20:12:45 +0000160 IntrusiveRefCntPtr<clang::FileManager> Files(
161 new clang::FileManager(clang::FileSystemOptions()));
Manuel Klimek1f76c4e2013-10-24 07:51:24 +0000162 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");
Peter Collingbournec689ee72013-11-06 20:12:45 +0000167 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
168 Files.getPtr());
Manuel Klimek1f76c4e2013-10-24 07:51:24 +0000169 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
Edwin Vane20c6f542013-05-29 16:01:10 +0000177struct VerifyEndCallback : public SourceFileCallbacks {
178 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
Edwin Vane3a331f32013-05-30 13:59:44 +0000179 virtual bool handleBeginSource(CompilerInstance &CI,
Craig Toppera798a9d2014-03-02 09:32:10 +0000180 StringRef Filename) override {
Edwin Vane20c6f542013-05-29 16:01:10 +0000181 ++BeginCalled;
182 return true;
183 }
Edwin Vane3a331f32013-05-30 13:59:44 +0000184 virtual void handleEndSource() {
Edwin Vane20c6f542013-05-29 16:01:10 +0000185 ++EndCalled;
Manuel Klimek8246d872012-10-25 08:49:11 +0000186 }
187 ASTConsumer *newASTConsumer() {
188 return new FindTopLevelDeclConsumer(&Matched);
189 }
Edwin Vane20c6f542013-05-29 16:01:10 +0000190 unsigned BeginCalled;
191 unsigned EndCalled;
Manuel Klimek8246d872012-10-25 08:49:11 +0000192 bool Matched;
193};
194
NAKAMURA Takumi95fd41a2012-10-25 09:38:41 +0000195#if !defined(_WIN32)
Edwin Vane20c6f542013-05-29 16:01:10 +0000196TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
Manuel Klimek8246d872012-10-25 08:49:11 +0000197 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);
Edwin Vane20c6f542013-05-29 16:01:10 +0000211 EXPECT_EQ(2u, EndCallback.BeginCalled);
212 EXPECT_EQ(2u, EndCallback.EndCalled);
Manuel Klimek8246d872012-10-25 08:49:11 +0000213}
NAKAMURA Takumi95fd41a2012-10-25 09:38:41 +0000214#endif
Manuel Klimek8246d872012-10-25 08:49:11 +0000215
Richard Smith9219d1b2012-11-27 21:31:01 +0000216struct 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
224struct SkipBodyAction : public clang::ASTFrontendAction {
225 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler,
226 StringRef) {
227 Compiler.getFrontendOpts().SkipFunctionBodies = true;
228 return new SkipBodyConsumer;
229 }
230};
231
Hal Finkel1d3e3d72013-01-28 04:37:38 +0000232TEST(runToolOnCode, TestSkipFunctionBody) {
Richard Smith9219d1b2012-11-27 21:31:01 +0000233 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
Manuel Klimekd91ac932013-06-04 14:44:44 +0000239struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster {
240 bool &Found;
241 bool &Ran;
242
243 CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { }
244
245 virtual CommandLineArguments
Craig Toppera798a9d2014-03-02 09:32:10 +0000246 Adjust(const CommandLineArguments &Args) override {
Manuel Klimekd91ac932013-06-04 14:44:44 +0000247 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
258TEST(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
Peter Collingbourne671a1e42013-11-06 23:02:51 +0000280#ifndef _WIN32
Peter Collingbournec689ee72013-11-06 20:12:45 +0000281TEST(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
Manuel Klimek64083012013-11-07 23:18:05 +0000299struct 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
308TEST(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
Manuel Klimek31cd3fc2013-11-12 17:53:18 +0000318TEST(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}
NAKAMURA Takumid3b07c62013-11-13 00:18:50 +0000329#endif
Manuel Klimek31cd3fc2013-11-12 17:53:18 +0000330
Manuel Klimek47c245a2012-04-04 12:07:46 +0000331} // end namespace tooling
332} // end namespace clang