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