Clang Project

clang_source_code/unittests/Tooling/ToolingTest.cpp
1//===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/AST/ASTConsumer.h"
10#include "clang/AST/DeclCXX.h"
11#include "clang/AST/DeclGroup.h"
12#include "clang/Frontend/ASTUnit.h"
13#include "clang/Frontend/CompilerInstance.h"
14#include "clang/Frontend/FrontendAction.h"
15#include "clang/Frontend/FrontendActions.h"
16#include "clang/Tooling/CompilationDatabase.h"
17#include "clang/Tooling/Tooling.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/Path.h"
20#include "llvm/Support/TargetRegistry.h"
21#include "llvm/Support/TargetSelect.h"
22#include "gtest/gtest.h"
23#include <algorithm>
24#include <string>
25
26namespace clang {
27namespace tooling {
28
29namespace {
30/// Takes an ast consumer and returns it from CreateASTConsumer. This only
31/// works with single translation unit compilations.
32class TestAction : public clang::ASTFrontendAction {
33public:
34  /// Takes ownership of TestConsumer.
35  explicit TestAction(std::unique_ptr<clang::ASTConsumerTestConsumer)
36      : TestConsumer(std::move(TestConsumer)) {}
37
38protected:
39  std::unique_ptr<clang::ASTConsumer>
40  CreateASTConsumer(clang::CompilerInstance &compiler,
41                    StringRef dummy) override {
42    /// TestConsumer will be deleted by the framework calling us.
43    return std::move(TestConsumer);
44  }
45
46private:
47  std::unique_ptr<clang::ASTConsumerTestConsumer;
48};
49
50class FindTopLevelDeclConsumer : public clang::ASTConsumer {
51 public:
52  explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
53      : FoundTopLevelDecl(FoundTopLevelDecl) {}
54  bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
55    *FoundTopLevelDecl = true;
56    return true;
57  }
58 private:
59  bool * const FoundTopLevelDecl;
60};
61// end namespace
62
63TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
64  bool FoundTopLevelDecl = false;
65  EXPECT_TRUE(
66      runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>(
67                        &FoundTopLevelDecl)),
68                    ""));
69  EXPECT_FALSE(FoundTopLevelDecl);
70}
71
72namespace {
73class FindClassDeclXConsumer : public clang::ASTConsumer {
74 public:
75  FindClassDeclXConsumer(bool *FoundClassDeclX)
76      : FoundClassDeclX(FoundClassDeclX) {}
77  bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
78    if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
79            *GroupRef.begin())) {
80      if (Record->getName() == "X") {
81        *FoundClassDeclX = true;
82      }
83    }
84    return true;
85  }
86 private:
87  bool *FoundClassDeclX;
88};
89bool FindClassDeclX(ASTUnit *AST) {
90  for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
91                                     e = AST->top_level_end();
92       i != e; ++i) {
93    if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
94      if (Record->getName() == "X") {
95        return true;
96      }
97    }
98  }
99  return false;
100}
101// end namespace
102
103TEST(runToolOnCode, FindsClassDecl) {
104  bool FoundClassDeclX = false;
105  EXPECT_TRUE(
106      runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
107                        &FoundClassDeclX)),
108                    "class X;"));
109  EXPECT_TRUE(FoundClassDeclX);
110
111  FoundClassDeclX = false;
112  EXPECT_TRUE(
113      runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>(
114                        &FoundClassDeclX)),
115                    "class Y;"));
116  EXPECT_FALSE(FoundClassDeclX);
117}
118
119TEST(buildASTFromCode, FindsClassDecl) {
120  std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
121  ASSERT_TRUE(AST.get());
122  EXPECT_TRUE(FindClassDeclX(AST.get()));
123
124  AST = buildASTFromCode("class Y;");
125  ASSERT_TRUE(AST.get());
126  EXPECT_FALSE(FindClassDeclX(AST.get()));
127}
128
129TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
130  std::unique_ptr<FrontendActionFactory> Factory(
131      newFrontendActionFactory<SyntaxOnlyAction>());
132  std::unique_ptr<FrontendAction> Action(Factory->create());
133  EXPECT_TRUE(Action.get() != nullptr);
134}
135
136struct IndependentFrontendActionCreator {
137  std::unique_ptr<ASTConsumer> newASTConsumer() {
138    return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr);
139  }
140};
141
142TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
143  IndependentFrontendActionCreator Creator;
144  std::unique_ptr<FrontendActionFactory> Factory(
145      newFrontendActionFactory(&Creator));
146  std::unique_ptr<FrontendAction> Action(Factory->create());
147  EXPECT_TRUE(Action.get() != nullptr);
148}
149
150TEST(ToolInvocation, TestMapVirtualFile) {
151  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
152      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
153  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
154      new llvm::vfs::InMemoryFileSystem);
155  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
156  llvm::IntrusiveRefCntPtr<FileManager> Files(
157      new FileManager(FileSystemOptions(), OverlayFileSystem));
158  std::vector<std::string> Args;
159  Args.push_back("tool-executable");
160  Args.push_back("-Idef");
161  Args.push_back("-fsyntax-only");
162  Args.push_back("test.cpp");
163  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
164                                            Files.get());
165  InMemoryFileSystem->addFile(
166      "test.cpp"0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
167  InMemoryFileSystem->addFile("def/abc"0,
168                              llvm::MemoryBuffer::getMemBuffer("\n"));
169  EXPECT_TRUE(Invocation.run());
170}
171
172TEST(ToolInvocation, TestVirtualModulesCompilation) {
173  // FIXME: Currently, this only tests that we don't exit with an error if a
174  // mapped module.map is found on the include path. In the future, expand this
175  // test to run a full modules enabled compilation, so we make sure we can
176  // rerun modules compilations with a virtual file system.
177  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
178      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
179  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
180      new llvm::vfs::InMemoryFileSystem);
181  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
182  llvm::IntrusiveRefCntPtr<FileManager> Files(
183      new FileManager(FileSystemOptions(), OverlayFileSystem));
184  std::vector<std::string> Args;
185  Args.push_back("tool-executable");
186  Args.push_back("-Idef");
187  Args.push_back("-fsyntax-only");
188  Args.push_back("test.cpp");
189  clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
190                                            Files.get());
191  InMemoryFileSystem->addFile(
192      "test.cpp"0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
193  InMemoryFileSystem->addFile("def/abc"0,
194                              llvm::MemoryBuffer::getMemBuffer("\n"));
195  // Add a module.map file in the include directory of our header, so we trigger
196  // the module.map header search logic.
197  InMemoryFileSystem->addFile("def/module.map"0,
198                              llvm::MemoryBuffer::getMemBuffer("\n"));
199  EXPECT_TRUE(Invocation.run());
200}
201
202struct VerifyEndCallback : public SourceFileCallbacks {
203  VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
204  bool handleBeginSource(CompilerInstance &CI) override {
205    ++BeginCalled;
206    return true;
207  }
208  void handleEndSource() override { ++EndCalled; }
209  std::unique_ptr<ASTConsumer> newASTConsumer() {
210    return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched);
211  }
212  unsigned BeginCalled;
213  unsigned EndCalled;
214  bool Matched;
215};
216
217#if !defined(_WIN32)
218TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
219  VerifyEndCallback EndCallback;
220
221  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
222  std::vector<std::string> Sources;
223  Sources.push_back("/a.cc");
224  Sources.push_back("/b.cc");
225  ClangTool Tool(Compilations, Sources);
226
227  Tool.mapVirtualFile("/a.cc""void a() {}");
228  Tool.mapVirtualFile("/b.cc""void b() {}");
229
230  std::unique_ptr<FrontendActionFactory> Action(
231      newFrontendActionFactory(&EndCallback, &EndCallback));
232  Tool.run(Action.get());
233
234  EXPECT_TRUE(EndCallback.Matched);
235  EXPECT_EQ(2u, EndCallback.BeginCalled);
236  EXPECT_EQ(2u, EndCallback.EndCalled);
237}
238#endif
239
240struct SkipBodyConsumer : public clang::ASTConsumer {
241  /// Skip the 'skipMe' function.
242  bool shouldSkipFunctionBody(Decl *D) override {
243    NamedDecl *F = dyn_cast<NamedDecl>(D);
244    return F && F->getNameAsString() == "skipMe";
245  }
246};
247
248struct SkipBodyAction : public clang::ASTFrontendAction {
249  std::unique_ptr<ASTConsumerCreateASTConsumer(CompilerInstance &Compiler,
250                                                 StringRef) override {
251    Compiler.getFrontendOpts().SkipFunctionBodies = true;
252    return llvm::make_unique<SkipBodyConsumer>();
253  }
254};
255
256TEST(runToolOnCode, TestSkipFunctionBody) {
257  std::vector<std::string> Args = {"-std=c++11"};
258  std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
259
260  EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
261                            "int skipMe() { an_error_here }"));
262  EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
263                             "int skipMeNot() { an_error_here }"));
264
265  // Test constructors with initializers
266  EXPECT_TRUE(runToolOnCodeWithArgs(
267      new SkipBodyAction,
268      "struct skipMe { skipMe() : an_error() { more error } };", Args));
269  EXPECT_TRUE(runToolOnCodeWithArgs(
270      new SkipBodyAction, "struct skipMe { skipMe(); };"
271                          "skipMe::skipMe() : an_error([](){;}) { more error }",
272      Args));
273  EXPECT_TRUE(runToolOnCodeWithArgs(
274      new SkipBodyAction, "struct skipMe { skipMe(); };"
275                          "skipMe::skipMe() : an_error{[](){;}} { more error }",
276      Args));
277  EXPECT_TRUE(runToolOnCodeWithArgs(
278      new SkipBodyAction,
279      "struct skipMe { skipMe(); };"
280      "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
281      Args));
282  EXPECT_TRUE(runToolOnCodeWithArgs(
283      new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };",
284      Args));
285
286  EXPECT_FALSE(runToolOnCodeWithArgs(
287      new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };",
288      Args));
289  EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction,
290                                     "struct skipMeNot { skipMeNot(); };"
291                                     "skipMeNot::skipMeNot() : an_error() { }",
292                                     Args));
293
294  // Try/catch
295  EXPECT_TRUE(runToolOnCode(
296      new SkipBodyAction,
297      "void skipMe() try { an_error() } catch(error) { error };"));
298  EXPECT_TRUE(runToolOnCode(
299      new SkipBodyAction,
300      "struct S { void skipMe() try { an_error() } catch(error) { error } };"));
301  EXPECT_TRUE(
302      runToolOnCode(new SkipBodyAction,
303                    "void skipMe() try { an_error() } catch(error) { error; }"
304                    "catch(error) { error } catch (error) { }"));
305  EXPECT_FALSE(runToolOnCode(
306      new SkipBodyAction,
307      "void skipMe() try something;")); // don't crash while parsing
308
309  // Template
310  EXPECT_TRUE(runToolOnCode(
311      new SkipBodyAction, "template<typename T> int skipMe() { an_error_here }"
312                          "int x = skipMe<int>();"));
313  EXPECT_FALSE(runToolOnCodeWithArgs(
314      new SkipBodyAction,
315      "template<typename T> int skipMeNot() { an_error_here }", Args2));
316}
317
318TEST(runToolOnCodeWithArgs, TestNoDepFile) {
319  llvm::SmallString<32> DepFilePath;
320  ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile""d",
321                                                               DepFilePath));
322  std::vector<std::string> Args;
323  Args.push_back("-MMD");
324  Args.push_back("-MT");
325  Args.push_back(DepFilePath.str());
326  Args.push_back("-MF");
327  Args.push_back(DepFilePath.str());
328  EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args));
329  EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
330  EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
331}
332
333struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction {
334  CheckColoredDiagnosticsAction(bool ShouldShowColor)
335      : ShouldShowColor(ShouldShowColor) {}
336  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
337                                                 StringRef) override {
338    if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor)
339      Compiler.getDiagnostics().Report(
340          Compiler.getDiagnostics().getCustomDiagID(
341              DiagnosticsEngine::Fatal,
342              "getDiagnosticOpts().ShowColors != ShouldShowColor"));
343    return llvm::make_unique<ASTConsumer>();
344  }
345
346private:
347  bool ShouldShowColor = true;
348};
349
350TEST(runToolOnCodeWithArgs, DiagnosticsColor) {
351
352  EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "",
353                                    {"-fcolor-diagnostics"}));
354  EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false),
355                                    "", {"-fno-color-diagnostics"}));
356  EXPECT_TRUE(
357      runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "",
358                            {"-fno-color-diagnostics""-fcolor-diagnostics"}));
359  EXPECT_TRUE(
360      runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), "",
361                            {"-fcolor-diagnostics""-fno-color-diagnostics"}));
362  EXPECT_TRUE(runToolOnCodeWithArgs(
363      new CheckColoredDiagnosticsAction(true), "",
364      {"-fno-color-diagnostics""-fdiagnostics-color=always"}));
365
366  // Check that this test would fail if ShowColors is not what it should.
367  EXPECT_FALSE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false),
368                                     "", {"-fcolor-diagnostics"}));
369}
370
371TEST(ClangToolTest, ArgumentAdjusters) {
372  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
373
374  ClangTool Tool(Compilations, std::vector<std::string>(1"/a.cc"));
375  Tool.mapVirtualFile("/a.cc""void a() {}");
376
377  std::unique_ptr<FrontendActionFactory> Action(
378      newFrontendActionFactory<SyntaxOnlyAction>());
379
380  bool Found = false;
381  bool Ran = false;
382  ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
383      [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
384    Ran = true;
385    if (llvm::is_contained(Args, "-fsyntax-only"))
386      Found = true;
387    return Args;
388  };
389  Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
390  Tool.run(Action.get());
391  EXPECT_TRUE(Ran);
392  EXPECT_TRUE(Found);
393
394  Ran = Found = false;
395  Tool.clearArgumentsAdjusters();
396  Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
397  Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
398  Tool.run(Action.get());
399  EXPECT_TRUE(Ran);
400  EXPECT_FALSE(Found);
401}
402
403TEST(ClangToolTest, BaseVirtualFileSystemUsage) {
404  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
405  llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
406      new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
407  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
408      new llvm::vfs::InMemoryFileSystem);
409  OverlayFileSystem->pushOverlay(InMemoryFileSystem);
410
411  InMemoryFileSystem->addFile(
412      "a.cpp"0, llvm::MemoryBuffer::getMemBuffer("int main() {}"));
413
414  ClangTool Tool(Compilations, std::vector<std::string>(1"a.cpp"),
415                 std::make_shared<PCHContainerOperations>(), OverlayFileSystem);
416  std::unique_ptr<FrontendActionFactory> Action(
417      newFrontendActionFactory<SyntaxOnlyAction>());
418  EXPECT_EQ(0, Tool.run(Action.get()));
419}
420
421// Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD.
422TEST(ClangToolTest, StripDependencyFileAdjuster) {
423  FixedCompilationDatabase Compilations("/", {"-MD""-c""-MMD""-w"});
424
425  ClangTool Tool(Compilations, std::vector<std::string>(1"/a.cc"));
426  Tool.mapVirtualFile("/a.cc""void a() {}");
427
428  std::unique_ptr<FrontendActionFactory> Action(
429      newFrontendActionFactory<SyntaxOnlyAction>());
430
431  CommandLineArguments FinalArgs;
432  ArgumentsAdjuster CheckFlagsAdjuster =
433    [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
434      FinalArgs = Args;
435      return Args;
436    };
437  Tool.clearArgumentsAdjusters();
438  Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
439  Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
440  Tool.run(Action.get());
441
442  auto HasFlag = [&FinalArgs](const std::string &Flag) {
443    return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) !=
444           FinalArgs.end();
445  };
446  EXPECT_FALSE(HasFlag("-MD"));
447  EXPECT_FALSE(HasFlag("-MMD"));
448  EXPECT_TRUE(HasFlag("-c"));
449  EXPECT_TRUE(HasFlag("-w"));
450}
451
452// Check getClangStripPluginsAdjuster strips plugin related args.
453TEST(ClangToolTest, StripPluginsAdjuster) {
454  FixedCompilationDatabase Compilations(
455      "/", {"-Xclang""-add-plugin""-Xclang""random-plugin"});
456
457  ClangTool Tool(Compilations, std::vector<std::string>(1"/a.cc"));
458  Tool.mapVirtualFile("/a.cc""void a() {}");
459
460  std::unique_ptr<FrontendActionFactory> Action(
461      newFrontendActionFactory<SyntaxOnlyAction>());
462
463  CommandLineArguments FinalArgs;
464  ArgumentsAdjuster CheckFlagsAdjuster =
465      [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
466        FinalArgs = Args;
467        return Args;
468      };
469  Tool.clearArgumentsAdjusters();
470  Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
471  Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
472  Tool.run(Action.get());
473
474  auto HasFlag = [&FinalArgs](const std::string &Flag) {
475    return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) !=
476           FinalArgs.end();
477  };
478  EXPECT_FALSE(HasFlag("-Xclang"));
479  EXPECT_FALSE(HasFlag("-add-plugin"));
480  EXPECT_FALSE(HasFlag("-random-plugin"));
481}
482
483namespace {
484/// Find a target name such that looking for it in TargetRegistry by that name
485/// returns the same target. We expect that there is at least one target
486/// configured with this property.
487std::string getAnyTarget() {
488  llvm::InitializeAllTargets();
489  for (const auto &Target : llvm::TargetRegistry::targets()) {
490    std::string Error;
491    StringRef TargetName(Target.getName());
492    if (TargetName == "x86-64")
493      TargetName = "x86_64";
494    if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) {
495      return TargetName;
496    }
497  }
498  return "";
499}
500}
501
502TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
503  std::string Target = getAnyTarget();
504  ASSERT_FALSE(Target.empty());
505
506  std::vector<std::string> Args = {"clang""-foo"};
507  addTargetAndModeForProgramName(Args, "");
508  EXPECT_EQ((std::vector<std::string>{"clang""-foo"}), Args);
509  addTargetAndModeForProgramName(Args, Target + "-g++");
510  EXPECT_EQ((std::vector<std::string>{"clang""-target", Target,
511                                      "--driver-mode=g++""-foo"}),
512            Args);
513}
514
515TEST(addTargetAndModeForProgramName, PathIgnored) {
516  std::string Target = getAnyTarget();
517  ASSERT_FALSE(Target.empty());
518
519  SmallString<32> ToolPath;
520  llvm::sys::path::append(ToolPath, "foo""bar", Target + "-g++");
521
522  std::vector<std::string> Args = {"clang""-foo"};
523  addTargetAndModeForProgramName(Args, ToolPath);
524  EXPECT_EQ((std::vector<std::string>{"clang""-target", Target,
525                                      "--driver-mode=g++""-foo"}),
526            Args);
527}
528
529TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
530  std::string Target = getAnyTarget();
531  ASSERT_FALSE(Target.empty());
532
533  std::vector<std::string> Args = {"clang""-foo""-target""something"};
534  addTargetAndModeForProgramName(Args, Target + "-g++");
535  EXPECT_EQ((std::vector<std::string>{"clang""--driver-mode=g++""-foo",
536                                      "-target""something"}),
537            Args);
538
539  std::vector<std::string> ArgsAlt = {"clang""-foo""-target=something"};
540  addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
541  EXPECT_EQ((std::vector<std::string>{"clang""--driver-mode=g++""-foo",
542                                      "-target=something"}),
543            ArgsAlt);
544}
545
546TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
547  std::string Target = getAnyTarget();
548  ASSERT_FALSE(Target.empty());
549
550  std::vector<std::string> Args = {"clang""-foo""--driver-mode=abc"};
551  addTargetAndModeForProgramName(Args, Target + "-g++");
552  EXPECT_EQ((std::vector<std::string>{"clang""-target", Target, "-foo",
553                                      "--driver-mode=abc"}),
554            Args);
555
556  std::vector<std::string> ArgsAlt = {"clang""-foo""--driver-mode""abc"};
557  addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
558  EXPECT_EQ((std::vector<std::string>{"clang""-target", Target, "-foo",
559                                      "--driver-mode""abc"}),
560            ArgsAlt);
561}
562
563#ifndef _WIN32
564TEST(ClangToolTest, BuildASTs) {
565  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
566
567  std::vector<std::string> Sources;
568  Sources.push_back("/a.cc");
569  Sources.push_back("/b.cc");
570  ClangTool Tool(Compilations, Sources);
571
572  Tool.mapVirtualFile("/a.cc""void a() {}");
573  Tool.mapVirtualFile("/b.cc""void b() {}");
574
575  std::vector<std::unique_ptr<ASTUnit>> ASTs;
576  EXPECT_EQ(0, Tool.buildASTs(ASTs));
577  EXPECT_EQ(2u, ASTs.size());
578}
579
580struct TestDiagnosticConsumer : public DiagnosticConsumer {
581  TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
582  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
583                        const Diagnostic &Info) override {
584    ++NumDiagnosticsSeen;
585  }
586  unsigned NumDiagnosticsSeen;
587};
588
589TEST(ClangToolTest, InjectDiagnosticConsumer) {
590  FixedCompilationDatabase Compilations("/"std::vector<std::string>());
591  ClangTool Tool(Compilationsstd::vector<std::string>(1"/a.cc"));
592  Tool.mapVirtualFile("/a.cc""int x = undeclared;");
593  TestDiagnosticConsumer Consumer;
594  Tool.setDiagnosticConsumer(&Consumer);
595  std::unique_ptr<FrontendActionFactoryAction(
596      newFrontendActionFactory<SyntaxOnlyAction>());
597  Tool.run(Action.get());
598  EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
599}
600
601TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
602  FixedCompilationDatabase Compilations("/"std::vector<std::string>());
603  ClangTool Tool(Compilationsstd::vector<std::string>(1"/a.cc"));
604  Tool.mapVirtualFile("/a.cc""int x = undeclared;");
605  TestDiagnosticConsumer Consumer;
606  Tool.setDiagnosticConsumer(&Consumer);
607  std::vector<std::unique_ptr<ASTUnit>> ASTs;
608  Tool.buildASTs(ASTs);
609  EXPECT_EQ(1uASTs.size());
610  EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
611}
612#endif
613
614TEST(runToolOnCode, TestResetDiagnostics) {
615  // This is a tool that resets the diagnostic during the compilation.
616  struct ResetDiagnosticAction : public clang::ASTFrontendAction {
617    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
618                                                   StringRef) override {
619      struct Consumer : public clang::ASTConsumer {
620        bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
621          auto &Diags = (*D.begin())->getASTContext().getDiagnostics();
622          // Ignore any error
623          Diags.Reset();
624          // Disable warnings because computing the CFG might crash.
625          Diags.setIgnoreAllWarnings(true);
626          return true;
627        }
628      };
629      return llvm::make_unique<Consumer>();
630    }
631  };
632
633  // Should not crash
634  EXPECT_FALSE(
635      runToolOnCode(new ResetDiagnosticAction,
636                    "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };"
637                    "void func() { long x; Foo f(x); }"));
638}
639
640// end namespace tooling
641// end namespace clang
642