blob: 25df601df82f51fa17edc10baef46712e4dd7029 [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"
19#include "gtest/gtest.h"
Peter Collingbournec689ee72013-11-06 20:12:45 +000020#include "llvm/ADT/STLExtras.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)), ""));
NAKAMURA Takumifdf9d712012-06-16 06:04:05 +000063#if !defined(_MSC_VER)
Meador Inge5d3fb222012-06-16 03:34:49 +000064 EXPECT_FALSE(FoundTopLevelDecl);
NAKAMURA Takumifdf9d712012-06-16 06:04:05 +000065#else
66 // FIXME: LangOpts.MicrosoftExt appends "class type_info;"
67 EXPECT_TRUE(FoundTopLevelDecl);
68#endif
Manuel Klimek47c245a2012-04-04 12:07:46 +000069}
70
71namespace {
72class FindClassDeclXConsumer : public clang::ASTConsumer {
73 public:
74 FindClassDeclXConsumer(bool *FoundClassDeclX)
75 : FoundClassDeclX(FoundClassDeclX) {}
76 virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) {
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};
Peter Collingbournec689ee72013-11-06 20:12:45 +000088bool 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}
Manuel Klimek47c245a2012-04-04 12:07:46 +0000100} // end namespace
101
102TEST(runToolOnCode, FindsClassDecl) {
103 bool FoundClassDeclX = false;
104 EXPECT_TRUE(runToolOnCode(new TestAction(
105 new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;"));
106 EXPECT_TRUE(FoundClassDeclX);
107
108 FoundClassDeclX = false;
109 EXPECT_TRUE(runToolOnCode(new TestAction(
110 new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;"));
111 EXPECT_FALSE(FoundClassDeclX);
112}
113
Peter Collingbournec689ee72013-11-06 20:12:45 +0000114TEST(buildASTFromCode, FindsClassDecl) {
115 OwningPtr<ASTUnit> AST(buildASTFromCode("class X;"));
116 ASSERT_TRUE(AST.get());
117 EXPECT_TRUE(FindClassDeclX(AST.get()));
118
119 AST.reset(buildASTFromCode("class Y;"));
120 ASSERT_TRUE(AST.get());
121 EXPECT_FALSE(FindClassDeclX(AST.get()));
122}
123
Manuel Klimek47c245a2012-04-04 12:07:46 +0000124TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000125 OwningPtr<FrontendActionFactory> Factory(
126 newFrontendActionFactory<SyntaxOnlyAction>());
127 OwningPtr<FrontendAction> Action(Factory->create());
Manuel Klimek47c245a2012-04-04 12:07:46 +0000128 EXPECT_TRUE(Action.get() != NULL);
129}
130
131struct IndependentFrontendActionCreator {
Manuel Klimek5da9dcb2012-07-05 18:13:01 +0000132 ASTConsumer *newASTConsumer() {
133 return new FindTopLevelDeclConsumer(NULL);
134 }
Manuel Klimek47c245a2012-04-04 12:07:46 +0000135};
136
137TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
138 IndependentFrontendActionCreator Creator;
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000139 OwningPtr<FrontendActionFactory> Factory(
140 newFrontendActionFactory(&Creator));
141 OwningPtr<FrontendAction> Action(Factory->create());
Manuel Klimek47c245a2012-04-04 12:07:46 +0000142 EXPECT_TRUE(Action.get() != NULL);
143}
144
Alexander Kornienko55f2ca92012-06-01 14:50:43 +0000145TEST(ToolInvocation, TestMapVirtualFile) {
Peter Collingbournec689ee72013-11-06 20:12:45 +0000146 IntrusiveRefCntPtr<clang::FileManager> Files(
147 new clang::FileManager(clang::FileSystemOptions()));
Alexander Kornienko55f2ca92012-06-01 14:50:43 +0000148 std::vector<std::string> Args;
149 Args.push_back("tool-executable");
150 Args.push_back("-Idef");
151 Args.push_back("-fsyntax-only");
152 Args.push_back("test.cpp");
Peter Collingbournec689ee72013-11-06 20:12:45 +0000153 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
154 Files.getPtr());
Alexander Kornienko55f2ca92012-06-01 14:50:43 +0000155 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
156 Invocation.mapVirtualFile("def/abc", "\n");
157 EXPECT_TRUE(Invocation.run());
158}
159
Manuel Klimek1f76c4e2013-10-24 07:51:24 +0000160TEST(ToolInvocation, TestVirtualModulesCompilation) {
161 // FIXME: Currently, this only tests that we don't exit with an error if a
162 // mapped module.map is found on the include path. In the future, expand this
163 // test to run a full modules enabled compilation, so we make sure we can
164 // rerun modules compilations with a virtual file system.
Peter Collingbournec689ee72013-11-06 20:12:45 +0000165 IntrusiveRefCntPtr<clang::FileManager> Files(
166 new clang::FileManager(clang::FileSystemOptions()));
Manuel Klimek1f76c4e2013-10-24 07:51:24 +0000167 std::vector<std::string> Args;
168 Args.push_back("tool-executable");
169 Args.push_back("-Idef");
170 Args.push_back("-fsyntax-only");
171 Args.push_back("test.cpp");
Peter Collingbournec689ee72013-11-06 20:12:45 +0000172 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
173 Files.getPtr());
Manuel Klimek1f76c4e2013-10-24 07:51:24 +0000174 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
175 Invocation.mapVirtualFile("def/abc", "\n");
176 // Add a module.map file in the include directory of our header, so we trigger
177 // the module.map header search logic.
178 Invocation.mapVirtualFile("def/module.map", "\n");
179 EXPECT_TRUE(Invocation.run());
180}
181
Edwin Vane20c6f542013-05-29 16:01:10 +0000182struct VerifyEndCallback : public SourceFileCallbacks {
183 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
Edwin Vane3a331f32013-05-30 13:59:44 +0000184 virtual bool handleBeginSource(CompilerInstance &CI,
185 StringRef Filename) LLVM_OVERRIDE {
Edwin Vane20c6f542013-05-29 16:01:10 +0000186 ++BeginCalled;
187 return true;
188 }
Edwin Vane3a331f32013-05-30 13:59:44 +0000189 virtual void handleEndSource() {
Edwin Vane20c6f542013-05-29 16:01:10 +0000190 ++EndCalled;
Manuel Klimek8246d872012-10-25 08:49:11 +0000191 }
192 ASTConsumer *newASTConsumer() {
193 return new FindTopLevelDeclConsumer(&Matched);
194 }
Edwin Vane20c6f542013-05-29 16:01:10 +0000195 unsigned BeginCalled;
196 unsigned EndCalled;
Manuel Klimek8246d872012-10-25 08:49:11 +0000197 bool Matched;
198};
199
NAKAMURA Takumi95fd41a2012-10-25 09:38:41 +0000200#if !defined(_WIN32)
Edwin Vane20c6f542013-05-29 16:01:10 +0000201TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
Manuel Klimek8246d872012-10-25 08:49:11 +0000202 VerifyEndCallback EndCallback;
203
204 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
205 std::vector<std::string> Sources;
206 Sources.push_back("/a.cc");
207 Sources.push_back("/b.cc");
208 ClangTool Tool(Compilations, Sources);
209
210 Tool.mapVirtualFile("/a.cc", "void a() {}");
211 Tool.mapVirtualFile("/b.cc", "void b() {}");
212
213 Tool.run(newFrontendActionFactory(&EndCallback, &EndCallback));
214
215 EXPECT_TRUE(EndCallback.Matched);
Edwin Vane20c6f542013-05-29 16:01:10 +0000216 EXPECT_EQ(2u, EndCallback.BeginCalled);
217 EXPECT_EQ(2u, EndCallback.EndCalled);
Manuel Klimek8246d872012-10-25 08:49:11 +0000218}
NAKAMURA Takumi95fd41a2012-10-25 09:38:41 +0000219#endif
Manuel Klimek8246d872012-10-25 08:49:11 +0000220
Richard Smith9219d1b2012-11-27 21:31:01 +0000221struct SkipBodyConsumer : public clang::ASTConsumer {
222 /// Skip the 'skipMe' function.
223 virtual bool shouldSkipFunctionBody(Decl *D) {
224 FunctionDecl *F = dyn_cast<FunctionDecl>(D);
225 return F && F->getNameAsString() == "skipMe";
226 }
227};
228
229struct SkipBodyAction : public clang::ASTFrontendAction {
230 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler,
231 StringRef) {
232 Compiler.getFrontendOpts().SkipFunctionBodies = true;
233 return new SkipBodyConsumer;
234 }
235};
236
Hal Finkel1d3e3d72013-01-28 04:37:38 +0000237TEST(runToolOnCode, TestSkipFunctionBody) {
Richard Smith9219d1b2012-11-27 21:31:01 +0000238 EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
239 "int skipMe() { an_error_here }"));
240 EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
241 "int skipMeNot() { an_error_here }"));
242}
243
Manuel Klimekd91ac932013-06-04 14:44:44 +0000244struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster {
245 bool &Found;
246 bool &Ran;
247
248 CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { }
249
250 virtual CommandLineArguments
251 Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE {
252 Ran = true;
253 for (unsigned I = 0, E = Args.size(); I != E; ++I) {
254 if (Args[I] == "-fsyntax-only") {
255 Found = true;
256 break;
257 }
258 }
259 return Args;
260 }
261};
262
263TEST(ClangToolTest, ArgumentAdjusters) {
264 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
265
266 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
267 Tool.mapVirtualFile("/a.cc", "void a() {}");
268
269 bool Found = false;
270 bool Ran = false;
271 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran));
272 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>());
273 EXPECT_TRUE(Ran);
274 EXPECT_TRUE(Found);
275
276 Ran = Found = false;
277 Tool.clearArgumentsAdjusters();
278 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran));
279 Tool.appendArgumentsAdjuster(new ClangSyntaxOnlyAdjuster());
280 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>());
281 EXPECT_TRUE(Ran);
282 EXPECT_FALSE(Found);
283}
284
Peter Collingbourne671a1e42013-11-06 23:02:51 +0000285#ifndef _WIN32
Peter Collingbournec689ee72013-11-06 20:12:45 +0000286TEST(ClangToolTest, BuildASTs) {
287 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
288
289 std::vector<std::string> Sources;
290 Sources.push_back("/a.cc");
291 Sources.push_back("/b.cc");
292 ClangTool Tool(Compilations, Sources);
293
294 Tool.mapVirtualFile("/a.cc", "void a() {}");
295 Tool.mapVirtualFile("/b.cc", "void b() {}");
296
297 std::vector<ASTUnit *> ASTs;
298 EXPECT_EQ(0, Tool.buildASTs(ASTs));
299 EXPECT_EQ(2u, ASTs.size());
300
301 llvm::DeleteContainerPointers(ASTs);
302}
Peter Collingbourne671a1e42013-11-06 23:02:51 +0000303#endif
Peter Collingbournec689ee72013-11-06 20:12:45 +0000304
Manuel Klimek47c245a2012-04-04 12:07:46 +0000305} // end namespace tooling
306} // end namespace clang