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/Tooling/ArgumentsAdjusters.h"
21 #include "clang/Tooling/CompilationDatabase.h"
22 #include "llvm/ADT/STLExtras.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/MC/TargetRegistry.h"
25 #include "llvm/Support/Host.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/TargetSelect.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.
TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)42   explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
43       : TestConsumer(std::move(TestConsumer)) {}
44 
45 protected:
46   std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance & compiler,StringRef dummy)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:
FindTopLevelDeclConsumer(bool * FoundTopLevelDecl)59   explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
60       : FoundTopLevelDecl(FoundTopLevelDecl) {}
HandleTopLevelDecl(clang::DeclGroupRef DeclGroup)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 
TEST(runToolOnCode,FindsNoTopLevelDeclOnEmptyCode)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:
FindClassDeclXConsumer(bool * FoundClassDeclX)82   FindClassDeclXConsumer(bool *FoundClassDeclX)
83       : FoundClassDeclX(FoundClassDeclX) {}
HandleTopLevelDecl(clang::DeclGroupRef GroupRef)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 };
FindClassDeclX(ASTUnit * AST)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 {
TestDiagnosticConsumerclang::tooling::__anon2081cfad0211::TestDiagnosticConsumer110   TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
HandleDiagnosticclang::tooling::__anon2081cfad0211::TestDiagnosticConsumer111   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
112                         const Diagnostic &Info) override {
113     ++NumDiagnosticsSeen;
114   }
115   unsigned NumDiagnosticsSeen;
116 };
117 } // end namespace
118 
TEST(runToolOnCode,FindsClassDecl)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 
TEST(buildASTFromCode,FindsClassDecl)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 
TEST(buildASTFromCode,ReportsErrors)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 
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromType)155 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
156   std::unique_ptr<FrontendActionFactory> Factory(
157       newFrontendActionFactory<SyntaxOnlyAction>());
158   std::unique_ptr<FrontendAction> Action(Factory->create());
159   EXPECT_TRUE(Action.get() != nullptr);
160 }
161 
162 struct IndependentFrontendActionCreator {
newASTConsumerclang::tooling::IndependentFrontendActionCreator163   std::unique_ptr<ASTConsumer> newASTConsumer() {
164     return std::make_unique<FindTopLevelDeclConsumer>(nullptr);
165   }
166 };
167 
TEST(newFrontendActionFactory,CreatesFrontendActionFactoryFromFactoryType)168 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
169   IndependentFrontendActionCreator Creator;
170   std::unique_ptr<FrontendActionFactory> Factory(
171       newFrontendActionFactory(&Creator));
172   std::unique_ptr<FrontendAction> Action(Factory->create());
173   EXPECT_TRUE(Action.get() != nullptr);
174 }
175 
TEST(ToolInvocation,TestMapVirtualFile)176 TEST(ToolInvocation, TestMapVirtualFile) {
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   EXPECT_TRUE(Invocation.run());
196 }
197 
TEST(ToolInvocation,TestVirtualModulesCompilation)198 TEST(ToolInvocation, TestVirtualModulesCompilation) {
199   // FIXME: Currently, this only tests that we don't exit with an error if a
200   // mapped module.map is found on the include path. In the future, expand this
201   // test to run a full modules enabled compilation, so we make sure we can
202   // rerun modules compilations with a virtual file system.
203   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
204       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
205   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
206       new llvm::vfs::InMemoryFileSystem);
207   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
208   llvm::IntrusiveRefCntPtr<FileManager> Files(
209       new FileManager(FileSystemOptions(), OverlayFileSystem));
210   std::vector<std::string> Args;
211   Args.push_back("tool-executable");
212   Args.push_back("-Idef");
213   Args.push_back("-fsyntax-only");
214   Args.push_back("test.cpp");
215   clang::tooling::ToolInvocation Invocation(
216       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
217   InMemoryFileSystem->addFile(
218       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n"));
219   InMemoryFileSystem->addFile("def/abc", 0,
220                               llvm::MemoryBuffer::getMemBuffer("\n"));
221   // Add a module.map file in the include directory of our header, so we trigger
222   // the module.map header search logic.
223   InMemoryFileSystem->addFile("def/module.map", 0,
224                               llvm::MemoryBuffer::getMemBuffer("\n"));
225   EXPECT_TRUE(Invocation.run());
226 }
227 
TEST(ToolInvocation,DiagnosticsEngineProperlyInitializedForCC1Construction)228 TEST(ToolInvocation, DiagnosticsEngineProperlyInitializedForCC1Construction) {
229   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
230       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
231   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
232       new llvm::vfs::InMemoryFileSystem);
233   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
234   llvm::IntrusiveRefCntPtr<FileManager> Files(
235       new FileManager(FileSystemOptions(), OverlayFileSystem));
236 
237   std::vector<std::string> Args;
238   Args.push_back("tool-executable");
239   // Unknown warning option will result in a warning.
240   Args.push_back("-fexpensive-optimizations");
241   // Argument that will suppress the warning above.
242   Args.push_back("-Wno-ignored-optimization-argument");
243   Args.push_back("-E");
244   Args.push_back("test.cpp");
245 
246   clang::tooling::ToolInvocation Invocation(
247       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
248   InMemoryFileSystem->addFile("test.cpp", 0,
249                               llvm::MemoryBuffer::getMemBuffer(""));
250   TextDiagnosticBuffer Consumer;
251   Invocation.setDiagnosticConsumer(&Consumer);
252   EXPECT_TRUE(Invocation.run());
253   // Check that the warning was ignored due to the '-Wno-xxx' argument.
254   EXPECT_EQ(std::distance(Consumer.warn_begin(), Consumer.warn_end()), 0u);
255 }
256 
TEST(ToolInvocation,CustomDiagnosticOptionsOverwriteParsedOnes)257 TEST(ToolInvocation, CustomDiagnosticOptionsOverwriteParsedOnes) {
258   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
259       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
260   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
261       new llvm::vfs::InMemoryFileSystem);
262   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
263   llvm::IntrusiveRefCntPtr<FileManager> Files(
264       new FileManager(FileSystemOptions(), OverlayFileSystem));
265 
266   std::vector<std::string> Args;
267   Args.push_back("tool-executable");
268   // Unknown warning option will result in a warning.
269   Args.push_back("-fexpensive-optimizations");
270   // Argument that will suppress the warning above.
271   Args.push_back("-Wno-ignored-optimization-argument");
272   Args.push_back("-E");
273   Args.push_back("test.cpp");
274 
275   clang::tooling::ToolInvocation Invocation(
276       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
277   InMemoryFileSystem->addFile("test.cpp", 0,
278                               llvm::MemoryBuffer::getMemBuffer(""));
279   TextDiagnosticBuffer Consumer;
280   Invocation.setDiagnosticConsumer(&Consumer);
281 
282   // Inject custom `DiagnosticOptions` for command-line parsing.
283   auto DiagOpts = llvm::makeIntrusiveRefCnt<DiagnosticOptions>();
284   Invocation.setDiagnosticOptions(&*DiagOpts);
285 
286   EXPECT_TRUE(Invocation.run());
287   // Check that the warning was issued during command-line parsing due to the
288   // custom `DiagnosticOptions` without '-Wno-xxx'.
289   EXPECT_EQ(std::distance(Consumer.warn_begin(), Consumer.warn_end()), 1u);
290 }
291 
292 struct DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer {
293   bool SawSourceManager;
294 
DiagnosticConsumerExpectingSourceManagerclang::tooling::DiagnosticConsumerExpectingSourceManager295   DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {}
296 
HandleDiagnosticclang::tooling::DiagnosticConsumerExpectingSourceManager297   void HandleDiagnostic(clang::DiagnosticsEngine::Level,
298                         const clang::Diagnostic &info) override {
299     SawSourceManager = info.hasSourceManager();
300   }
301 };
302 
TEST(ToolInvocation,DiagConsumerExpectingSourceManager)303 TEST(ToolInvocation, DiagConsumerExpectingSourceManager) {
304   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
305       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
306   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
307       new llvm::vfs::InMemoryFileSystem);
308   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
309   llvm::IntrusiveRefCntPtr<FileManager> Files(
310       new FileManager(FileSystemOptions(), OverlayFileSystem));
311   std::vector<std::string> Args;
312   Args.push_back("tool-executable");
313   // Note: intentional error; user probably meant -ferror-limit=0.
314   Args.push_back("-ferror-limit=-1");
315   Args.push_back("-fsyntax-only");
316   Args.push_back("test.cpp");
317   clang::tooling::ToolInvocation Invocation(
318       Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
319   InMemoryFileSystem->addFile(
320       "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}\n"));
321 
322   DiagnosticConsumerExpectingSourceManager Consumer;
323   Invocation.setDiagnosticConsumer(&Consumer);
324 
325   EXPECT_TRUE(Invocation.run());
326   EXPECT_TRUE(Consumer.SawSourceManager);
327 }
328 
329 namespace {
330 /// Overlays the real filesystem with the given VFS and returns the result.
331 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)332 overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
333   auto RFS = llvm::vfs::getRealFileSystem();
334   auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(RFS);
335   OverlayFS->pushOverlay(VFS);
336   return OverlayFS;
337 }
338 
339 struct CommandLineExtractorTest : public ::testing::Test {
340   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS;
341   llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
342   driver::Driver Driver;
343 
344 public:
CommandLineExtractorTestclang::tooling::__anon2081cfad0311::CommandLineExtractorTest345   CommandLineExtractorTest()
346       : InMemoryFS(new llvm::vfs::InMemoryFileSystem),
347         Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)),
348         Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags,
349                "clang LLVM compiler", overlayRealFS(InMemoryFS)) {}
350 
addFileclang::tooling::__anon2081cfad0311::CommandLineExtractorTest351   void addFile(StringRef Name, StringRef Content) {
352     InMemoryFS->addFile(Name, 0, llvm::MemoryBuffer::getMemBuffer(Content));
353   }
354 
355   const llvm::opt::ArgStringList *
extractCC1Argumentsclang::tooling::__anon2081cfad0311::CommandLineExtractorTest356   extractCC1Arguments(llvm::ArrayRef<const char *> Argv) {
357     const std::unique_ptr<driver::Compilation> Compilation(
358         Driver.BuildCompilation(llvm::makeArrayRef(Argv)));
359 
360     return getCC1Arguments(Diags.get(), Compilation.get());
361   }
362 };
363 } // namespace
364 
TEST_F(CommandLineExtractorTest,AcceptOffloading)365 TEST_F(CommandLineExtractorTest, AcceptOffloading) {
366   addFile("test.c", "int main() {}\n");
367   const char *Args[] = {"clang",     "-target",  "arm64-apple-macosx11.0.0",
368                         "-x",        "hip",      "test.c",
369                         "-nogpulib", "-nogpuinc"};
370   EXPECT_NE(extractCC1Arguments(Args), nullptr);
371 }
372 
TEST_F(CommandLineExtractorTest,AcceptOffloadingCompile)373 TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) {
374   addFile("test.c", "int main() {}\n");
375   const char *Args[] = {"clang",  "-target",   "arm64-apple-macosx11.0.0",
376                         "-c",     "-x",        "hip",
377                         "test.c", "-nogpulib", "-nogpuinc"};
378   EXPECT_NE(extractCC1Arguments(Args), nullptr);
379 }
380 
TEST_F(CommandLineExtractorTest,AcceptOffloadingSyntaxOnly)381 TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) {
382   addFile("test.c", "int main() {}\n");
383   const char *Args[] = {
384       "clang",         "-target",   "arm64-apple-macosx11.0.0",
385       "-fsyntax-only", "-x",        "hip",
386       "test.c",        "-nogpulib", "-nogpuinc"};
387   EXPECT_NE(extractCC1Arguments(Args), nullptr);
388 }
389 
TEST_F(CommandLineExtractorTest,AcceptExternalAssembler)390 TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) {
391   addFile("test.c", "int main() {}\n");
392   const char *Args[] = {
393       "clang", "-target", "arm64-apple-macosx11.0.0", "-fno-integrated-as",
394       "-c",    "test.c"};
395   EXPECT_NE(extractCC1Arguments(Args), nullptr);
396 }
397 
TEST_F(CommandLineExtractorTest,AcceptEmbedBitcode)398 TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) {
399   addFile("test.c", "int main() {}\n");
400   const char *Args[] = {"clang", "-target",         "arm64-apple-macosx11.0.0",
401                         "-c",    "-fembed-bitcode", "test.c"};
402   EXPECT_NE(extractCC1Arguments(Args), nullptr);
403 }
404 
TEST_F(CommandLineExtractorTest,AcceptSaveTemps)405 TEST_F(CommandLineExtractorTest, AcceptSaveTemps) {
406   addFile("test.c", "int main() {}\n");
407   const char *Args[] = {"clang", "-target",     "arm64-apple-macosx11.0.0",
408                         "-c",    "-save-temps", "test.c"};
409   EXPECT_NE(extractCC1Arguments(Args), nullptr);
410 }
411 
TEST_F(CommandLineExtractorTest,RejectMultipleArchitectures)412 TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) {
413   addFile("test.c", "int main() {}\n");
414   const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
415                         "-arch", "x86_64",  "-arch",
416                         "arm64", "-c",      "test.c"};
417   EXPECT_EQ(extractCC1Arguments(Args), nullptr);
418 }
419 
TEST_F(CommandLineExtractorTest,RejectMultipleInputFiles)420 TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) {
421   addFile("one.c", "void one() {}\n");
422   addFile("two.c", "void two() {}\n");
423   const char *Args[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
424                         "-c",    "one.c",   "two.c"};
425   EXPECT_EQ(extractCC1Arguments(Args), nullptr);
426 }
427 
428 struct VerifyEndCallback : public SourceFileCallbacks {
VerifyEndCallbackclang::tooling::VerifyEndCallback429   VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
handleBeginSourceclang::tooling::VerifyEndCallback430   bool handleBeginSource(CompilerInstance &CI) override {
431     ++BeginCalled;
432     return true;
433   }
handleEndSourceclang::tooling::VerifyEndCallback434   void handleEndSource() override { ++EndCalled; }
newASTConsumerclang::tooling::VerifyEndCallback435   std::unique_ptr<ASTConsumer> newASTConsumer() {
436     return std::make_unique<FindTopLevelDeclConsumer>(&Matched);
437   }
438   unsigned BeginCalled;
439   unsigned EndCalled;
440   bool Matched;
441 };
442 
443 #if !defined(_WIN32)
TEST(newFrontendActionFactory,InjectsSourceFileCallbacks)444 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
445   VerifyEndCallback EndCallback;
446 
447   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
448   std::vector<std::string> Sources;
449   Sources.push_back("/a.cc");
450   Sources.push_back("/b.cc");
451   ClangTool Tool(Compilations, Sources);
452 
453   Tool.mapVirtualFile("/a.cc", "void a() {}");
454   Tool.mapVirtualFile("/b.cc", "void b() {}");
455 
456   std::unique_ptr<FrontendActionFactory> Action(
457       newFrontendActionFactory(&EndCallback, &EndCallback));
458   Tool.run(Action.get());
459 
460   EXPECT_TRUE(EndCallback.Matched);
461   EXPECT_EQ(2u, EndCallback.BeginCalled);
462   EXPECT_EQ(2u, EndCallback.EndCalled);
463 }
464 #endif
465 
466 struct SkipBodyConsumer : public clang::ASTConsumer {
467   /// Skip the 'skipMe' function.
shouldSkipFunctionBodyclang::tooling::SkipBodyConsumer468   bool shouldSkipFunctionBody(Decl *D) override {
469     NamedDecl *F = dyn_cast<NamedDecl>(D);
470     return F && F->getNameAsString() == "skipMe";
471   }
472 };
473 
474 struct SkipBodyAction : public clang::ASTFrontendAction {
CreateASTConsumerclang::tooling::SkipBodyAction475   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
476                                                  StringRef) override {
477     Compiler.getFrontendOpts().SkipFunctionBodies = true;
478     return std::make_unique<SkipBodyConsumer>();
479   }
480 };
481 
TEST(runToolOnCode,TestSkipFunctionBody)482 TEST(runToolOnCode, TestSkipFunctionBody) {
483   std::vector<std::string> Args = {"-std=c++11"};
484   std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
485 
486   EXPECT_TRUE(runToolOnCode(std::make_unique<SkipBodyAction>(),
487                             "int skipMe() { an_error_here }"));
488   EXPECT_FALSE(runToolOnCode(std::make_unique<SkipBodyAction>(),
489                              "int skipMeNot() { an_error_here }"));
490 
491   // Test constructors with initializers
492   EXPECT_TRUE(runToolOnCodeWithArgs(
493       std::make_unique<SkipBodyAction>(),
494       "struct skipMe { skipMe() : an_error() { more error } };", Args));
495   EXPECT_TRUE(runToolOnCodeWithArgs(
496       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
497                           "skipMe::skipMe() : an_error([](){;}) { more error }",
498       Args));
499   EXPECT_TRUE(runToolOnCodeWithArgs(
500       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe(); };"
501                           "skipMe::skipMe() : an_error{[](){;}} { more error }",
502       Args));
503   EXPECT_TRUE(runToolOnCodeWithArgs(
504       std::make_unique<SkipBodyAction>(),
505       "struct skipMe { skipMe(); };"
506       "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
507       Args));
508   EXPECT_TRUE(runToolOnCodeWithArgs(
509       std::make_unique<SkipBodyAction>(), "struct skipMe { skipMe() : bases()... { error } };",
510       Args));
511 
512   EXPECT_FALSE(runToolOnCodeWithArgs(
513       std::make_unique<SkipBodyAction>(), "struct skipMeNot { skipMeNot() : an_error() { } };",
514       Args));
515   EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(),
516                                      "struct skipMeNot { skipMeNot(); };"
517                                      "skipMeNot::skipMeNot() : an_error() { }",
518                                      Args));
519 
520   // Try/catch
521   EXPECT_TRUE(runToolOnCode(
522       std::make_unique<SkipBodyAction>(),
523       "void skipMe() try { an_error() } catch(error) { error };"));
524   EXPECT_TRUE(runToolOnCode(
525       std::make_unique<SkipBodyAction>(),
526       "struct S { void skipMe() try { an_error() } catch(error) { error } };"));
527   EXPECT_TRUE(
528       runToolOnCode(std::make_unique<SkipBodyAction>(),
529                     "void skipMe() try { an_error() } catch(error) { error; }"
530                     "catch(error) { error } catch (error) { }"));
531   EXPECT_FALSE(runToolOnCode(
532       std::make_unique<SkipBodyAction>(),
533       "void skipMe() try something;")); // don't crash while parsing
534 
535   // Template
536   EXPECT_TRUE(runToolOnCode(
537       std::make_unique<SkipBodyAction>(), "template<typename T> int skipMe() { an_error_here }"
538                           "int x = skipMe<int>();"));
539   EXPECT_FALSE(runToolOnCodeWithArgs(
540       std::make_unique<SkipBodyAction>(),
541       "template<typename T> int skipMeNot() { an_error_here }", Args2));
542 }
543 
TEST(runToolOnCodeWithArgs,TestNoDepFile)544 TEST(runToolOnCodeWithArgs, TestNoDepFile) {
545   llvm::SmallString<32> DepFilePath;
546   ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d",
547                                                                DepFilePath));
548   std::vector<std::string> Args;
549   Args.push_back("-MMD");
550   Args.push_back("-MT");
551   Args.push_back(std::string(DepFilePath.str()));
552   Args.push_back("-MF");
553   Args.push_back(std::string(DepFilePath.str()));
554   EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<SkipBodyAction>(), "", Args));
555   EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str()));
556   EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str()));
557 }
558 
559 struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction {
CheckColoredDiagnosticsActionclang::tooling::CheckColoredDiagnosticsAction560   CheckColoredDiagnosticsAction(bool ShouldShowColor)
561       : ShouldShowColor(ShouldShowColor) {}
CreateASTConsumerclang::tooling::CheckColoredDiagnosticsAction562   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
563                                                  StringRef) override {
564     if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor)
565       Compiler.getDiagnostics().Report(
566           Compiler.getDiagnostics().getCustomDiagID(
567               DiagnosticsEngine::Fatal,
568               "getDiagnosticOpts().ShowColors != ShouldShowColor"));
569     return std::make_unique<ASTConsumer>();
570   }
571 
572 private:
573   bool ShouldShowColor = true;
574 };
575 
TEST(runToolOnCodeWithArgs,DiagnosticsColor)576 TEST(runToolOnCodeWithArgs, DiagnosticsColor) {
577   EXPECT_TRUE(runToolOnCodeWithArgs(
578       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
579       {"-fcolor-diagnostics"}));
580   EXPECT_TRUE(runToolOnCodeWithArgs(
581       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
582       {"-fno-color-diagnostics"}));
583   EXPECT_TRUE(runToolOnCodeWithArgs(
584       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
585       {"-fno-color-diagnostics", "-fcolor-diagnostics"}));
586   EXPECT_TRUE(runToolOnCodeWithArgs(
587       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
588       {"-fcolor-diagnostics", "-fno-color-diagnostics"}));
589   EXPECT_TRUE(runToolOnCodeWithArgs(
590       std::make_unique<CheckColoredDiagnosticsAction>(true), "",
591       {"-fno-color-diagnostics", "-fdiagnostics-color=always"}));
592 
593   // Check that this test would fail if ShowColors is not what it should.
594   EXPECT_FALSE(runToolOnCodeWithArgs(
595       std::make_unique<CheckColoredDiagnosticsAction>(false), "",
596       {"-fcolor-diagnostics"}));
597 }
598 
TEST(ClangToolTest,ArgumentAdjusters)599 TEST(ClangToolTest, ArgumentAdjusters) {
600   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
601 
602   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
603   Tool.mapVirtualFile("/a.cc", "void a() {}");
604 
605   std::unique_ptr<FrontendActionFactory> Action(
606       newFrontendActionFactory<SyntaxOnlyAction>());
607 
608   bool Found = false;
609   bool Ran = false;
610   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
611       [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) {
612     Ran = true;
613     if (llvm::is_contained(Args, "-fsyntax-only"))
614       Found = true;
615     return Args;
616   };
617   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
618   Tool.run(Action.get());
619   EXPECT_TRUE(Ran);
620   EXPECT_TRUE(Found);
621 
622   Ran = Found = false;
623   Tool.clearArgumentsAdjusters();
624   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
625   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
626   Tool.run(Action.get());
627   EXPECT_TRUE(Ran);
628   EXPECT_FALSE(Found);
629 }
630 
TEST(ClangToolTest,NoDoubleSyntaxOnly)631 TEST(ClangToolTest, NoDoubleSyntaxOnly) {
632   FixedCompilationDatabase Compilations("/", {"-fsyntax-only"});
633 
634   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
635   Tool.mapVirtualFile("/a.cc", "void a() {}");
636 
637   std::unique_ptr<FrontendActionFactory> Action(
638       newFrontendActionFactory<SyntaxOnlyAction>());
639 
640   size_t SyntaxOnlyCount = 0;
641   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
642       [&SyntaxOnlyCount](const CommandLineArguments &Args,
643                          StringRef /*unused*/) {
644         for (llvm::StringRef Arg : Args) {
645           if (Arg == "-fsyntax-only")
646             ++SyntaxOnlyCount;
647         }
648         return Args;
649       };
650 
651   Tool.clearArgumentsAdjusters();
652   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
653   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
654   Tool.run(Action.get());
655   EXPECT_EQ(SyntaxOnlyCount, 1U);
656 }
657 
TEST(ClangToolTest,NoOutputCommands)658 TEST(ClangToolTest, NoOutputCommands) {
659   FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd",
660                                               "--save-temps",
661                                               "--save-temps=somedir"});
662 
663   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
664   Tool.mapVirtualFile("/a.cc", "void a() {}");
665 
666   std::unique_ptr<FrontendActionFactory> Action(
667       newFrontendActionFactory<SyntaxOnlyAction>());
668 
669   const std::vector<llvm::StringRef> OutputCommands = {"-save-temps"};
670   bool Ran = false;
671   ArgumentsAdjuster CheckSyntaxOnlyAdjuster =
672       [&OutputCommands, &Ran](const CommandLineArguments &Args,
673                               StringRef /*unused*/) {
674         for (llvm::StringRef Arg : Args) {
675           for (llvm::StringRef OutputCommand : OutputCommands)
676             EXPECT_FALSE(Arg.contains(OutputCommand));
677         }
678         Ran = true;
679         return Args;
680       };
681 
682   Tool.clearArgumentsAdjusters();
683   Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
684   Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
685   Tool.run(Action.get());
686   EXPECT_TRUE(Ran);
687 }
688 
TEST(ClangToolTest,BaseVirtualFileSystemUsage)689 TEST(ClangToolTest, BaseVirtualFileSystemUsage) {
690   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
691   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
692       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
693   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
694       new llvm::vfs::InMemoryFileSystem);
695   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
696 
697   InMemoryFileSystem->addFile(
698       "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}"));
699 
700   ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"),
701                  std::make_shared<PCHContainerOperations>(), OverlayFileSystem);
702   std::unique_ptr<FrontendActionFactory> Action(
703       newFrontendActionFactory<SyntaxOnlyAction>());
704   EXPECT_EQ(0, Tool.run(Action.get()));
705 }
706 
707 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD.
TEST(ClangToolTest,StripDependencyFileAdjuster)708 TEST(ClangToolTest, StripDependencyFileAdjuster) {
709   FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"});
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_FALSE(HasFlag("-MD"));
732   EXPECT_FALSE(HasFlag("-MMD"));
733   EXPECT_TRUE(HasFlag("-c"));
734   EXPECT_TRUE(HasFlag("-w"));
735 }
736 
737 // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants
TEST(ClangToolTest,StripDependencyFileAdjusterShowIncludes)738 TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) {
739   FixedCompilationDatabase Compilations(
740       "/", {"/showIncludes", "/showIncludes:user", "-showIncludes",
741             "-showIncludes:user", "-c"});
742 
743   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
744   Tool.mapVirtualFile("/a.cc", "void a() {}");
745 
746   std::unique_ptr<FrontendActionFactory> Action(
747       newFrontendActionFactory<SyntaxOnlyAction>());
748 
749   CommandLineArguments FinalArgs;
750   ArgumentsAdjuster CheckFlagsAdjuster =
751       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
752         FinalArgs = Args;
753         return Args;
754       };
755   Tool.clearArgumentsAdjusters();
756   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
757   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
758   Tool.run(Action.get());
759 
760   auto HasFlag = [&FinalArgs](const std::string &Flag) {
761     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
762   };
763   EXPECT_FALSE(HasFlag("/showIncludes"));
764   EXPECT_FALSE(HasFlag("/showIncludes:user"));
765   EXPECT_FALSE(HasFlag("-showIncludes"));
766   EXPECT_FALSE(HasFlag("-showIncludes:user"));
767   EXPECT_TRUE(HasFlag("-c"));
768 }
769 
770 // Check getClangStripDependencyFileAdjuster doesn't strip args when using the
771 // MSVC cl.exe driver
TEST(ClangToolTest,StripDependencyFileAdjusterMsvc)772 TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) {
773   FixedCompilationDatabase Compilations(
774       "/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"});
775 
776   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
777   Tool.mapVirtualFile("/a.cc", "void a() {}");
778 
779   std::unique_ptr<FrontendActionFactory> Action(
780       newFrontendActionFactory<SyntaxOnlyAction>());
781 
782   CommandLineArguments FinalArgs;
783   ArgumentsAdjuster CheckFlagsAdjuster =
784       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
785         FinalArgs = Args;
786         return Args;
787       };
788   Tool.clearArgumentsAdjusters();
789   Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
790   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
791   Tool.run(Action.get());
792 
793   auto HasFlag = [&FinalArgs](const std::string &Flag) {
794     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
795   };
796   EXPECT_TRUE(HasFlag("-MD"));
797   EXPECT_TRUE(HasFlag("-MDd"));
798   EXPECT_TRUE(HasFlag("-MT"));
799   EXPECT_TRUE(HasFlag("-O1"));
800   EXPECT_TRUE(HasFlag("-MTd"));
801   EXPECT_TRUE(HasFlag("-MP"));
802 }
803 
804 // Check getClangStripPluginsAdjuster strips plugin related args.
TEST(ClangToolTest,StripPluginsAdjuster)805 TEST(ClangToolTest, StripPluginsAdjuster) {
806   FixedCompilationDatabase Compilations(
807       "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"});
808 
809   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
810   Tool.mapVirtualFile("/a.cc", "void a() {}");
811 
812   std::unique_ptr<FrontendActionFactory> Action(
813       newFrontendActionFactory<SyntaxOnlyAction>());
814 
815   CommandLineArguments FinalArgs;
816   ArgumentsAdjuster CheckFlagsAdjuster =
817       [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
818         FinalArgs = Args;
819         return Args;
820       };
821   Tool.clearArgumentsAdjusters();
822   Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
823   Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
824   Tool.run(Action.get());
825 
826   auto HasFlag = [&FinalArgs](const std::string &Flag) {
827     return llvm::find(FinalArgs, Flag) != FinalArgs.end();
828   };
829   EXPECT_FALSE(HasFlag("-Xclang"));
830   EXPECT_FALSE(HasFlag("-add-plugin"));
831   EXPECT_FALSE(HasFlag("-random-plugin"));
832 }
833 
834 namespace {
835 /// Find a target name such that looking for it in TargetRegistry by that name
836 /// returns the same target. We expect that there is at least one target
837 /// configured with this property.
getAnyTarget()838 std::string getAnyTarget() {
839   llvm::InitializeAllTargets();
840   for (const auto &Target : llvm::TargetRegistry::targets()) {
841     std::string Error;
842     StringRef TargetName(Target.getName());
843     if (TargetName == "x86-64")
844       TargetName = "x86_64";
845     if (llvm::TargetRegistry::lookupTarget(std::string(TargetName), Error) ==
846         &Target) {
847       return std::string(TargetName);
848     }
849   }
850   return "";
851 }
852 }
853 
TEST(addTargetAndModeForProgramName,AddsTargetAndMode)854 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
855   std::string Target = getAnyTarget();
856   ASSERT_FALSE(Target.empty());
857 
858   std::vector<std::string> Args = {"clang", "-foo"};
859   addTargetAndModeForProgramName(Args, "");
860   EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
861   addTargetAndModeForProgramName(Args, Target + "-g++");
862   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
863                                       "--driver-mode=g++", "-foo"}),
864             Args);
865 }
866 
TEST(addTargetAndModeForProgramName,PathIgnored)867 TEST(addTargetAndModeForProgramName, PathIgnored) {
868   std::string Target = getAnyTarget();
869   ASSERT_FALSE(Target.empty());
870 
871   SmallString<32> ToolPath;
872   llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
873 
874   std::vector<std::string> Args = {"clang", "-foo"};
875   addTargetAndModeForProgramName(Args, ToolPath);
876   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
877                                       "--driver-mode=g++", "-foo"}),
878             Args);
879 }
880 
TEST(addTargetAndModeForProgramName,IgnoresExistingTarget)881 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
882   std::string Target = getAnyTarget();
883   ASSERT_FALSE(Target.empty());
884 
885   std::vector<std::string> Args = {"clang", "-foo", "-target", "something"};
886   addTargetAndModeForProgramName(Args, Target + "-g++");
887   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
888                                       "-target", "something"}),
889             Args);
890 
891   std::vector<std::string> ArgsAlt = {"clang", "-foo", "--target=something"};
892   addTargetAndModeForProgramName(ArgsAlt, Target + "-g++");
893   EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
894                                       "--target=something"}),
895             ArgsAlt);
896 }
897 
TEST(addTargetAndModeForProgramName,IgnoresExistingMode)898 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
899   std::string Target = getAnyTarget();
900   ASSERT_FALSE(Target.empty());
901 
902   std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"};
903   addTargetAndModeForProgramName(Args, Target + "-g++");
904   EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo",
905                                       "--driver-mode=abc"}),
906             Args);
907 }
908 
909 #ifndef _WIN32
TEST(ClangToolTest,BuildASTs)910 TEST(ClangToolTest, BuildASTs) {
911   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
912 
913   std::vector<std::string> Sources;
914   Sources.push_back("/a.cc");
915   Sources.push_back("/b.cc");
916   ClangTool Tool(Compilations, Sources);
917 
918   Tool.mapVirtualFile("/a.cc", "void a() {}");
919   Tool.mapVirtualFile("/b.cc", "void b() {}");
920 
921   std::vector<std::unique_ptr<ASTUnit>> ASTs;
922   EXPECT_EQ(0, Tool.buildASTs(ASTs));
923   EXPECT_EQ(2u, ASTs.size());
924 }
925 
TEST(ClangToolTest,InjectDiagnosticConsumer)926 TEST(ClangToolTest, InjectDiagnosticConsumer) {
927   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
928   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
929   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
930   TestDiagnosticConsumer Consumer;
931   Tool.setDiagnosticConsumer(&Consumer);
932   std::unique_ptr<FrontendActionFactory> Action(
933       newFrontendActionFactory<SyntaxOnlyAction>());
934   Tool.run(Action.get());
935   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
936 }
937 
TEST(ClangToolTest,InjectDiagnosticConsumerInBuildASTs)938 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
939   FixedCompilationDatabase Compilations("/", std::vector<std::string>());
940   ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
941   Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
942   TestDiagnosticConsumer Consumer;
943   Tool.setDiagnosticConsumer(&Consumer);
944   std::vector<std::unique_ptr<ASTUnit>> ASTs;
945   Tool.buildASTs(ASTs);
946   EXPECT_EQ(1u, ASTs.size());
947   EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
948 }
949 #endif
950 
TEST(runToolOnCode,TestResetDiagnostics)951 TEST(runToolOnCode, TestResetDiagnostics) {
952   // This is a tool that resets the diagnostic during the compilation.
953   struct ResetDiagnosticAction : public clang::ASTFrontendAction {
954     std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
955                                                    StringRef) override {
956       struct Consumer : public clang::ASTConsumer {
957         bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
958           auto &Diags = (*D.begin())->getASTContext().getDiagnostics();
959           // Ignore any error
960           Diags.Reset();
961           // Disable warnings because computing the CFG might crash.
962           Diags.setIgnoreAllWarnings(true);
963           return true;
964         }
965       };
966       return std::make_unique<Consumer>();
967     }
968   };
969 
970   // Should not crash
971   EXPECT_FALSE(
972       runToolOnCode(std::make_unique<ResetDiagnosticAction>(),
973                     "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };"
974                     "void func() { long x; Foo f(x); }"));
975 }
976 
977 } // end namespace tooling
978 } // end namespace clang
979