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