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/Driver/Compilation.h"
13 #include "clang/Driver/Driver.h"
14 #include "clang/Frontend/ASTUnit.h"
15 #include "clang/Frontend/CompilerInstance.h"
16 #include "clang/Frontend/FrontendAction.h"
17 #include "clang/Frontend/FrontendActions.h"
18 #include "clang/Tooling/ArgumentsAdjusters.h"
19 #include "clang/Tooling/CompilationDatabase.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/Support/Host.h"
24 #include "llvm/Support/Path.h"
25 #include "llvm/Support/TargetRegistry.h"
26 #include "llvm/Support/TargetSelect.h"
27 #include "gtest/gtest.h"
28 #include <algorithm>
29 #include <string>
30 #include <vector>
31 
32 namespace clang {
33 namespace tooling {
34 
35 namespace {
36 /// Takes an ast consumer and returns it from CreateASTConsumer. This only
37 /// works with single translation unit compilations.
38 class TestAction : public clang::ASTFrontendAction {
39 public:
40   /// Takes ownership of TestConsumer.
41   explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
42       : TestConsumer(std::move(TestConsumer)) {}
43 
44 protected:
45   std::unique_ptr<clang::ASTConsumer>
46   CreateASTConsumer(clang::CompilerInstance &compiler,
47                     StringRef dummy) override {
48     /// TestConsumer will be deleted by the framework calling us.
49     return std::move(TestConsumer);
50   }
51 
52 private:
53   std::unique_ptr<clang::ASTConsumer> TestConsumer;
54 };
55 
56 class FindTopLevelDeclConsumer : public clang::ASTConsumer {
57  public:
58   explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
59       : FoundTopLevelDecl(FoundTopLevelDecl) {}
60   bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
61     *FoundTopLevelDecl = true;
62     return true;
63   }
64  private:
65   bool * const FoundTopLevelDecl;
66 };
67 } // end namespace
68 
69 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
70   bool FoundTopLevelDecl = false;
71   EXPECT_TRUE(runToolOnCode(
72       std::make_unique<TestAction>(
73           std::make_unique<FindTopLevelDeclConsumer>(&FoundTopLevelDecl)),
74       ""));
75   EXPECT_FALSE(FoundTopLevelDecl);
76 }
77 
78 namespace {
79 class FindClassDeclXConsumer : public clang::ASTConsumer {
80  public:
81   FindClassDeclXConsumer(bool *FoundClassDeclX)
82       : FoundClassDeclX(FoundClassDeclX) {}
83   bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
84     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
85             *GroupRef.begin())) {
86       if (Record->getName() == "X") {
87         *FoundClassDeclX = true;
88       }
89     }
90     return true;
91   }
92  private:
93   bool *FoundClassDeclX;
94 };
95 bool FindClassDeclX(ASTUnit *AST) {
96   for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
97                                      e = AST->top_level_end();
98        i != e; ++i) {
99     if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
100       if (Record->getName() == "X") {
101         return true;
102       }
103     }
104   }
105   return false;
106 }
107 
108 struct TestDiagnosticConsumer : public DiagnosticConsumer {
109   TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
110   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
111                         const Diagnostic &Info) override {
112     ++NumDiagnosticsSeen;
113   }
114   unsigned NumDiagnosticsSeen;
115 };
116 } // end namespace
117 
118 TEST(runToolOnCode, FindsClassDecl) {
119   bool FoundClassDeclX = false;
120   EXPECT_TRUE(runToolOnCode(
121       std::make_unique<TestAction>(
122           std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)),
123       "class X;"));
124   EXPECT_TRUE(FoundClassDeclX);
125 
126   FoundClassDeclX = false;
127   EXPECT_TRUE(runToolOnCode(
128       std::make_unique<TestAction>(
129           std::make_unique<FindClassDeclXConsumer>(&FoundClassDeclX)),
130       "class Y;"));
131   EXPECT_FALSE(FoundClassDeclX);
132 }
133 
134 TEST(buildASTFromCode, FindsClassDecl) {
135   std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
136   ASSERT_TRUE(AST.get());
137   EXPECT_TRUE(FindClassDeclX(AST.get()));
138 
139   AST = buildASTFromCode("class Y;");
140   ASSERT_TRUE(AST.get());
141   EXPECT_FALSE(FindClassDeclX(AST.get()));
142 }
143 
144 TEST(buildASTFromCode, ReportsErrors) {
145   TestDiagnosticConsumer Consumer;
146   std::unique_ptr<ASTUnit> AST = buildASTFromCodeWithArgs(
147       "int x = \"A\";", {}, "input.cc", "clang-tool",
148       std::make_shared<PCHContainerOperations>(),
149       getClangStripDependencyFileAdjuster(), FileContentMappings(), &Consumer);
150   EXPECT_TRUE(AST.get());
151   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
152 }
153 
154 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
155   std::unique_ptr<FrontendActionFactory> Factory(
156       newFrontendActionFactory<SyntaxOnlyAction>());
157   std::unique_ptr<FrontendAction> Action(Factory->create());
158   EXPECT_TRUE(Action.get() != nullptr);
159 }
160 
161 struct IndependentFrontendActionCreator {
162   std::unique_ptr<ASTConsumer> newASTConsumer() {
163     return std::make_unique<FindTopLevelDeclConsumer>(nullptr);
164   }
165 };
166 
167 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
168   IndependentFrontendActionCreator Creator;
169   std::unique_ptr<FrontendActionFactory> Factory(
170       newFrontendActionFactory(&Creator));
171   std::unique_ptr<FrontendAction> Action(Factory->create());
172   EXPECT_TRUE(Action.get() != nullptr);
173 }
174 
175 TEST(ToolInvocation, TestMapVirtualFile) {
176   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
177       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
178   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
179       new llvm::vfs::InMemoryFileSystem);
180   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
181   llvm::IntrusiveRefCntPtr<FileManager> Files(
182       new FileManager(FileSystemOptions(), OverlayFileSystem));
183   std::vector<std::string> Args;
184   Args.push_back("tool-executable");
185   Args.push_back("-Idef");
186   Args.push_back("-fsyntax-only");
187   Args.push_back("test.cpp");
188   clang::tooling::ToolInvocation Invocation(
189       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
190   InMemoryFileSystem->addFile(
191       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
192   InMemoryFileSystem->addFile("def/abc", 0,
193                               llvm::MemoryBuffer::getMemBuffer("\n"));
194   EXPECT_TRUE(Invocation.run());
195 }
196 
197 TEST(ToolInvocation, TestVirtualModulesCompilation) {
198   // FIXME: Currently, this only tests that we don't exit with an error if a
199   // mapped module.map is found on the include path. In the future, expand this
200   // test to run a full modules enabled compilation, so we make sure we can
201   // rerun modules compilations with a virtual file system.
202   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
203       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
204   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
205       new llvm::vfs::InMemoryFileSystem);
206   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
207   llvm::IntrusiveRefCntPtr<FileManager> Files(
208       new FileManager(FileSystemOptions(), OverlayFileSystem));
209   std::vector<std::string> Args;
210   Args.push_back("tool-executable");
211   Args.push_back("-Idef");
212   Args.push_back("-fsyntax-only");
213   Args.push_back("test.cpp");
214   clang::tooling::ToolInvocation Invocation(
215       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
216   InMemoryFileSystem->addFile(
217       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
218   InMemoryFileSystem->addFile("def/abc", 0,
219                               llvm::MemoryBuffer::getMemBuffer("\n"));
220   // Add a module.map file in the include directory of our header, so we trigger
221   // the module.map header search logic.
222   InMemoryFileSystem->addFile("def/module.map", 0,
223                               llvm::MemoryBuffer::getMemBuffer("\n"));
224   EXPECT_TRUE(Invocation.run());
225 }
226 
227 struct DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer {
228   bool SawSourceManager;
229 
230   DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {}
231 
232   void HandleDiagnostic(clang::DiagnosticsEngine::Level,
233                         const clang::Diagnostic &info) override {
234     SawSourceManager = info.hasSourceManager();
235   }
236 };
237 
238 TEST(ToolInvocation, DiagConsumerExpectingSourceManager) {
239   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
240       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
241   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
242       new llvm::vfs::InMemoryFileSystem);
243   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
244   llvm::IntrusiveRefCntPtr<FileManager> Files(
245       new FileManager(FileSystemOptions(), OverlayFileSystem));
246   std::vector<std::string> Args;
247   Args.push_back("tool-executable");
248   // Note: intentional error; user probably meant -ferror-limit=0.
249   Args.push_back("-ferror-limit=-1");
250   Args.push_back("-fsyntax-only");
251   Args.push_back("test.cpp");
252   clang::tooling::ToolInvocation Invocation(
253       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
254   InMemoryFileSystem->addFile(
255       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}\n"));
256 
257   DiagnosticConsumerExpectingSourceManager Consumer;
258   Invocation.setDiagnosticConsumer(&Consumer);
259 
260   EXPECT_TRUE(Invocation.run());
261   EXPECT_TRUE(Consumer.SawSourceManager);
262 }
263 
264 namespace {
265 /// Overlays the real filesystem with the given VFS and returns the result.
266 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
267 overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
268   auto RFS = llvm::vfs::getRealFileSystem();
269   auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(RFS);
270   OverlayFS->pushOverlay(VFS);
271   return OverlayFS;
272 }
273 
274 struct CommandLineExtractorTest : public ::testing::Test {
275   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS;
276   llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
277   driver::Driver Driver;
278 
279 public:
280   CommandLineExtractorTest()
281       : InMemoryFS(new llvm::vfs::InMemoryFileSystem),
282         Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)),
283         Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags,
284                "clang LLVM compiler", overlayRealFS(InMemoryFS)) {}
285 
286   void addFile(StringRef Name, StringRef Content) {
287     InMemoryFS->addFile(Name, 0, llvm::MemoryBuffer::getMemBuffer(Content));
288   }
289 
290   const llvm::opt::ArgStringList *
291   extractCC1Arguments(llvm::ArrayRef<const char *> Argv) {
292     const std::unique_ptr<driver::Compilation> Compilation(
293         Driver.BuildCompilation(llvm::makeArrayRef(Argv)));
294 
295     return getCC1Arguments(Diags.get(), Compilation.get());
296   }
297 };
298 } // namespace
299 
300 TEST_F(CommandLineExtractorTest, AcceptOffloading) {
301   addFile("test.c", "int main() {}\n");
302   const char *Args[] = {"clang",     "-target",  "arm64-apple-macosx11.0.0",
303                         "-x",        "hip",      "test.c",
304                         "-nogpulib", "-nogpuinc"};
305   EXPECT_NE(extractCC1Arguments(Args), nullptr);
306 }
307 
308 TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) {
309   addFile("test.c", "int main() {}\n");
310   const char *Args[] = {"clang",  "-target",   "arm64-apple-macosx11.0.0",
311                         "-c",     "-x",        "hip",
312                         "test.c", "-nogpulib", "-nogpuinc"};
313   EXPECT_NE(extractCC1Arguments(Args), nullptr);
314 }
315 
316 TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) {
317   addFile("test.c", "int main() {}\n");
318   const char *Args[] = {
319       "clang",         "-target",   "arm64-apple-macosx11.0.0",
320       "-fsyntax-only", "-x",        "hip",
321       "test.c",        "-nogpulib", "-nogpuinc"};
322   EXPECT_NE(extractCC1Arguments(Args), nullptr);
323 }
324 
325 TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) {
326   addFile("test.c", "int main() {}\n");
327   const char *Args[] = {
328       "clang", "-target", "arm64-apple-macosx11.0.0", "-fno-integrated-as",
329       "-c",    "test.c"};
330   EXPECT_NE(extractCC1Arguments(Args), nullptr);
331 }
332 
333 TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) {
334   addFile("test.c", "int main() {}\n");
335   const char *Args[] = {"clang", "-target",         "arm64-apple-macosx11.0.0",
336                         "-c",    "-fembed-bitcode", "test.c"};
337   EXPECT_NE(extractCC1Arguments(Args), nullptr);
338 }
339 
340 TEST_F(CommandLineExtractorTest, AcceptSaveTemps) {
341   addFile("test.c", "int main() {}\n");
342   const char *Args[] = {"clang", "-target",     "arm64-apple-macosx11.0.0",
343                         "-c",    "-save-temps", "test.c"};
344   EXPECT_NE(extractCC1Arguments(Args), nullptr);
345 }
346 
347 TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) {
348   addFile("test.c", "int main() {}\n");
349   const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
350                         "-arch", "x86_64",  "-arch",
351                         "arm64", "-c",      "test.c"};
352   EXPECT_EQ(extractCC1Arguments(Args), nullptr);
353 }
354 
355 TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) {
356   addFile("one.c", "void one() {}\n");
357   addFile("two.c", "void two() {}\n");
358   const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
359                         "-c",    "one.c",   "two.c"};
360   EXPECT_EQ(extractCC1Arguments(Args), nullptr);
361 }
362 
363 struct VerifyEndCallback : public SourceFileCallbacks {
364   VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
365   bool handleBeginSource(CompilerInstance &CI) override {
366     ++BeginCalled;
367     return true;
368   }
369   void handleEndSource() override { ++EndCalled; }
370   std::unique_ptr<ASTConsumer> newASTConsumer() {
371     return std::make_unique<FindTopLevelDeclConsumer>(&Matched);
372   }
373   unsigned BeginCalled;
374   unsigned EndCalled;
375   bool Matched;
376 };
377 
378 #if !defined(_WIN32)
379 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
380   VerifyEndCallback EndCallback;
381 
382   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
383   std::vector<std::string> Sources;
384   Sources.push_back("/a.cc");
385   Sources.push_back("/b.cc");
386   ClangTool Tool(Compilations, Sources);
387 
388   Tool.mapVirtualFile("/a.cc", "void a() {}");
389   Tool.mapVirtualFile("/b.cc", "void b() {}");
390 
391   std::unique_ptr<FrontendActionFactory> Action(
392       newFrontendActionFactory(&EndCallback, &EndCallback));
393   Tool.run(Action.get());
394 
395   EXPECT_TRUE(EndCallback.Matched);
396   EXPECT_EQ(2u, EndCallback.BeginCalled);
397   EXPECT_EQ(2u, EndCallback.EndCalled);
398 }
399 #endif
400 
401 struct SkipBodyConsumer : public clang::ASTConsumer {
402   /// Skip the 'skipMe' function.
403   bool shouldSkipFunctionBody(Decl *D) override {
404     NamedDecl *F = dyn_cast<NamedDecl>(D);
405     return F && F->getNameAsString() == "skipMe";
406   }
407 };
408 
409 struct SkipBodyAction : public clang::ASTFrontendAction {
410   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
411                                                  StringRef) override {
412     Compiler.getFrontendOpts().SkipFunctionBodies = true;
413     return std::make_unique<SkipBodyConsumer>();
414   }
415 };
416 
417 TEST(runToolOnCode, TestSkipFunctionBody) {
418   std::vector<std::string> Args = {"-std=c++11"};
419   std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
420 
421   EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(),
422                             "int skipMe() { an_error_here }"));
423   EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(),
424                              "int skipMeNot() { an_error_here }"));
425 
426   // Test constructors with initializers
427   EXPECT_TRUE(runToolOnCodeWithArgs(
428       std::make_unique<SkipBodyAction>(),
429       "struct skipMe { skipMe() : an_error() { more error } };", Args));
430   EXPECT_TRUE(runToolOnCodeWithArgs(
431       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
432                           "skipMe::skipMe() : an_error([](){;}) { more error }",
433       Args));
434   EXPECT_TRUE(runToolOnCodeWithArgs(
435       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
436                           "skipMe::skipMe() : an_error{[](){;}} { more error }",
437       Args));
438   EXPECT_TRUE(runToolOnCodeWithArgs(
439       std::make_unique<SkipBodyAction>(),
440       "struct skipMe { skipMe(); };"
441       "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
442       Args));
443   EXPECT_TRUE(runToolOnCodeWithArgs(
444       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };",
445       Args));
446 
447   EXPECT_FALSE(runToolOnCodeWithArgs(
448       std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };",
449       Args));
450   EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(),
451                                      "struct skipMeNot { skipMeNot(); };"
452                                      "skipMeNot::skipMeNot() : an_error() { }",
453                                      Args));
454 
455   // Try/catch
456   EXPECT_TRUE(runToolOnCode(
457       std::make_unique<SkipBodyAction>(),
458       "void skipMe() try { an_error() } catch(error) { error };"));
459   EXPECT_TRUE(runToolOnCode(
460       std::make_unique<SkipBodyAction>(),
461       "struct S { void skipMe() try { an_error() } catch(error) { error } };"));
462   EXPECT_TRUE(
463       runToolOnCode(std::make_unique<SkipBodyAction>(),
464                     "void skipMe() try { an_error() } catch(error) { error; }"
465                     "catch(error) { error } catch (error) { }"));
466   EXPECT_FALSE(runToolOnCode(
467       std::make_unique<SkipBodyAction>(),
468       "void skipMe() try something;")); // don't crash while parsing
469 
470   // Template
471   EXPECT_TRUE(runToolOnCode(
472       std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }"
473                           "int x = skipMe<int>();"));
474   EXPECT_FALSE(runToolOnCodeWithArgs(
475       std::make_unique<SkipBodyAction>(),
476       "template<typename T> int skipMeNot() { an_error_here }", Args2));
477 }
478 
479 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
480   llvm::SmallString<32> DepFilePath;
481   ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d",
482                                                                DepFilePath));
483   std::vector<std::string> Args;
484   Args.push_back("-MMD");
485   Args.push_back("-MT");
486   Args.push_back(std::string(DepFilePath.str()));
487   Args.push_back("-MF");
488   Args.push_back(std::string(DepFilePath.str()));
489   EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args));
490   EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
491   EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
492 }
493 
494 struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction {
495   CheckColoredDiagnosticsAction(bool ShouldShowColor)
496       : ShouldShowColor(ShouldShowColor) {}
497   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
498                                                  StringRef) override {
499     if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor)
500       Compiler.getDiagnostics().Report(
501           Compiler.getDiagnostics().getCustomDiagID(
502               DiagnosticsEngine::Fatal,
503               "getDiagnosticOpts().ShowColors != ShouldShowColor"));
504     return std::make_unique<ASTConsumer>();
505   }
506 
507 private:
508   bool ShouldShowColor = true;
509 };
510 
511 TEST(runToolOnCodeWithArgs, DiagnosticsColor) {
512   EXPECT_TRUE(runToolOnCodeWithArgs(
513       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
514       {"-fcolor-diagnostics"}));
515   EXPECT_TRUE(runToolOnCodeWithArgs(
516       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
517       {"-fno-color-diagnostics"}));
518   EXPECT_TRUE(runToolOnCodeWithArgs(
519       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
520       {"-fno-color-diagnostics", "-fcolor-diagnostics"}));
521   EXPECT_TRUE(runToolOnCodeWithArgs(
522       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
523       {"-fcolor-diagnostics", "-fno-color-diagnostics"}));
524   EXPECT_TRUE(runToolOnCodeWithArgs(
525       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
526       {"-fno-color-diagnostics", "-fdiagnostics-color=always"}));
527 
528   // Check that this test would fail if ShowColors is not what it should.
529   EXPECT_FALSE(runToolOnCodeWithArgs(
530       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
531       {"-fcolor-diagnostics"}));
532 }
533 
534 TEST(ClangToolTest, ArgumentAdjusters) {
535   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
536 
537   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
538   Tool.mapVirtualFile("/a.cc", "void a() {}");
539 
540   std::unique_ptr<FrontendActionFactory> Action(
541       newFrontendActionFactory<SyntaxOnlyAction>());
542 
543   bool Found = false;
544   bool Ran = false;
545   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
546       [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
547     Ran = true;
548     if (llvm::is_contained(Args, "-fsyntax-only"))
549       Found = true;
550     return Args;
551   };
552   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
553   Tool.run(Action.get());
554   EXPECT_TRUE(Ran);
555   EXPECT_TRUE(Found);
556 
557   Ran = Found = false;
558   Tool.clearArgumentsAdjusters();
559   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
560   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
561   Tool.run(Action.get());
562   EXPECT_TRUE(Ran);
563   EXPECT_FALSE(Found);
564 }
565 
566 TEST(ClangToolTest, NoDoubleSyntaxOnly) {
567   FixedCompilationDatabase Compilations("/", {"-fsyntax-only"});
568 
569   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
570   Tool.mapVirtualFile("/a.cc", "void a() {}");
571 
572   std::unique_ptr<FrontendActionFactory> Action(
573       newFrontendActionFactory<SyntaxOnlyAction>());
574 
575   size_t SyntaxOnlyCount = 0;
576   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
577       [&SyntaxOnlyCount](const CommandLineArguments &Args,
578                          StringRef /*unused*/) {
579         for (llvm::StringRef Arg : Args) {
580           if (Arg == "-fsyntax-only")
581             ++SyntaxOnlyCount;
582         }
583         return Args;
584       };
585 
586   Tool.clearArgumentsAdjusters();
587   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
588   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
589   Tool.run(Action.get());
590   EXPECT_EQ(SyntaxOnlyCount, 1U);
591 }
592 
593 TEST(ClangToolTest, NoOutputCommands) {
594   FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd",
595                                               "--save-temps",
596                                               "--save-temps=somedir"});
597 
598   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
599   Tool.mapVirtualFile("/a.cc", "void a() {}");
600 
601   std::unique_ptr<FrontendActionFactory> Action(
602       newFrontendActionFactory<SyntaxOnlyAction>());
603 
604   const std::vector<llvm::StringRef> OutputCommands = {"-save-temps"};
605   bool Ran = false;
606   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
607       [&OutputCommands, &Ran](const CommandLineArguments &Args,
608                               StringRef /*unused*/) {
609         for (llvm::StringRef Arg : Args) {
610           for (llvm::StringRef OutputCommand : OutputCommands)
611             EXPECT_FALSE(Arg.contains(OutputCommand));
612         }
613         Ran = true;
614         return Args;
615       };
616 
617   Tool.clearArgumentsAdjusters();
618   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
619   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
620   Tool.run(Action.get());
621   EXPECT_TRUE(Ran);
622 }
623 
624 TEST(ClangToolTest, BaseVirtualFileSystemUsage) {
625   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
626   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
627       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
628   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
629       new llvm::vfs::InMemoryFileSystem);
630   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
631 
632   InMemoryFileSystem->addFile(
633       "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}"));
634 
635   ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"),
636                  std::make_shared<PCHContainerOperations>(), OverlayFileSystem);
637   std::unique_ptr<FrontendActionFactory> Action(
638       newFrontendActionFactory<SyntaxOnlyAction>());
639   EXPECT_EQ(0, Tool.run(Action.get()));
640 }
641 
642 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD.
643 TEST(ClangToolTest, StripDependencyFileAdjuster) {
644   FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"});
645 
646   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
647   Tool.mapVirtualFile("/a.cc", "void a() {}");
648 
649   std::unique_ptr<FrontendActionFactory> Action(
650       newFrontendActionFactory<SyntaxOnlyAction>());
651 
652   CommandLineArguments FinalArgs;
653   ArgumentsAdjuster CheckFlagsAdjuster =
654     [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
655       FinalArgs = Args;
656       return Args;
657     };
658   Tool.clearArgumentsAdjusters();
659   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
660   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
661   Tool.run(Action.get());
662 
663   auto HasFlag = [&FinalArgs](const std::string &Flag) {
664     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
665   };
666   EXPECT_FALSE(HasFlag("-MD"));
667   EXPECT_FALSE(HasFlag("-MMD"));
668   EXPECT_TRUE(HasFlag("-c"));
669   EXPECT_TRUE(HasFlag("-w"));
670 }
671 
672 // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants
673 TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) {
674   FixedCompilationDatabase Compilations(
675       "/", {"/showIncludes", "/showIncludes:user", "-showIncludes",
676             "-showIncludes:user", "-c"});
677 
678   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
679   Tool.mapVirtualFile("/a.cc", "void a() {}");
680 
681   std::unique_ptr<FrontendActionFactory> Action(
682       newFrontendActionFactory<SyntaxOnlyAction>());
683 
684   CommandLineArguments FinalArgs;
685   ArgumentsAdjuster CheckFlagsAdjuster =
686       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
687         FinalArgs = Args;
688         return Args;
689       };
690   Tool.clearArgumentsAdjusters();
691   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
692   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
693   Tool.run(Action.get());
694 
695   auto HasFlag = [&FinalArgs](const std::string &Flag) {
696     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
697   };
698   EXPECT_FALSE(HasFlag("/showIncludes"));
699   EXPECT_FALSE(HasFlag("/showIncludes:user"));
700   EXPECT_FALSE(HasFlag("-showIncludes"));
701   EXPECT_FALSE(HasFlag("-showIncludes:user"));
702   EXPECT_TRUE(HasFlag("-c"));
703 }
704 
705 // Check getClangStripDependencyFileAdjuster doesn't strip args when using the
706 // MSVC cl.exe driver
707 TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) {
708   FixedCompilationDatabase Compilations(
709       "/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"});
710 
711   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
712   Tool.mapVirtualFile("/a.cc", "void a() {}");
713 
714   std::unique_ptr<FrontendActionFactory> Action(
715       newFrontendActionFactory<SyntaxOnlyAction>());
716 
717   CommandLineArguments FinalArgs;
718   ArgumentsAdjuster CheckFlagsAdjuster =
719       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
720         FinalArgs = Args;
721         return Args;
722       };
723   Tool.clearArgumentsAdjusters();
724   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
725   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
726   Tool.run(Action.get());
727 
728   auto HasFlag = [&FinalArgs](const std::string &Flag) {
729     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
730   };
731   EXPECT_TRUE(HasFlag("-MD"));
732   EXPECT_TRUE(HasFlag("-MDd"));
733   EXPECT_TRUE(HasFlag("-MT"));
734   EXPECT_TRUE(HasFlag("-O1"));
735   EXPECT_TRUE(HasFlag("-MTd"));
736   EXPECT_TRUE(HasFlag("-MP"));
737 }
738 
739 // Check getClangStripPluginsAdjuster strips plugin related args.
740 TEST(ClangToolTest, StripPluginsAdjuster) {
741   FixedCompilationDatabase Compilations(
742       "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"});
743 
744   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
745   Tool.mapVirtualFile("/a.cc", "void a() {}");
746 
747   std::unique_ptr<FrontendActionFactory> Action(
748       newFrontendActionFactory<SyntaxOnlyAction>());
749 
750   CommandLineArguments FinalArgs;
751   ArgumentsAdjuster CheckFlagsAdjuster =
752       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
753         FinalArgs = Args;
754         return Args;
755       };
756   Tool.clearArgumentsAdjusters();
757   Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
758   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
759   Tool.run(Action.get());
760 
761   auto HasFlag = [&FinalArgs](const std::string &Flag) {
762     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
763   };
764   EXPECT_FALSE(HasFlag("-Xclang"));
765   EXPECT_FALSE(HasFlag("-add-plugin"));
766   EXPECT_FALSE(HasFlag("-random-plugin"));
767 }
768 
769 namespace {
770 /// Find a target name such that looking for it in TargetRegistry by that name
771 /// returns the same target. We expect that there is at least one target
772 /// configured with this property.
773 std::string getAnyTarget() {
774   llvm::InitializeAllTargets();
775   for (const auto &Target : llvm::TargetRegistry::targets()) {
776     std::string Error;
777     StringRef TargetName(Target.getName());
778     if (TargetName == "x86-64")
779       TargetName = "x86_64";
780     if (llvm::TargetRegistry::lookupTarget(std::string(TargetName), Error) ==
781         &Target) {
782       return std::string(TargetName);
783     }
784   }
785   return "";
786 }
787 }
788 
789 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
790   std::string Target = getAnyTarget();
791   ASSERT_FALSE(Target.empty());
792 
793   std::vector<std::string> Args = {"clang", "-foo"};
794   addTargetAndModeForProgramName(Args, "");
795   EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
796   addTargetAndModeForProgramName(Args, Target + "-g++");
797   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
798                                       "--driver-mode=g++", "-foo"}),
799             Args);
800 }
801 
802 TEST(addTargetAndModeForProgramName, PathIgnored) {
803   std::string Target = getAnyTarget();
804   ASSERT_FALSE(Target.empty());
805 
806   SmallString<32> ToolPath;
807   llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
808 
809   std::vector<std::string> Args = {"clang", "-foo"};
810   addTargetAndModeForProgramName(Args, ToolPath);
811   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
812                                       "--driver-mode=g++", "-foo"}),
813             Args);
814 }
815 
816 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
817   std::string Target = getAnyTarget();
818   ASSERT_FALSE(Target.empty());
819 
820   std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
821   addTargetAndModeForProgramName(Args, Target + "-g++");
822   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
823                                       "-target", "something"}),
824             Args);
825 
826   std::vector<std::string> ArgsAlt = {"clang", "-foo", "--target=something"};
827   addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
828   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
829                                       "--target=something"}),
830             ArgsAlt);
831 }
832 
833 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
834   std::string Target = getAnyTarget();
835   ASSERT_FALSE(Target.empty());
836 
837   std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
838   addTargetAndModeForProgramName(Args, Target + "-g++");
839   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo",
840                                       "--driver-mode=abc"}),
841             Args);
842 }
843 
844 #ifndef _WIN32
845 TEST(ClangToolTest, BuildASTs) {
846   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
847 
848   std::vector<std::string> Sources;
849   Sources.push_back("/a.cc");
850   Sources.push_back("/b.cc");
851   ClangTool Tool(Compilations, Sources);
852 
853   Tool.mapVirtualFile("/a.cc", "void a() {}");
854   Tool.mapVirtualFile("/b.cc", "void b() {}");
855 
856   std::vector<std::unique_ptr<ASTUnit>> ASTs;
857   EXPECT_EQ(0, Tool.buildASTs(ASTs));
858   EXPECT_EQ(2u, ASTs.size());
859 }
860 
861 TEST(ClangToolTest, InjectDiagnosticConsumer) {
862   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
863   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
864   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
865   TestDiagnosticConsumer Consumer;
866   Tool.setDiagnosticConsumer(&Consumer);
867   std::unique_ptr<FrontendActionFactory> Action(
868       newFrontendActionFactory<SyntaxOnlyAction>());
869   Tool.run(Action.get());
870   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
871 }
872 
873 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
874   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
875   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
876   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
877   TestDiagnosticConsumer Consumer;
878   Tool.setDiagnosticConsumer(&Consumer);
879   std::vector<std::unique_ptr<ASTUnit>> ASTs;
880   Tool.buildASTs(ASTs);
881   EXPECT_EQ(1u, ASTs.size());
882   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
883 }
884 #endif
885 
886 TEST(runToolOnCode, TestResetDiagnostics) {
887   // This is a tool that resets the diagnostic during the compilation.
888   struct ResetDiagnosticAction : public clang::ASTFrontendAction {
889     std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
890                                                    StringRef) override {
891       struct Consumer : public clang::ASTConsumer {
892         bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
893           auto &Diags = (*D.begin())->getASTContext().getDiagnostics();
894           // Ignore any error
895           Diags.Reset();
896           // Disable warnings because computing the CFG might crash.
897           Diags.setIgnoreAllWarnings(true);
898           return true;
899         }
900       };
901       return std::make_unique<Consumer>();
902     }
903   };
904 
905   // Should not crash
906   EXPECT_FALSE(
907       runToolOnCode(std::make_unique<ResetDiagnosticAction>(),
908                     "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };"
909                     "void func() { long x; Foo f(x); }"));
910 }
911 
912 } // end namespace tooling
913 } // end namespace clang
914