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