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(buildASTFromCode, FileSystem) {
156 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
157 new llvm::vfs::InMemoryFileSystem);
158 InMemoryFileSystem->addFile(Path: "included_file.h", ModificationTime: 0,
159 Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: "class X;"));
160 std::unique_ptr<ASTUnit> AST = buildASTFromCodeWithArgs(
161 Code: R"(#include "included_file.h")", Args: {}, FileName: "input.cc", ToolName: "clang-tool",
162 PCHContainerOps: std::make_shared<PCHContainerOperations>(),
163 Adjuster: getClangStripDependencyFileAdjuster(), VirtualMappedFiles: FileContentMappings(), DiagConsumer: nullptr,
164 BaseFS: InMemoryFileSystem);
165 ASSERT_TRUE(AST.get());
166 EXPECT_TRUE(FindClassDeclX(AST.get()));
167}
168
169TEST(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
176struct IndependentFrontendActionCreator {
177 std::unique_ptr<ASTConsumer> newASTConsumer() {
178 return std::make_unique<FindTopLevelDeclConsumer>(args: nullptr);
179 }
180};
181
182TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
183 IndependentFrontendActionCreator Creator;
184 std::unique_ptr<FrontendActionFactory> Factory(
185 newFrontendActionFactory(ConsumerFactory: &Creator));
186 std::unique_ptr<FrontendAction> Action(Factory->create());
187 EXPECT_TRUE(Action.get() != nullptr);
188}
189
190TEST(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(FS: InMemoryFileSystem);
196 llvm::IntrusiveRefCntPtr<FileManager> Files(
197 new FileManager(FileSystemOptions(), OverlayFileSystem));
198 std::vector<std::string> Args;
199 Args.push_back(x: "tool-executable");
200 Args.push_back(x: "-Idef");
201 Args.push_back(x: "-fsyntax-only");
202 Args.push_back(x: "test.cpp");
203 clang::tooling::ToolInvocation Invocation(
204 Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
205 InMemoryFileSystem->addFile(
206 Path: "test.cpp", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "#include <abc>\n"));
207 InMemoryFileSystem->addFile(Path: "def/abc", ModificationTime: 0,
208 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n"));
209 EXPECT_TRUE(Invocation.run());
210}
211
212TEST(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(FS: InMemoryFileSystem);
222 llvm::IntrusiveRefCntPtr<FileManager> Files(
223 new FileManager(FileSystemOptions(), OverlayFileSystem));
224 std::vector<std::string> Args;
225 Args.push_back(x: "tool-executable");
226 Args.push_back(x: "-Idef");
227 Args.push_back(x: "-fsyntax-only");
228 Args.push_back(x: "test.cpp");
229 clang::tooling::ToolInvocation Invocation(
230 Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
231 InMemoryFileSystem->addFile(
232 Path: "test.cpp", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "#include <abc>\n"));
233 InMemoryFileSystem->addFile(Path: "def/abc", ModificationTime: 0,
234 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\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(Path: "def/module.modulemap", ModificationTime: 0,
238 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "\n"));
239 EXPECT_TRUE(Invocation.run());
240}
241
242TEST(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(FS: InMemoryFileSystem);
248 llvm::IntrusiveRefCntPtr<FileManager> Files(
249 new FileManager(FileSystemOptions(), OverlayFileSystem));
250
251 std::vector<std::string> Args;
252 Args.push_back(x: "tool-executable");
253 // Unknown warning option will result in a warning.
254 Args.push_back(x: "-fexpensive-optimizations");
255 // Argument that will suppress the warning above.
256 Args.push_back(x: "-Wno-ignored-optimization-argument");
257 Args.push_back(x: "-E");
258 Args.push_back(x: "test.cpp");
259
260 clang::tooling::ToolInvocation Invocation(
261 Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
262 InMemoryFileSystem->addFile(Path: "test.cpp", ModificationTime: 0,
263 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: ""));
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
271TEST(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(FS: InMemoryFileSystem);
277 llvm::IntrusiveRefCntPtr<FileManager> Files(
278 new FileManager(FileSystemOptions(), OverlayFileSystem));
279
280 std::vector<std::string> Args;
281 Args.push_back(x: "tool-executable");
282 // Unknown warning option will result in a warning.
283 Args.push_back(x: "-fexpensive-optimizations");
284 // Argument that will suppress the warning above.
285 Args.push_back(x: "-Wno-ignored-optimization-argument");
286 Args.push_back(x: "-E");
287 Args.push_back(x: "test.cpp");
288
289 clang::tooling::ToolInvocation Invocation(
290 Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
291 InMemoryFileSystem->addFile(Path: "test.cpp", ModificationTime: 0,
292 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: ""));
293 TextDiagnosticBuffer Consumer;
294 Invocation.setDiagnosticConsumer(&Consumer);
295
296 // Inject custom `DiagnosticOptions` for command-line parsing.
297 DiagnosticOptions DiagOpts;
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
306struct 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
317TEST(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(FS: InMemoryFileSystem);
323 llvm::IntrusiveRefCntPtr<FileManager> Files(
324 new FileManager(FileSystemOptions(), OverlayFileSystem));
325 std::vector<std::string> Args;
326 Args.push_back(x: "tool-executable");
327 // Note: intentional error; user probably meant -ferror-limit=0.
328 Args.push_back(x: "-ferror-limit=-1");
329 Args.push_back(x: "-fsyntax-only");
330 Args.push_back(x: "test.cpp");
331 clang::tooling::ToolInvocation Invocation(
332 Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
333 InMemoryFileSystem->addFile(
334 Path: "test.cpp", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "int main() {}\n"));
335
336 DiagnosticConsumerExpectingSourceManager Consumer;
337 Invocation.setDiagnosticConsumer(&Consumer);
338
339 EXPECT_TRUE(Invocation.run());
340 EXPECT_TRUE(Consumer.SawSourceManager);
341}
342
343TEST(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(FS: InMemoryFileSystem);
349 llvm::IntrusiveRefCntPtr<FileManager> Files(
350 new FileManager(FileSystemOptions(), OverlayFileSystem));
351 std::vector<std::string> Args;
352 Args.push_back(x: "tool-executable");
353 Args.push_back(x: "-cc1");
354 Args.push_back(x: "-fsyntax-only");
355 Args.push_back(x: "test.cpp");
356 clang::tooling::ToolInvocation Invocation(
357 Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
358 InMemoryFileSystem->addFile(
359 Path: "test.cpp", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "void foo(void);\n"));
360 EXPECT_TRUE(Invocation.run());
361}
362
363TEST(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(FS: InMemoryFileSystem);
369 llvm::IntrusiveRefCntPtr<FileManager> Files(
370 new FileManager(FileSystemOptions(), OverlayFileSystem));
371 std::vector<std::string> Args;
372 Args.push_back(x: "tool-executable");
373 Args.push_back(x: "-cc1");
374 Args.push_back(x: "-invalid-arg");
375 Args.push_back(x: "test.cpp");
376 clang::tooling::ToolInvocation Invocation(
377 Args, std::make_unique<SyntaxOnlyAction>(), Files.get());
378 InMemoryFileSystem->addFile(
379 Path: "test.cpp", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "void foo(void);\n"));
380 EXPECT_FALSE(Invocation.run());
381}
382
383namespace {
384/// Overlays the real filesystem with the given VFS and returns the result.
385llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
386overlayRealFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
387 auto RFS = llvm::vfs::getRealFileSystem();
388 auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(A&: RFS);
389 OverlayFS->pushOverlay(FS: VFS);
390 return OverlayFS;
391}
392
393struct CommandLineExtractorTest : public ::testing::Test {
394 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS;
395 DiagnosticOptions DiagOpts;
396 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
397 driver::Driver Driver;
398
399public:
400 CommandLineExtractorTest()
401 : InMemoryFS(new llvm::vfs::InMemoryFileSystem),
402 Diags(CompilerInstance::createDiagnostics(VFS&: *InMemoryFS, Opts&: DiagOpts)),
403 Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags,
404 "clang LLVM compiler", overlayRealFS(VFS: InMemoryFS)) {}
405
406 void addFile(StringRef Name, StringRef Content) {
407 InMemoryFS->addFile(Path: Name, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: 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(Args: llvm::ArrayRef(Argv)));
414
415 return getCC1Arguments(Diagnostics: Diags.get(), Compilation: Compilation.get());
416 }
417};
418} // namespace
419
420TEST_F(CommandLineExtractorTest, AcceptOffloading) {
421 addFile(Name: "test.c", Content: "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
428TEST_F(CommandLineExtractorTest, AcceptOffloadingCompile) {
429 addFile(Name: "test.c", Content: "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
436TEST_F(CommandLineExtractorTest, AcceptOffloadingSyntaxOnly) {
437 addFile(Name: "test.c", Content: "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
445TEST_F(CommandLineExtractorTest, AcceptExternalAssembler) {
446 addFile(Name: "test.c", Content: "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
453TEST_F(CommandLineExtractorTest, AcceptEmbedBitcode) {
454 addFile(Name: "test.c", Content: "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
460TEST_F(CommandLineExtractorTest, AcceptSaveTemps) {
461 addFile(Name: "test.c", Content: "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
467TEST_F(CommandLineExtractorTest, AcceptPreprocessedInputFile) {
468 addFile(Name: "test.i", Content: "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
474TEST_F(CommandLineExtractorTest, RejectMultipleArchitectures) {
475 addFile(Name: "test.c", Content: "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
482TEST_F(CommandLineExtractorTest, RejectMultipleInputFiles) {
483 addFile(Name: "one.c", Content: "void one() {}\n");
484 addFile(Name: "two.c", Content: "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
490struct 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>(args: &Matched);
499 }
500 unsigned BeginCalled;
501 unsigned EndCalled;
502 bool Matched;
503};
504
505#if !defined(_WIN32)
506TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
507 VerifyEndCallback EndCallback;
508
509 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
510 std::vector<std::string> Sources;
511 Sources.push_back(x: "/a.cc");
512 Sources.push_back(x: "/b.cc");
513 ClangTool Tool(Compilations, Sources);
514
515 Tool.mapVirtualFile(FilePath: "/a.cc", Content: "void a() {}");
516 Tool.mapVirtualFile(FilePath: "/b.cc", Content: "void b() {}");
517
518 std::unique_ptr<FrontendActionFactory> Action(
519 newFrontendActionFactory(ConsumerFactory: &EndCallback, Callbacks: &EndCallback));
520 Tool.run(Action: Action.get());
521
522 EXPECT_TRUE(EndCallback.Matched);
523 EXPECT_EQ(2u, EndCallback.BeginCalled);
524 EXPECT_EQ(2u, EndCallback.EndCalled);
525}
526#endif
527
528struct SkipBodyConsumer : public clang::ASTConsumer {
529 /// Skip the 'skipMe' function.
530 bool shouldSkipFunctionBody(Decl *D) override {
531 NamedDecl *F = dyn_cast<NamedDecl>(Val: D);
532 return F && F->getNameAsString() == "skipMe";
533 }
534};
535
536struct 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
544TEST(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
611TEST(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(x: "-MMD");
617 Args.push_back(x: "-MT");
618 Args.push_back(x: std::string(DepFilePath.str()));
619 Args.push_back(x: "-MF");
620 Args.push_back(x: 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
626struct 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 DiagID: Compiler.getDiagnostics().getCustomDiagID(
634 L: DiagnosticsEngine::Fatal,
635 FormatString: "getDiagnosticOpts().ShowColors != ShouldShowColor"));
636 return std::make_unique<ASTConsumer>();
637 }
638
639private:
640 bool ShouldShowColor = true;
641};
642
643TEST(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
666TEST(ClangToolTest, ArgumentAdjusters) {
667 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
668
669 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
670 Tool.mapVirtualFile(FilePath: "/a.cc", Content: "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(Range: Args, Element: "-fsyntax-only"))
681 Found = true;
682 return Args;
683 };
684 Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster);
685 Tool.run(Action: Action.get());
686 EXPECT_TRUE(Ran);
687 EXPECT_TRUE(Found);
688
689 Ran = Found = false;
690 Tool.clearArgumentsAdjusters();
691 Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster);
692 Tool.appendArgumentsAdjuster(Adjuster: getClangSyntaxOnlyAdjuster());
693 Tool.run(Action: Action.get());
694 EXPECT_TRUE(Ran);
695 EXPECT_FALSE(Found);
696}
697
698TEST(ClangToolTest, NoDoubleSyntaxOnly) {
699 FixedCompilationDatabase Compilations("/", {"-fsyntax-only"});
700
701 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
702 Tool.mapVirtualFile(FilePath: "/a.cc", Content: "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(Adjuster: getClangSyntaxOnlyAdjuster());
720 Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster);
721 Tool.run(Action: Action.get());
722 EXPECT_EQ(SyntaxOnlyCount, 1U);
723}
724
725TEST(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(FilePath: "/a.cc", Content: "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(Adjuster: getClangSyntaxOnlyAdjuster());
751 Tool.appendArgumentsAdjuster(Adjuster: CheckSyntaxOnlyAdjuster);
752 Tool.run(Action: Action.get());
753 EXPECT_TRUE(Ran);
754}
755
756TEST(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(FS: InMemoryFileSystem);
763
764 InMemoryFileSystem->addFile(
765 Path: "a.cpp", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "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.
775TEST(ClangToolTest, StripDependencyFileAdjuster) {
776 FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"});
777
778 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
779 Tool.mapVirtualFile(FilePath: "/a.cc", Content: "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(Adjuster: getClangStripDependencyFileAdjuster());
792 Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster);
793 Tool.run(Action: Action.get());
794
795 auto HasFlag = [&FinalArgs](const std::string &Flag) {
796 return llvm::is_contained(Range&: FinalArgs, Element: Flag);
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
805TEST(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(FilePath: "/a.cc", Content: "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(Adjuster: getClangStripDependencyFileAdjuster());
824 Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster);
825 Tool.run(Action: Action.get());
826
827 auto HasFlag = [&FinalArgs](const std::string &Flag) {
828 return llvm::is_contained(Range&: FinalArgs, Element: Flag);
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
839TEST(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(FilePath: "/a.cc", Content: "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(Adjuster: getClangStripDependencyFileAdjuster());
857 Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster);
858 Tool.run(Action: Action.get());
859
860 auto HasFlag = [&FinalArgs](const std::string &Flag) {
861 return llvm::is_contained(Range&: FinalArgs, Element: Flag);
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.
872TEST(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(FilePath: "/a.cc", Content: "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(Adjuster: getStripPluginsAdjuster());
890 Tool.appendArgumentsAdjuster(Adjuster: CheckFlagsAdjuster);
891 Tool.run(Action: Action.get());
892
893 auto HasFlag = [&FinalArgs](const std::string &Flag) {
894 return llvm::is_contained(Range&: FinalArgs, Element: Flag);
895 };
896 EXPECT_FALSE(HasFlag("-Xclang"));
897 EXPECT_FALSE(HasFlag("-add-plugin"));
898 EXPECT_FALSE(HasFlag("-random-plugin"));
899}
900
901TEST(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(CommandLine&: Args, InvokedAs: "");
909 EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args);
910 addTargetAndModeForProgramName(CommandLine&: Args, InvokedAs: Target + "-g++");
911 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
912 "--driver-mode=g++", "-foo"}),
913 Args);
914}
915
916TEST(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(path&: ToolPath, a: "foo", b: "bar", c: Target + "-g++");
923
924 std::vector<std::string> Args = {"clang", "-foo"};
925 addTargetAndModeForProgramName(CommandLine&: Args, InvokedAs: ToolPath);
926 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target,
927 "--driver-mode=g++", "-foo"}),
928 Args);
929}
930
931TEST(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(CommandLine&: Args, InvokedAs: 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(CommandLine&: ArgsAlt, InvokedAs: Target + "-g++");
944 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo",
945 "--target=something"}),
946 ArgsAlt);
947}
948
949TEST(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(CommandLine&: Args, InvokedAs: Target + "-g++");
956 EXPECT_EQ((std::vector<std::string>{"clang", "--target=" + Target, "-foo",
957 "--driver-mode=abc"}),
958 Args);
959}
960
961#ifndef _WIN32
962TEST(ClangToolTest, BuildASTs) {
963 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
964
965 std::vector<std::string> Sources;
966 Sources.push_back(x: "/a.cc");
967 Sources.push_back(x: "/b.cc");
968 ClangTool Tool(Compilations, Sources);
969
970 Tool.mapVirtualFile(FilePath: "/a.cc", Content: "void a() {}");
971 Tool.mapVirtualFile(FilePath: "/b.cc", Content: "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
978TEST(ClangToolTest, InjectDiagnosticConsumer) {
979 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
980 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
981 Tool.mapVirtualFile(FilePath: "/a.cc", Content: "int x = undeclared;");
982 TestDiagnosticConsumer Consumer;
983 Tool.setDiagnosticConsumer(&Consumer);
984 std::unique_ptr<FrontendActionFactory> Action(
985 newFrontendActionFactory<SyntaxOnlyAction>());
986 Tool.run(Action: Action.get());
987 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
988}
989
990TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
991 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
992 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
993 Tool.mapVirtualFile(FilePath: "/a.cc", Content: "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
1003TEST(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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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