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

source code of clang/unittests/Tooling/ToolingTest.cpp