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 DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer {
225   bool SawSourceManager;
226 
227   DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {}
228 
229   void HandleDiagnostic(clang::DiagnosticsEngine::Level,
230                         const clang::Diagnostic &info) override {
231     SawSourceManager = info.hasSourceManager();
232   }
233 };
234 
235 TEST(ToolInvocation, DiagConsumerExpectingSourceManager) {
236   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
237       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
238   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
239       new llvm::vfs::InMemoryFileSystem);
240   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
241   llvm::IntrusiveRefCntPtr<FileManager> Files(
242       new FileManager(FileSystemOptions(), OverlayFileSystem));
243   std::vector<std::string> Args;
244   Args.push_back("tool-executable");
245   // Note: intentional error; user probably meant -ferror-limit=0.
246   Args.push_back("-ferror-limit=-1");
247   Args.push_back("-fsyntax-only");
248   Args.push_back("test.cpp");
249   clang::tooling::ToolInvocation Invocation(
250       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
251   InMemoryFileSystem->addFile(
252       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}\n"));
253 
254   DiagnosticConsumerExpectingSourceManager Consumer;
255   Invocation.setDiagnosticConsumer(&Consumer);
256 
257   EXPECT_TRUE(Invocation.run());
258   EXPECT_TRUE(Consumer.SawSourceManager);
259 }
260 
261 struct VerifyEndCallback : public SourceFileCallbacks {
262   VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
263   bool handleBeginSource(CompilerInstance &CI) override {
264     ++BeginCalled;
265     return true;
266   }
267   void handleEndSource() override { ++EndCalled; }
268   std::unique_ptr<ASTConsumer> newASTConsumer() {
269     return std::make_unique<FindTopLevelDeclConsumer>(&Matched);
270   }
271   unsigned BeginCalled;
272   unsigned EndCalled;
273   bool Matched;
274 };
275 
276 #if !defined(_WIN32)
277 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
278   VerifyEndCallback EndCallback;
279 
280   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
281   std::vector<std::string> Sources;
282   Sources.push_back("/a.cc");
283   Sources.push_back("/b.cc");
284   ClangTool Tool(Compilations, Sources);
285 
286   Tool.mapVirtualFile("/a.cc", "void a() {}");
287   Tool.mapVirtualFile("/b.cc", "void b() {}");
288 
289   std::unique_ptr<FrontendActionFactory> Action(
290       newFrontendActionFactory(&EndCallback, &EndCallback));
291   Tool.run(Action.get());
292 
293   EXPECT_TRUE(EndCallback.Matched);
294   EXPECT_EQ(2u, EndCallback.BeginCalled);
295   EXPECT_EQ(2u, EndCallback.EndCalled);
296 }
297 #endif
298 
299 struct SkipBodyConsumer : public clang::ASTConsumer {
300   /// Skip the 'skipMe' function.
301   bool shouldSkipFunctionBody(Decl *D) override {
302     NamedDecl *F = dyn_cast<NamedDecl>(D);
303     return F && F->getNameAsString() == "skipMe";
304   }
305 };
306 
307 struct SkipBodyAction : public clang::ASTFrontendAction {
308   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
309                                                  StringRef) override {
310     Compiler.getFrontendOpts().SkipFunctionBodies = true;
311     return std::make_unique<SkipBodyConsumer>();
312   }
313 };
314 
315 TEST(runToolOnCode, TestSkipFunctionBody) {
316   std::vector<std::string> Args = {"-std=c++11"};
317   std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
318 
319   EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(),
320                             "int skipMe() { an_error_here }"));
321   EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(),
322                              "int skipMeNot() { an_error_here }"));
323 
324   // Test constructors with initializers
325   EXPECT_TRUE(runToolOnCodeWithArgs(
326       std::make_unique<SkipBodyAction>(),
327       "struct skipMe { skipMe() : an_error() { more error } };", Args));
328   EXPECT_TRUE(runToolOnCodeWithArgs(
329       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
330                           "skipMe::skipMe() : an_error([](){;}) { more error }",
331       Args));
332   EXPECT_TRUE(runToolOnCodeWithArgs(
333       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
334                           "skipMe::skipMe() : an_error{[](){;}} { more error }",
335       Args));
336   EXPECT_TRUE(runToolOnCodeWithArgs(
337       std::make_unique<SkipBodyAction>(),
338       "struct skipMe { skipMe(); };"
339       "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
340       Args));
341   EXPECT_TRUE(runToolOnCodeWithArgs(
342       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };",
343       Args));
344 
345   EXPECT_FALSE(runToolOnCodeWithArgs(
346       std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };",
347       Args));
348   EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(),
349                                      "struct skipMeNot { skipMeNot(); };"
350                                      "skipMeNot::skipMeNot() : an_error() { }",
351                                      Args));
352 
353   // Try/catch
354   EXPECT_TRUE(runToolOnCode(
355       std::make_unique<SkipBodyAction>(),
356       "void skipMe() try { an_error() } catch(error) { error };"));
357   EXPECT_TRUE(runToolOnCode(
358       std::make_unique<SkipBodyAction>(),
359       "struct S { void skipMe() try { an_error() } catch(error) { error } };"));
360   EXPECT_TRUE(
361       runToolOnCode(std::make_unique<SkipBodyAction>(),
362                     "void skipMe() try { an_error() } catch(error) { error; }"
363                     "catch(error) { error } catch (error) { }"));
364   EXPECT_FALSE(runToolOnCode(
365       std::make_unique<SkipBodyAction>(),
366       "void skipMe() try something;")); // don't crash while parsing
367 
368   // Template
369   EXPECT_TRUE(runToolOnCode(
370       std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }"
371                           "int x = skipMe<int>();"));
372   EXPECT_FALSE(runToolOnCodeWithArgs(
373       std::make_unique<SkipBodyAction>(),
374       "template<typename T> int skipMeNot() { an_error_here }", Args2));
375 }
376 
377 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
378   llvm::SmallString<32> DepFilePath;
379   ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d",
380                                                                DepFilePath));
381   std::vector<std::string> Args;
382   Args.push_back("-MMD");
383   Args.push_back("-MT");
384   Args.push_back(std::string(DepFilePath.str()));
385   Args.push_back("-MF");
386   Args.push_back(std::string(DepFilePath.str()));
387   EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args));
388   EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
389   EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
390 }
391 
392 struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction {
393   CheckColoredDiagnosticsAction(bool ShouldShowColor)
394       : ShouldShowColor(ShouldShowColor) {}
395   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
396                                                  StringRef) override {
397     if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor)
398       Compiler.getDiagnostics().Report(
399           Compiler.getDiagnostics().getCustomDiagID(
400               DiagnosticsEngine::Fatal,
401               "getDiagnosticOpts().ShowColors != ShouldShowColor"));
402     return std::make_unique<ASTConsumer>();
403   }
404 
405 private:
406   bool ShouldShowColor = true;
407 };
408 
409 TEST(runToolOnCodeWithArgs, DiagnosticsColor) {
410   EXPECT_TRUE(runToolOnCodeWithArgs(
411       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
412       {"-fcolor-diagnostics"}));
413   EXPECT_TRUE(runToolOnCodeWithArgs(
414       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
415       {"-fno-color-diagnostics"}));
416   EXPECT_TRUE(runToolOnCodeWithArgs(
417       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
418       {"-fno-color-diagnostics", "-fcolor-diagnostics"}));
419   EXPECT_TRUE(runToolOnCodeWithArgs(
420       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
421       {"-fcolor-diagnostics", "-fno-color-diagnostics"}));
422   EXPECT_TRUE(runToolOnCodeWithArgs(
423       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
424       {"-fno-color-diagnostics", "-fdiagnostics-color=always"}));
425 
426   // Check that this test would fail if ShowColors is not what it should.
427   EXPECT_FALSE(runToolOnCodeWithArgs(
428       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
429       {"-fcolor-diagnostics"}));
430 }
431 
432 TEST(ClangToolTest, ArgumentAdjusters) {
433   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
434 
435   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
436   Tool.mapVirtualFile("/a.cc", "void a() {}");
437 
438   std::unique_ptr<FrontendActionFactory> Action(
439       newFrontendActionFactory<SyntaxOnlyAction>());
440 
441   bool Found = false;
442   bool Ran = false;
443   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
444       [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
445     Ran = true;
446     if (llvm::is_contained(Args, "-fsyntax-only"))
447       Found = true;
448     return Args;
449   };
450   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
451   Tool.run(Action.get());
452   EXPECT_TRUE(Ran);
453   EXPECT_TRUE(Found);
454 
455   Ran = Found = false;
456   Tool.clearArgumentsAdjusters();
457   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
458   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
459   Tool.run(Action.get());
460   EXPECT_TRUE(Ran);
461   EXPECT_FALSE(Found);
462 }
463 
464 TEST(ClangToolTest, NoDoubleSyntaxOnly) {
465   FixedCompilationDatabase Compilations("/", {"-fsyntax-only"});
466 
467   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
468   Tool.mapVirtualFile("/a.cc", "void a() {}");
469 
470   std::unique_ptr<FrontendActionFactory> Action(
471       newFrontendActionFactory<SyntaxOnlyAction>());
472 
473   size_t SyntaxOnlyCount = 0;
474   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
475       [&SyntaxOnlyCount](const CommandLineArguments &Args,
476                          StringRef /*unused*/) {
477         for (llvm::StringRef Arg : Args) {
478           if (Arg == "-fsyntax-only")
479             ++SyntaxOnlyCount;
480         }
481         return Args;
482       };
483 
484   Tool.clearArgumentsAdjusters();
485   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
486   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
487   Tool.run(Action.get());
488   EXPECT_EQ(SyntaxOnlyCount, 1U);
489 }
490 
491 TEST(ClangToolTest, NoOutputCommands) {
492   FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd",
493                                               "--save-temps",
494                                               "--save-temps=somedir"});
495 
496   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
497   Tool.mapVirtualFile("/a.cc", "void a() {}");
498 
499   std::unique_ptr<FrontendActionFactory> Action(
500       newFrontendActionFactory<SyntaxOnlyAction>());
501 
502   const std::vector<llvm::StringRef> OutputCommands = {"-save-temps"};
503   bool Ran = false;
504   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
505       [&OutputCommands, &Ran](const CommandLineArguments &Args,
506                               StringRef /*unused*/) {
507         for (llvm::StringRef Arg : Args) {
508           for (llvm::StringRef OutputCommand : OutputCommands)
509             EXPECT_FALSE(Arg.contains(OutputCommand));
510         }
511         Ran = true;
512         return Args;
513       };
514 
515   Tool.clearArgumentsAdjusters();
516   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
517   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
518   Tool.run(Action.get());
519   EXPECT_TRUE(Ran);
520 }
521 
522 TEST(ClangToolTest, BaseVirtualFileSystemUsage) {
523   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
524   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
525       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
526   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
527       new llvm::vfs::InMemoryFileSystem);
528   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
529 
530   InMemoryFileSystem->addFile(
531       "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}"));
532 
533   ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"),
534                  std::make_shared<PCHContainerOperations>(), OverlayFileSystem);
535   std::unique_ptr<FrontendActionFactory> Action(
536       newFrontendActionFactory<SyntaxOnlyAction>());
537   EXPECT_EQ(0, Tool.run(Action.get()));
538 }
539 
540 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD.
541 TEST(ClangToolTest, StripDependencyFileAdjuster) {
542   FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"});
543 
544   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
545   Tool.mapVirtualFile("/a.cc", "void a() {}");
546 
547   std::unique_ptr<FrontendActionFactory> Action(
548       newFrontendActionFactory<SyntaxOnlyAction>());
549 
550   CommandLineArguments FinalArgs;
551   ArgumentsAdjuster CheckFlagsAdjuster =
552     [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
553       FinalArgs = Args;
554       return Args;
555     };
556   Tool.clearArgumentsAdjusters();
557   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
558   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
559   Tool.run(Action.get());
560 
561   auto HasFlag = [&FinalArgs](const std::string &Flag) {
562     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
563   };
564   EXPECT_FALSE(HasFlag("-MD"));
565   EXPECT_FALSE(HasFlag("-MMD"));
566   EXPECT_TRUE(HasFlag("-c"));
567   EXPECT_TRUE(HasFlag("-w"));
568 }
569 
570 // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants
571 TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) {
572   FixedCompilationDatabase Compilations(
573       "/", {"/showIncludes", "/showIncludes:user", "-showIncludes",
574             "-showIncludes:user", "-c"});
575 
576   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
577   Tool.mapVirtualFile("/a.cc", "void a() {}");
578 
579   std::unique_ptr<FrontendActionFactory> Action(
580       newFrontendActionFactory<SyntaxOnlyAction>());
581 
582   CommandLineArguments FinalArgs;
583   ArgumentsAdjuster CheckFlagsAdjuster =
584       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
585         FinalArgs = Args;
586         return Args;
587       };
588   Tool.clearArgumentsAdjusters();
589   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
590   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
591   Tool.run(Action.get());
592 
593   auto HasFlag = [&FinalArgs](const std::string &Flag) {
594     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
595   };
596   EXPECT_FALSE(HasFlag("/showIncludes"));
597   EXPECT_FALSE(HasFlag("/showIncludes:user"));
598   EXPECT_FALSE(HasFlag("-showIncludes"));
599   EXPECT_FALSE(HasFlag("-showIncludes:user"));
600   EXPECT_TRUE(HasFlag("-c"));
601 }
602 
603 // Check getClangStripDependencyFileAdjuster doesn't strip args when using the
604 // MSVC cl.exe driver
605 TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) {
606   FixedCompilationDatabase Compilations(
607       "/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"});
608 
609   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
610   Tool.mapVirtualFile("/a.cc", "void a() {}");
611 
612   std::unique_ptr<FrontendActionFactory> Action(
613       newFrontendActionFactory<SyntaxOnlyAction>());
614 
615   CommandLineArguments FinalArgs;
616   ArgumentsAdjuster CheckFlagsAdjuster =
617       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
618         FinalArgs = Args;
619         return Args;
620       };
621   Tool.clearArgumentsAdjusters();
622   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
623   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
624   Tool.run(Action.get());
625 
626   auto HasFlag = [&FinalArgs](const std::string &Flag) {
627     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
628   };
629   EXPECT_TRUE(HasFlag("-MD"));
630   EXPECT_TRUE(HasFlag("-MDd"));
631   EXPECT_TRUE(HasFlag("-MT"));
632   EXPECT_TRUE(HasFlag("-O1"));
633   EXPECT_TRUE(HasFlag("-MTd"));
634   EXPECT_TRUE(HasFlag("-MP"));
635 }
636 
637 // Check getClangStripPluginsAdjuster strips plugin related args.
638 TEST(ClangToolTest, StripPluginsAdjuster) {
639   FixedCompilationDatabase Compilations(
640       "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"});
641 
642   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
643   Tool.mapVirtualFile("/a.cc", "void a() {}");
644 
645   std::unique_ptr<FrontendActionFactory> Action(
646       newFrontendActionFactory<SyntaxOnlyAction>());
647 
648   CommandLineArguments FinalArgs;
649   ArgumentsAdjuster CheckFlagsAdjuster =
650       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
651         FinalArgs = Args;
652         return Args;
653       };
654   Tool.clearArgumentsAdjusters();
655   Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
656   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
657   Tool.run(Action.get());
658 
659   auto HasFlag = [&FinalArgs](const std::string &Flag) {
660     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
661   };
662   EXPECT_FALSE(HasFlag("-Xclang"));
663   EXPECT_FALSE(HasFlag("-add-plugin"));
664   EXPECT_FALSE(HasFlag("-random-plugin"));
665 }
666 
667 namespace {
668 /// Find a target name such that looking for it in TargetRegistry by that name
669 /// returns the same target. We expect that there is at least one target
670 /// configured with this property.
671 std::string getAnyTarget() {
672   llvm::InitializeAllTargets();
673   for (const auto &Target : llvm::TargetRegistry::targets()) {
674     std::string Error;
675     StringRef TargetName(Target.getName());
676     if (TargetName == "x86-64")
677       TargetName = "x86_64";
678     if (llvm::TargetRegistry::lookupTarget(std::string(TargetName), Error) ==
679         &Target) {
680       return std::string(TargetName);
681     }
682   }
683   return "";
684 }
685 }
686 
687 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
688   std::string Target = getAnyTarget();
689   ASSERT_FALSE(Target.empty());
690 
691   std::vector<std::string> Args = {"clang", "-foo"};
692   addTargetAndModeForProgramName(Args, "");
693   EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
694   addTargetAndModeForProgramName(Args, Target + "-g++");
695   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
696                                       "--driver-mode=g++", "-foo"}),
697             Args);
698 }
699 
700 TEST(addTargetAndModeForProgramName, PathIgnored) {
701   std::string Target = getAnyTarget();
702   ASSERT_FALSE(Target.empty());
703 
704   SmallString<32> ToolPath;
705   llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
706 
707   std::vector<std::string> Args = {"clang", "-foo"};
708   addTargetAndModeForProgramName(Args, ToolPath);
709   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
710                                       "--driver-mode=g++", "-foo"}),
711             Args);
712 }
713 
714 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
715   std::string Target = getAnyTarget();
716   ASSERT_FALSE(Target.empty());
717 
718   std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
719   addTargetAndModeForProgramName(Args, Target + "-g++");
720   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
721                                       "-target", "something"}),
722             Args);
723 
724   std::vector<std::string> ArgsAlt = {"clang", "-foo", "--target=something"};
725   addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
726   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
727                                       "--target=something"}),
728             ArgsAlt);
729 }
730 
731 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
732   std::string Target = getAnyTarget();
733   ASSERT_FALSE(Target.empty());
734 
735   std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
736   addTargetAndModeForProgramName(Args, Target + "-g++");
737   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo",
738                                       "--driver-mode=abc"}),
739             Args);
740 }
741 
742 #ifndef _WIN32
743 TEST(ClangToolTest, BuildASTs) {
744   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
745 
746   std::vector<std::string> Sources;
747   Sources.push_back("/a.cc");
748   Sources.push_back("/b.cc");
749   ClangTool Tool(Compilations, Sources);
750 
751   Tool.mapVirtualFile("/a.cc", "void a() {}");
752   Tool.mapVirtualFile("/b.cc", "void b() {}");
753 
754   std::vector<std::unique_ptr<ASTUnit>> ASTs;
755   EXPECT_EQ(0, Tool.buildASTs(ASTs));
756   EXPECT_EQ(2u, ASTs.size());
757 }
758 
759 TEST(ClangToolTest, InjectDiagnosticConsumer) {
760   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
761   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
762   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
763   TestDiagnosticConsumer Consumer;
764   Tool.setDiagnosticConsumer(&Consumer);
765   std::unique_ptr<FrontendActionFactory> Action(
766       newFrontendActionFactory<SyntaxOnlyAction>());
767   Tool.run(Action.get());
768   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
769 }
770 
771 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
772   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
773   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
774   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
775   TestDiagnosticConsumer Consumer;
776   Tool.setDiagnosticConsumer(&Consumer);
777   std::vector<std::unique_ptr<ASTUnit>> ASTs;
778   Tool.buildASTs(ASTs);
779   EXPECT_EQ(1u, ASTs.size());
780   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
781 }
782 #endif
783 
784 TEST(runToolOnCode, TestResetDiagnostics) {
785   // This is a tool that resets the diagnostic during the compilation.
786   struct ResetDiagnosticAction : public clang::ASTFrontendAction {
787     std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
788                                                    StringRef) override {
789       struct Consumer : public clang::ASTConsumer {
790         bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
791           auto &Diags = (*D.begin())->getASTContext().getDiagnostics();
792           // Ignore any error
793           Diags.Reset();
794           // Disable warnings because computing the CFG might crash.
795           Diags.setIgnoreAllWarnings(true);
796           return true;
797         }
798       };
799       return std::make_unique<Consumer>();
800     }
801   };
802 
803   // Should not crash
804   EXPECT_FALSE(
805       runToolOnCode(std::make_unique<ResetDiagnosticAction>(),
806                     "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };"
807                     "void func() { long x; Foo f(x); }"));
808 }
809 
810 } // end namespace tooling
811 } // end namespace clang
812