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.
TestAction(std::unique_ptr<clang::ASTConsumer> 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>
CreateASTConsumer(clang::CompilerInstance & compiler,StringRef dummy)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:
FindTopLevelDeclConsumer(bool * FoundTopLevelDecl)50 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
51 : FoundTopLevelDecl(FoundTopLevelDecl) {}
HandleTopLevelDecl(clang::DeclGroupRef DeclGroup)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
TEST(runToolOnCode,FindsNoTopLevelDeclOnEmptyCode)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:
FindClassDeclXConsumer(bool * FoundClassDeclX)73 FindClassDeclXConsumer(bool *FoundClassDeclX)
74 : FoundClassDeclX(FoundClassDeclX) {}
HandleTopLevelDecl(clang::DeclGroupRef GroupRef)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 };
FindClassDeclX(ASTUnit * AST)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
TEST(runToolOnCode,FindsClassDecl)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
TEST(buildASTFromCode,FindsClassDecl)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
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromType)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 {
newASTConsumerclang::tooling::IndependentFrontendActionCreator135 std::unique_ptr<ASTConsumer> newASTConsumer() {
136 return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr);
137 }
138 };
139
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromFactoryType)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
TEST(ToolInvocation,TestMapVirtualFile)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
TEST(ToolInvocation,TestVirtualModulesCompilation)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 {
VerifyEndCallbackclang::tooling::VerifyEndCallback186 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
handleBeginSourceclang::tooling::VerifyEndCallback187 virtual bool handleBeginSource(CompilerInstance &CI,
188 StringRef Filename) override {
189 ++BeginCalled;
190 return true;
191 }
handleEndSourceclang::tooling::VerifyEndCallback192 virtual void handleEndSource() override {
193 ++EndCalled;
194 }
newASTConsumerclang::tooling::VerifyEndCallback195 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)
TEST(newFrontendActionFactory,InjectsSourceFileCallbacks)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.
shouldSkipFunctionBodyclang::tooling::SkipBodyConsumer228 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>
CreateASTConsumerclang::tooling::SkipBodyAction236 CreateASTConsumer(CompilerInstance &Compiler, StringRef) {
237 Compiler.getFrontendOpts().SkipFunctionBodies = true;
238 return llvm::make_unique<SkipBodyConsumer>();
239 }
240 };
241
TEST(runToolOnCode,TestSkipFunctionBody)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
TEST(runToolOnCodeWithArgs,TestNoDepFile)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
TEST(ClangToolTest,ArgumentAdjusters)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
TEST(ClangToolTest,BuildASTs)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 {
TestDiagnosticConsumerclang::tooling::TestDiagnosticConsumer314 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
HandleDiagnosticclang::tooling::TestDiagnosticConsumer315 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
316 const Diagnostic &Info) {
317 ++NumDiagnosticsSeen;
318 }
319 unsigned NumDiagnosticsSeen;
320 };
321
TEST(ClangToolTest,InjectDiagnosticConsumer)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
TEST(ClangToolTest,InjectDiagnosticConsumerInBuildASTs)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
349