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