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