[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang / unittests / Tooling / ToolingTest.cpp
blobaca343a963daea0b6d99bcf018c024c33a5bc786
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 //===----------------------------------------------------------------------===//
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>
33 namespace clang {
34 namespace tooling {
36 namespace {
37 /// Takes an ast consumer and returns it from CreateASTConsumer. This only
38 /// works with single translation unit compilations.
39 class TestAction : public clang::ASTFrontendAction {
40 public:
41 /// Takes ownership of TestConsumer.
42 explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer)
43 : TestConsumer(std::move(TestConsumer)) {}
45 protected:
46 std::unique_ptr<clang::ASTConsumer>
47 CreateASTConsumer(clang::CompilerInstance &compiler,
48 StringRef dummy) override {
49 /// TestConsumer will be deleted by the framework calling us.
50 return std::move(TestConsumer);
53 private:
54 std::unique_ptr<clang::ASTConsumer> TestConsumer;
57 class FindTopLevelDeclConsumer : public clang::ASTConsumer {
58 public:
59 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
60 : FoundTopLevelDecl(FoundTopLevelDecl) {}
61 bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override {
62 *FoundTopLevelDecl = true;
63 return true;
65 private:
66 bool * const FoundTopLevelDecl;
68 } // end namespace
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);
79 namespace {
80 class FindClassDeclXConsumer : public clang::ASTConsumer {
81 public:
82 FindClassDeclXConsumer(bool *FoundClassDeclX)
83 : FoundClassDeclX(FoundClassDeclX) {}
84 bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override {
85 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
86 *GroupRef.begin())) {
87 if (Record->getName() == "X") {
88 *FoundClassDeclX = true;
91 return true;
93 private:
94 bool *FoundClassDeclX;
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;
106 return false;
109 struct TestDiagnosticConsumer : public DiagnosticConsumer {
110 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
111 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
112 const Diagnostic &Info) override {
113 ++NumDiagnosticsSeen;
115 unsigned NumDiagnosticsSeen;
117 } // end namespace
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);
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);
135 TEST(buildASTFromCode, FindsClassDecl) {
136 std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;");
137 ASSERT_TRUE(AST.get());
138 EXPECT_TRUE(FindClassDeclX(AST.get()));
140 AST = buildASTFromCode("class Y;");
141 ASSERT_TRUE(AST.get());
142 EXPECT_FALSE(FindClassDeclX(AST.get()));
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);
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);
162 struct IndependentFrontendActionCreator {
163 std::unique_ptr<ASTConsumer> newASTConsumer() {
164 return std::make_unique<FindTopLevelDeclConsumer>(nullptr);
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);
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());
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());
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));
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");
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);
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));
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");
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);
282 // Inject custom `DiagnosticOptions` for command-line parsing.
283 auto DiagOpts = llvm::makeIntrusiveRefCnt<DiagnosticOptions>();
284 Invocation.setDiagnosticOptions(&*DiagOpts);
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);
292 struct DiagnosticConsumerExpectingSourceManager : public DiagnosticConsumer {
293 bool SawSourceManager;
295 DiagnosticConsumerExpectingSourceManager() : SawSourceManager(false) {}
297 void HandleDiagnostic(clang::DiagnosticsEngine::Level,
298 const clang::Diagnostic &info) override {
299 SawSourceManager = info.hasSourceManager();
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"));
322 DiagnosticConsumerExpectingSourceManager Consumer;
323 Invocation.setDiagnosticConsumer(&Consumer);
325 EXPECT_TRUE(Invocation.run());
326 EXPECT_TRUE(Consumer.SawSourceManager);
329 namespace {
330 /// Overlays the real filesystem with the given VFS and returns the result.
331 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
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;
339 struct CommandLineExtractorTest : public ::testing::Test {
340 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFS;
341 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
342 driver::Driver Driver;
344 public:
345 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)) {}
351 void addFile(StringRef Name, StringRef Content) {
352 InMemoryFS->addFile(Name, 0, llvm::MemoryBuffer::getMemBuffer(Content));
355 const llvm::opt::ArgStringList *
356 extractCC1Arguments(llvm::ArrayRef<const char *> Argv) {
357 const std::unique_ptr<driver::Compilation> Compilation(
358 Driver.BuildCompilation(llvm::makeArrayRef(Argv)));
360 return getCC1Arguments(Diags.get(), Compilation.get());
363 } // namespace
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);
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);
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);
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);
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);
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);
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);
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);
428 struct VerifyEndCallback : public SourceFileCallbacks {
429 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
430 bool handleBeginSource(CompilerInstance &CI) override {
431 ++BeginCalled;
432 return true;
434 void handleEndSource() override { ++EndCalled; }
435 std::unique_ptr<ASTConsumer> newASTConsumer() {
436 return std::make_unique<FindTopLevelDeclConsumer>(&Matched);
438 unsigned BeginCalled;
439 unsigned EndCalled;
440 bool Matched;
443 #if !defined(_WIN32)
444 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
445 VerifyEndCallback EndCallback;
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);
453 Tool.mapVirtualFile("/a.cc", "void a() {}");
454 Tool.mapVirtualFile("/b.cc", "void b() {}");
456 std::unique_ptr<FrontendActionFactory> Action(
457 newFrontendActionFactory(&EndCallback, &EndCallback));
458 Tool.run(Action.get());
460 EXPECT_TRUE(EndCallback.Matched);
461 EXPECT_EQ(2u, EndCallback.BeginCalled);
462 EXPECT_EQ(2u, EndCallback.EndCalled);
464 #endif
466 struct SkipBodyConsumer : public clang::ASTConsumer {
467 /// Skip the 'skipMe' function.
468 bool shouldSkipFunctionBody(Decl *D) override {
469 NamedDecl *F = dyn_cast<NamedDecl>(D);
470 return F && F->getNameAsString() == "skipMe";
474 struct SkipBodyAction : public clang::ASTFrontendAction {
475 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
476 StringRef) override {
477 Compiler.getFrontendOpts().SkipFunctionBodies = true;
478 return std::make_unique<SkipBodyConsumer>();
482 TEST(runToolOnCode, TestSkipFunctionBody) {
483 std::vector<std::string> Args = {"-std=c++11"};
484 std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
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 }"));
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));
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));
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
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));
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()));
559 struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction {
560 CheckColoredDiagnosticsAction(bool ShouldShowColor)
561 : ShouldShowColor(ShouldShowColor) {}
562 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>();
572 private:
573 bool ShouldShowColor = true;
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"}));
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"}));
599 TEST(ClangToolTest, ArgumentAdjusters) {
600 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
602 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
603 Tool.mapVirtualFile("/a.cc", "void a() {}");
605 std::unique_ptr<FrontendActionFactory> Action(
606 newFrontendActionFactory<SyntaxOnlyAction>());
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;
617 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
618 Tool.run(Action.get());
619 EXPECT_TRUE(Ran);
620 EXPECT_TRUE(Found);
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);
631 TEST(ClangToolTest, NoDoubleSyntaxOnly) {
632 FixedCompilationDatabase Compilations("/", {"-fsyntax-only"});
634 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
635 Tool.mapVirtualFile("/a.cc", "void a() {}");
637 std::unique_ptr<FrontendActionFactory> Action(
638 newFrontendActionFactory<SyntaxOnlyAction>());
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;
648 return Args;
651 Tool.clearArgumentsAdjusters();
652 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
653 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
654 Tool.run(Action.get());
655 EXPECT_EQ(SyntaxOnlyCount, 1U);
658 TEST(ClangToolTest, NoOutputCommands) {
659 FixedCompilationDatabase Compilations("/", {"-save-temps", "-save-temps=cwd",
660 "--save-temps",
661 "--save-temps=somedir"});
663 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
664 Tool.mapVirtualFile("/a.cc", "void a() {}");
666 std::unique_ptr<FrontendActionFactory> Action(
667 newFrontendActionFactory<SyntaxOnlyAction>());
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));
678 Ran = true;
679 return Args;
682 Tool.clearArgumentsAdjusters();
683 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
684 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster);
685 Tool.run(Action.get());
686 EXPECT_TRUE(Ran);
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);
697 InMemoryFileSystem->addFile(
698 "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}"));
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()));
707 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD.
708 TEST(ClangToolTest, StripDependencyFileAdjuster) {
709 FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"});
711 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
712 Tool.mapVirtualFile("/a.cc", "void a() {}");
714 std::unique_ptr<FrontendActionFactory> Action(
715 newFrontendActionFactory<SyntaxOnlyAction>());
717 CommandLineArguments FinalArgs;
718 ArgumentsAdjuster CheckFlagsAdjuster =
719 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
720 FinalArgs = Args;
721 return Args;
723 Tool.clearArgumentsAdjusters();
724 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
725 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
726 Tool.run(Action.get());
728 auto HasFlag = [&FinalArgs](const std::string &Flag) {
729 return llvm::find(FinalArgs, Flag) != FinalArgs.end();
731 EXPECT_FALSE(HasFlag("-MD"));
732 EXPECT_FALSE(HasFlag("-MMD"));
733 EXPECT_TRUE(HasFlag("-c"));
734 EXPECT_TRUE(HasFlag("-w"));
737 // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants
738 TEST(ClangToolTest, StripDependencyFileAdjusterShowIncludes) {
739 FixedCompilationDatabase Compilations(
740 "/", {"/showIncludes", "/showIncludes:user", "-showIncludes",
741 "-showIncludes:user", "-c"});
743 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
744 Tool.mapVirtualFile("/a.cc", "void a() {}");
746 std::unique_ptr<FrontendActionFactory> Action(
747 newFrontendActionFactory<SyntaxOnlyAction>());
749 CommandLineArguments FinalArgs;
750 ArgumentsAdjuster CheckFlagsAdjuster =
751 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
752 FinalArgs = Args;
753 return Args;
755 Tool.clearArgumentsAdjusters();
756 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
757 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
758 Tool.run(Action.get());
760 auto HasFlag = [&FinalArgs](const std::string &Flag) {
761 return llvm::find(FinalArgs, Flag) != FinalArgs.end();
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"));
770 // Check getClangStripDependencyFileAdjuster doesn't strip args when using the
771 // MSVC cl.exe driver
772 TEST(ClangToolTest, StripDependencyFileAdjusterMsvc) {
773 FixedCompilationDatabase Compilations(
774 "/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"});
776 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
777 Tool.mapVirtualFile("/a.cc", "void a() {}");
779 std::unique_ptr<FrontendActionFactory> Action(
780 newFrontendActionFactory<SyntaxOnlyAction>());
782 CommandLineArguments FinalArgs;
783 ArgumentsAdjuster CheckFlagsAdjuster =
784 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
785 FinalArgs = Args;
786 return Args;
788 Tool.clearArgumentsAdjusters();
789 Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
790 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
791 Tool.run(Action.get());
793 auto HasFlag = [&FinalArgs](const std::string &Flag) {
794 return llvm::find(FinalArgs, Flag) != FinalArgs.end();
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"));
804 // Check getClangStripPluginsAdjuster strips plugin related args.
805 TEST(ClangToolTest, StripPluginsAdjuster) {
806 FixedCompilationDatabase Compilations(
807 "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"});
809 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
810 Tool.mapVirtualFile("/a.cc", "void a() {}");
812 std::unique_ptr<FrontendActionFactory> Action(
813 newFrontendActionFactory<SyntaxOnlyAction>());
815 CommandLineArguments FinalArgs;
816 ArgumentsAdjuster CheckFlagsAdjuster =
817 [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) {
818 FinalArgs = Args;
819 return Args;
821 Tool.clearArgumentsAdjusters();
822 Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
823 Tool.appendArgumentsAdjuster(CheckFlagsAdjuster);
824 Tool.run(Action.get());
826 auto HasFlag = [&FinalArgs](const std::string &Flag) {
827 return llvm::find(FinalArgs, Flag) != FinalArgs.end();
829 EXPECT_FALSE(HasFlag("-Xclang"));
830 EXPECT_FALSE(HasFlag("-add-plugin"));
831 EXPECT_FALSE(HasFlag("-random-plugin"));
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.
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);
850 return "";
854 TEST(addTargetAndModeForProgramName, AddsTargetAndMode) {
855 std::string Target = getAnyTarget();
856 ASSERT_FALSE(Target.empty());
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);
867 TEST(addTargetAndModeForProgramName, PathIgnored) {
868 std::string Target = getAnyTarget();
869 ASSERT_FALSE(Target.empty());
871 SmallString<32> ToolPath;
872 llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++");
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);
881 TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) {
882 std::string Target = getAnyTarget();
883 ASSERT_FALSE(Target.empty());
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);
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);
898 TEST(addTargetAndModeForProgramName, IgnoresExistingMode) {
899 std::string Target = getAnyTarget();
900 ASSERT_FALSE(Target.empty());
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);
909 #ifndef _WIN32
910 TEST(ClangToolTest, BuildASTs) {
911 FixedCompilationDatabase Compilations("/", std::vector<std::string>());
913 std::vector<std::string> Sources;
914 Sources.push_back("/a.cc");
915 Sources.push_back("/b.cc");
916 ClangTool Tool(Compilations, Sources);
918 Tool.mapVirtualFile("/a.cc", "void a() {}");
919 Tool.mapVirtualFile("/b.cc", "void b() {}");
921 std::vector<std::unique_ptr<ASTUnit>> ASTs;
922 EXPECT_EQ(0, Tool.buildASTs(ASTs));
923 EXPECT_EQ(2u, ASTs.size());
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);
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);
949 #endif
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;
966 return std::make_unique<Consumer>();
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); }"));
977 } // end namespace tooling
978 } // end namespace clang