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 "llvm/Support/TargetSelect.h"
22#include "llvm/Support/TargetRegistry.h"
23#include "gtest/gtest.h"
24#include <algorithm>
25#include <string>
26
27namespace clang {
28namespace tooling {
29
30namespace {
31/// Takes an ast consumer and returns it from CreateASTConsumer. This only
32/// works with single translation unit compilations.
33class TestAction : public clang::ASTFrontendAction {
34public:
35  /// Takes ownership of TestConsumer.
36  explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
37      : TestConsumer(std::move(TestConsumer)) {}
38
39protected:
40  std::unique_ptr<clang::ASTConsumer>
41  CreateASTConsumer(clang::CompilerInstance &compiler,
42                    StringRef dummy) override {
43    /// TestConsumer will be deleted by the framework calling us.
44    return std::move(TestConsumer);
45  }
46
47private:
48  std::unique_ptr<clang::ASTConsumer> TestConsumer;
49};
50
51class FindTopLevelDeclConsumer : public clang::ASTConsumer {
52 public:
53  explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
54      : FoundTopLevelDecl(FoundTopLevelDecl) {}
55  bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
56    *FoundTopLevelDecl = true;
57    return true;
58  }
59 private:
60  bool * const FoundTopLevelDecl;
61};
62} // end namespace
63
64TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
65  bool FoundTopLevelDecl = false;
66  EXPECT_TRUE(
67      runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>(
68                        &FoundTopLevelDecl)),
69                    ""));
70  EXPECT_FALSE(FoundTopLevelDecl);
71}
72
73namespace {
74class FindClassDeclXConsumer : public clang::ASTConsumer {
75 public:
76  FindClassDeclXConsumer(bool *FoundClassDeclX)
77      : FoundClassDeclX(FoundClassDeclX) {}
78  bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
79    if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
80            *GroupRef.begin())) {
81      if (Record->getName() == "X") {
82        *FoundClassDeclX = true;
83      }
84    }
85    return true;
86  }
87 private:
88  bool *FoundClassDeclX;
89};
90bool FindClassDeclX(ASTUnit *AST) {
91  for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
92                                     e = AST->top_level_end();
93       i != e; ++i) {
94    if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
95      if (Record->getName() == "X") {
96        return true;
97      }
98    }
99  }
100  return false;
101}
102} // end namespace
103
104TEST(runToolOnCode, FindsClassDecl) {
105  bool FoundClassDeclX = false;
106  EXPECT_TRUE(
107      runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
108                        &FoundClassDeclX)),
109                    "class X;"));
110  EXPECT_TRUE(FoundClassDeclX);
111
112  FoundClassDeclX = false;
113  EXPECT_TRUE(
114      runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
115                        &FoundClassDeclX)),
116                    "class Y;"));
117  EXPECT_FALSE(FoundClassDeclX);
118}
119
120TEST(buildASTFromCode, FindsClassDecl) {
121  std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
122  ASSERT_TRUE(AST.get());
123  EXPECT_TRUE(FindClassDeclX(AST.get()));
124
125  AST = buildASTFromCode("class Y;");
126  ASSERT_TRUE(AST.get());
127  EXPECT_FALSE(FindClassDeclX(AST.get()));
128}
129
130TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
131  std::unique_ptr<FrontendActionFactory> Factory(
132      newFrontendActionFactory<SyntaxOnlyAction>());
133  std::unique_ptr<FrontendAction> Action(Factory->create());
134  EXPECT_TRUE(Action.get() != nullptr);
135}
136
137struct IndependentFrontendActionCreator {
138  std::unique_ptr<ASTConsumer> newASTConsumer() {
139    return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr);
140  }
141};
142
143TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
144  IndependentFrontendActionCreator Creator;
145  std::unique_ptr<FrontendActionFactory> Factory(
146      newFrontendActionFactory(&Creator));
147  std::unique_ptr<FrontendAction> Action(Factory->create());
148  EXPECT_TRUE(Action.get() != nullptr);
149}
150
151TEST(ToolInvocation, TestMapVirtualFile) {
152  llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem(
153      new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
154  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
155      new vfs::InMemoryFileSystem);
156  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
157  llvm::IntrusiveRefCntPtr<FileManager> Files(
158      new FileManager(FileSystemOptions(), OverlayFileSystem));
159  std::vector<std::string> Args;
160  Args.push_back("tool-executable");
161  Args.push_back("-Idef");
162  Args.push_back("-fsyntax-only");
163  Args.push_back("test.cpp");
164  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
165                                            Files.get());
166  InMemoryFileSystem->addFile(
167      "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
168  InMemoryFileSystem->addFile("def/abc", 0,
169                              llvm::MemoryBuffer::getMemBuffer("\n"));
170  EXPECT_TRUE(Invocation.run());
171}
172
173TEST(ToolInvocation, TestVirtualModulesCompilation) {
174  // FIXME: Currently, this only tests that we don't exit with an error if a
175  // mapped module.map is found on the include path. In the future, expand this
176  // test to run a full modules enabled compilation, so we make sure we can
177  // rerun modules compilations with a virtual file system.
178  llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem(
179      new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
180  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
181      new vfs::InMemoryFileSystem);
182  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
183  llvm::IntrusiveRefCntPtr<FileManager> Files(
184      new FileManager(FileSystemOptions(), OverlayFileSystem));
185  std::vector<std::string> Args;
186  Args.push_back("tool-executable");
187  Args.push_back("-Idef");
188  Args.push_back("-fsyntax-only");
189  Args.push_back("test.cpp");
190  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
191                                            Files.get());
192  InMemoryFileSystem->addFile(
193      "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
194  InMemoryFileSystem->addFile("def/abc", 0,
195                              llvm::MemoryBuffer::getMemBuffer("\n"));
196  // Add a module.map file in the include directory of our header, so we trigger
197  // the module.map header search logic.
198  InMemoryFileSystem->addFile("def/module.map", 0,
199                              llvm::MemoryBuffer::getMemBuffer("\n"));
200  EXPECT_TRUE(Invocation.run());
201}
202
203struct VerifyEndCallback : public SourceFileCallbacks {
204  VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
205  bool handleBeginSource(CompilerInstance &CI, StringRef Filename) override {
206    ++BeginCalled;
207    return true;
208  }
209  void handleEndSource() override { ++EndCalled; }
210  std::unique_ptr<ASTConsumer> newASTConsumer() {
211    return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched);
212  }
213  unsigned BeginCalled;
214  unsigned EndCalled;
215  bool Matched;
216};
217
218#if !defined(LLVM_ON_WIN32)
219TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
220  VerifyEndCallback EndCallback;
221
222  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
223  std::vector<std::string> Sources;
224  Sources.push_back("/a.cc");
225  Sources.push_back("/b.cc");
226  ClangTool Tool(Compilations, Sources);
227
228  Tool.mapVirtualFile("/a.cc", "void a() {}");
229  Tool.mapVirtualFile("/b.cc", "void b() {}");
230
231  std::unique_ptr<FrontendActionFactory> Action(
232      newFrontendActionFactory(&EndCallback, &EndCallback));
233  Tool.run(Action.get());
234
235  EXPECT_TRUE(EndCallback.Matched);
236  EXPECT_EQ(2u, EndCallback.BeginCalled);
237  EXPECT_EQ(2u, EndCallback.EndCalled);
238}
239#endif
240
241struct SkipBodyConsumer : public clang::ASTConsumer {
242  /// Skip the 'skipMe' function.
243  bool shouldSkipFunctionBody(Decl *D) override {
244    NamedDecl *F = dyn_cast<NamedDecl>(D);
245    return F && F->getNameAsString() == "skipMe";
246  }
247};
248
249struct SkipBodyAction : public clang::ASTFrontendAction {
250  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
251                                                 StringRef) override {
252    Compiler.getFrontendOpts().SkipFunctionBodies = true;
253    return llvm::make_unique<SkipBodyConsumer>();
254  }
255};
256
257TEST(runToolOnCode, TestSkipFunctionBody) {
258  std::vector<std::string> Args = {"-std=c++11"};
259  std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
260
261  EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
262                            "int skipMe() { an_error_here }"));
263  EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
264                             "int skipMeNot() { an_error_here }"));
265
266  // Test constructors with initializers
267  EXPECT_TRUE(runToolOnCodeWithArgs(
268      new SkipBodyAction,
269      "struct skipMe { skipMe() : an_error() { more error } };", Args));
270  EXPECT_TRUE(runToolOnCodeWithArgs(
271      new SkipBodyAction, "struct skipMe { skipMe(); };"
272                          "skipMe::skipMe() : an_error([](){;}) { more error }",
273      Args));
274  EXPECT_TRUE(runToolOnCodeWithArgs(
275      new SkipBodyAction, "struct skipMe { skipMe(); };"
276                          "skipMe::skipMe() : an_error{[](){;}} { more error }",
277      Args));
278  EXPECT_TRUE(runToolOnCodeWithArgs(
279      new SkipBodyAction,
280      "struct skipMe { skipMe(); };"
281      "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
282      Args));
283  EXPECT_TRUE(runToolOnCodeWithArgs(
284      new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };",
285      Args));
286
287  EXPECT_FALSE(runToolOnCodeWithArgs(
288      new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };",
289      Args));
290  EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction,
291                                     "struct skipMeNot { skipMeNot(); };"
292                                     "skipMeNot::skipMeNot() : an_error() { }",
293                                     Args));
294
295  // Try/catch
296  EXPECT_TRUE(runToolOnCode(
297      new SkipBodyAction,
298      "void skipMe() try { an_error() } catch(error) { error };"));
299  EXPECT_TRUE(runToolOnCode(
300      new SkipBodyAction,
301      "struct S { void skipMe() try { an_error() } catch(error) { error } };"));
302  EXPECT_TRUE(
303      runToolOnCode(new SkipBodyAction,
304                    "void skipMe() try { an_error() } catch(error) { error; }"
305                    "catch(error) { error } catch (error) { }"));
306  EXPECT_FALSE(runToolOnCode(
307      new SkipBodyAction,
308      "void skipMe() try something;")); // don't crash while parsing
309
310  // Template
311  EXPECT_TRUE(runToolOnCode(
312      new SkipBodyAction, "template<typename T> int skipMe() { an_error_here }"
313                          "int x = skipMe<int>();"));
314  EXPECT_FALSE(runToolOnCodeWithArgs(
315      new SkipBodyAction,
316      "template<typename T> int skipMeNot() { an_error_here }", Args2));
317}
318
319TEST(runToolOnCodeWithArgs, TestNoDepFile) {
320  llvm::SmallString<32> DepFilePath;
321  ASSERT_FALSE(
322      llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath));
323  std::vector<std::string> Args;
324  Args.push_back("-MMD");
325  Args.push_back("-MT");
326  Args.push_back(DepFilePath.str());
327  Args.push_back("-MF");
328  Args.push_back(DepFilePath.str());
329  EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args));
330  EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
331  EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
332}
333
334TEST(ClangToolTest, ArgumentAdjusters) {
335  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
336
337  ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
338  Tool.mapVirtualFile("/a.cc", "void a() {}");
339
340  std::unique_ptr<FrontendActionFactory> Action(
341      newFrontendActionFactory<SyntaxOnlyAction>());
342
343  bool Found = false;
344  bool Ran = false;
345  ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
346      [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
347    Ran = true;
348    if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end())
349      Found = true;
350    return Args;
351  };
352  Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
353  Tool.run(Action.get());
354  EXPECT_TRUE(Ran);
355  EXPECT_TRUE(Found);
356
357  Ran = Found = false;
358  Tool.clearArgumentsAdjusters();
359  Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
360  Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
361  Tool.run(Action.get());
362  EXPECT_TRUE(Ran);
363  EXPECT_FALSE(Found);
364}
365
366namespace {
367/// Find a target name such that looking for it in TargetRegistry by that name
368/// returns the same target. We expect that there is at least one target
369/// configured with this property.
370std::string getAnyTarget() {
371  llvm::InitializeAllTargets();
372  for (const auto &Target : llvm::TargetRegistry::targets()) {
373    std::string Error;
374    StringRef TargetName(Target.getName());
375    if (TargetName == "x86-64")
376      TargetName = "x86_64";
377    if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) {
378      return TargetName;
379    }
380  }
381  return "";
382}
383}
384
385TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
386  std::string Target = getAnyTarget();
387  ASSERT_FALSE(Target.empty());
388
389  std::vector<std::string> Args = {"clang", "-foo"};
390  addTargetAndModeForProgramName(Args, "");
391  EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
392  addTargetAndModeForProgramName(Args, Target + "-g++");
393  EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
394                                      "--driver-mode=g++", "-foo"}),
395            Args);
396}
397
398TEST(addTargetAndModeForProgramName, PathIgnored) {
399  std::string Target = getAnyTarget();
400  ASSERT_FALSE(Target.empty());
401
402  SmallString<32> ToolPath;
403  llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
404
405  std::vector<std::string> Args = {"clang", "-foo"};
406  addTargetAndModeForProgramName(Args, ToolPath);
407  EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target,
408                                      "--driver-mode=g++", "-foo"}),
409            Args);
410}
411
412TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
413  std::string Target = getAnyTarget();
414  ASSERT_FALSE(Target.empty());
415
416  std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
417  addTargetAndModeForProgramName(Args, Target + "-g++");
418  EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
419                                      "-target", "something"}),
420            Args);
421
422  std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"};
423  addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
424  EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
425                                      "-target=something"}),
426            ArgsAlt);
427}
428
429TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
430  std::string Target = getAnyTarget();
431  ASSERT_FALSE(Target.empty());
432
433  std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
434  addTargetAndModeForProgramName(Args, Target + "-g++");
435  EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
436                                      "--driver-mode=abc"}),
437            Args);
438
439  std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"};
440  addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
441  EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo",
442                                      "--driver-mode", "abc"}),
443            ArgsAlt);
444}
445
446#ifndef LLVM_ON_WIN32
447TEST(ClangToolTest, BuildASTs) {
448  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
449
450  std::vector<std::string> Sources;
451  Sources.push_back("/a.cc");
452  Sources.push_back("/b.cc");
453  ClangTool Tool(Compilations, Sources);
454
455  Tool.mapVirtualFile("/a.cc", "void a() {}");
456  Tool.mapVirtualFile("/b.cc", "void b() {}");
457
458  std::vector<std::unique_ptr<ASTUnit>> ASTs;
459  EXPECT_EQ(0, Tool.buildASTs(ASTs));
460  EXPECT_EQ(2u, ASTs.size());
461}
462
463struct TestDiagnosticConsumer : public DiagnosticConsumer {
464  TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
465  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
466                        const Diagnostic &Info) override {
467    ++NumDiagnosticsSeen;
468  }
469  unsigned NumDiagnosticsSeen;
470};
471
472TEST(ClangToolTest, InjectDiagnosticConsumer) {
473  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
474  ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
475  Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
476  TestDiagnosticConsumer Consumer;
477  Tool.setDiagnosticConsumer(&Consumer);
478  std::unique_ptr<FrontendActionFactory> Action(
479      newFrontendActionFactory<SyntaxOnlyAction>());
480  Tool.run(Action.get());
481  EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
482}
483
484TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
485  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
486  ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
487  Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
488  TestDiagnosticConsumer Consumer;
489  Tool.setDiagnosticConsumer(&Consumer);
490  std::vector<std::unique_ptr<ASTUnit>> ASTs;
491  Tool.buildASTs(ASTs);
492  EXPECT_EQ(1u, ASTs.size());
493  EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
494}
495#endif
496
497} // end namespace tooling
498} // end namespace clang
499