1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
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
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/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"
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
{
41 /// Takes ownership of TestConsumer.
42 explicit TestAction(std::unique_ptr
<clang::ASTConsumer
> TestConsumer
)
43 : TestConsumer(std::move(TestConsumer
)) {}
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
);
54 std::unique_ptr
<clang::ASTConsumer
> TestConsumer
;
57 class FindTopLevelDeclConsumer
: public clang::ASTConsumer
{
59 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl
)
60 : FoundTopLevelDecl(FoundTopLevelDecl
) {}
61 bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup
) override
{
62 *FoundTopLevelDecl
= true;
66 bool * const FoundTopLevelDecl
;
70 TEST(runToolOnCode
, FindsNoTopLevelDeclOnEmptyCode
) {
71 bool FoundTopLevelDecl
= false;
72 EXPECT_TRUE(runToolOnCode(
73 std::make_unique
<TestAction
>(
74 std::make_unique
<FindTopLevelDeclConsumer
>(&FoundTopLevelDecl
)),
76 EXPECT_FALSE(FoundTopLevelDecl
);
80 class FindClassDeclXConsumer
: public clang::ASTConsumer
{
82 FindClassDeclXConsumer(bool *FoundClassDeclX
)
83 : FoundClassDeclX(FoundClassDeclX
) {}
84 bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef
) override
{
85 if (CXXRecordDecl
* Record
= dyn_cast
<clang::CXXRecordDecl
>(
87 if (Record
->getName() == "X") {
88 *FoundClassDeclX
= true;
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();
100 if (CXXRecordDecl
* Record
= dyn_cast
<clang::CXXRecordDecl
>(*i
)) {
101 if (Record
->getName() == "X") {
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
;
119 TEST(runToolOnCode
, FindsClassDecl
) {
120 bool FoundClassDeclX
= false;
121 EXPECT_TRUE(runToolOnCode(
122 std::make_unique
<TestAction
>(
123 std::make_unique
<FindClassDeclXConsumer
>(&FoundClassDeclX
)),
125 EXPECT_TRUE(FoundClassDeclX
);
127 FoundClassDeclX
= false;
128 EXPECT_TRUE(runToolOnCode(
129 std::make_unique
<TestAction
>(
130 std::make_unique
<FindClassDeclXConsumer
>(&FoundClassDeclX
)),
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.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(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.modulemap file in the include directory of our header, so we
222 // trigger the module.modulemap header search logic.
223 InMemoryFileSystem
->addFile("def/module.modulemap", 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 TEST(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(InMemoryFileSystem
);
335 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
336 new FileManager(FileSystemOptions(), OverlayFileSystem
));
337 std::vector
<std::string
> Args
;
338 Args
.push_back("tool-executable");
339 Args
.push_back("-cc1");
340 Args
.push_back("-fsyntax-only");
341 Args
.push_back("test.cpp");
342 clang::tooling::ToolInvocation
Invocation(
343 Args
, std::make_unique
<SyntaxOnlyAction
>(), Files
.get());
344 InMemoryFileSystem
->addFile(
345 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("void foo(void);\n"));
346 EXPECT_TRUE(Invocation
.run());
349 TEST(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(InMemoryFileSystem
);
355 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
356 new FileManager(FileSystemOptions(), OverlayFileSystem
));
357 std::vector
<std::string
> Args
;
358 Args
.push_back("tool-executable");
359 Args
.push_back("-cc1");
360 Args
.push_back("-invalid-arg");
361 Args
.push_back("test.cpp");
362 clang::tooling::ToolInvocation
Invocation(
363 Args
, std::make_unique
<SyntaxOnlyAction
>(), Files
.get());
364 InMemoryFileSystem
->addFile(
365 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("void foo(void);\n"));
366 EXPECT_FALSE(Invocation
.run());
370 /// Overlays the real filesystem with the given VFS and returns the result.
371 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
>
372 overlayRealFS(llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> VFS
) {
373 auto RFS
= llvm::vfs::getRealFileSystem();
374 auto OverlayFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::OverlayFileSystem
>(RFS
);
375 OverlayFS
->pushOverlay(VFS
);
379 struct CommandLineExtractorTest
: public ::testing::Test
{
380 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFS
;
381 llvm::IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
;
382 driver::Driver Driver
;
385 CommandLineExtractorTest()
386 : InMemoryFS(new llvm::vfs::InMemoryFileSystem
),
387 Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions
)),
388 Driver("clang", llvm::sys::getDefaultTargetTriple(), *Diags
,
389 "clang LLVM compiler", overlayRealFS(InMemoryFS
)) {}
391 void addFile(StringRef Name
, StringRef Content
) {
392 InMemoryFS
->addFile(Name
, 0, llvm::MemoryBuffer::getMemBuffer(Content
));
395 const llvm::opt::ArgStringList
*
396 extractCC1Arguments(llvm::ArrayRef
<const char *> Argv
) {
397 const std::unique_ptr
<driver::Compilation
> Compilation(
398 Driver
.BuildCompilation(llvm::ArrayRef(Argv
)));
400 return getCC1Arguments(Diags
.get(), Compilation
.get());
405 TEST_F(CommandLineExtractorTest
, AcceptOffloading
) {
406 addFile("test.c", "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);
413 TEST_F(CommandLineExtractorTest
, AcceptOffloadingCompile
) {
414 addFile("test.c", "int main() {}\n");
415 const char *Args
[] = {"clang", "-target", "arm64-apple-macosx11.0.0",
417 "test.c", "-nogpulib", "-nogpuinc"};
418 EXPECT_NE(extractCC1Arguments(Args
), nullptr);
421 TEST_F(CommandLineExtractorTest
, AcceptOffloadingSyntaxOnly
) {
422 addFile("test.c", "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);
430 TEST_F(CommandLineExtractorTest
, AcceptExternalAssembler
) {
431 addFile("test.c", "int main() {}\n");
432 const char *Args
[] = {
433 "clang", "-target", "arm64-apple-macosx11.0.0", "-fno-integrated-as",
435 EXPECT_NE(extractCC1Arguments(Args
), nullptr);
438 TEST_F(CommandLineExtractorTest
, AcceptEmbedBitcode
) {
439 addFile("test.c", "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);
445 TEST_F(CommandLineExtractorTest
, AcceptSaveTemps
) {
446 addFile("test.c", "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);
452 TEST_F(CommandLineExtractorTest
, AcceptPreprocessedInputFile
) {
453 addFile("test.i", "int main() {}\n");
454 const char *Args
[] = {"clang", "-target", "arm64-apple-macosx11.0.0", "-c",
456 EXPECT_NE(extractCC1Arguments(Args
), nullptr);
459 TEST_F(CommandLineExtractorTest
, RejectMultipleArchitectures
) {
460 addFile("test.c", "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);
467 TEST_F(CommandLineExtractorTest
, RejectMultipleInputFiles
) {
468 addFile("one.c", "void one() {}\n");
469 addFile("two.c", "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);
475 struct VerifyEndCallback
: public SourceFileCallbacks
{
476 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
477 bool handleBeginSource(CompilerInstance
&CI
) override
{
481 void handleEndSource() override
{ ++EndCalled
; }
482 std::unique_ptr
<ASTConsumer
> newASTConsumer() {
483 return std::make_unique
<FindTopLevelDeclConsumer
>(&Matched
);
485 unsigned BeginCalled
;
491 TEST(newFrontendActionFactory
, InjectsSourceFileCallbacks
) {
492 VerifyEndCallback EndCallback
;
494 FixedCompilationDatabase
Compilations("/", std::vector
<std::string
>());
495 std::vector
<std::string
> Sources
;
496 Sources
.push_back("/a.cc");
497 Sources
.push_back("/b.cc");
498 ClangTool
Tool(Compilations
, Sources
);
500 Tool
.mapVirtualFile("/a.cc", "void a() {}");
501 Tool
.mapVirtualFile("/b.cc", "void b() {}");
503 std::unique_ptr
<FrontendActionFactory
> Action(
504 newFrontendActionFactory(&EndCallback
, &EndCallback
));
505 Tool
.run(Action
.get());
507 EXPECT_TRUE(EndCallback
.Matched
);
508 EXPECT_EQ(2u, EndCallback
.BeginCalled
);
509 EXPECT_EQ(2u, EndCallback
.EndCalled
);
513 struct SkipBodyConsumer
: public clang::ASTConsumer
{
514 /// Skip the 'skipMe' function.
515 bool shouldSkipFunctionBody(Decl
*D
) override
{
516 NamedDecl
*F
= dyn_cast
<NamedDecl
>(D
);
517 return F
&& F
->getNameAsString() == "skipMe";
521 struct 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
>();
529 TEST(runToolOnCode
, TestSkipFunctionBody
) {
530 std::vector
<std::string
> Args
= {"-std=c++11"};
531 std::vector
<std::string
> Args2
= {"-fno-delayed-template-parsing"};
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 }"));
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 }",
546 EXPECT_TRUE(runToolOnCodeWithArgs(
547 std::make_unique
<SkipBodyAction
>(), "struct skipMe { skipMe(); };"
548 "skipMe::skipMe() : an_error{[](){;}} { more error }",
550 EXPECT_TRUE(runToolOnCodeWithArgs(
551 std::make_unique
<SkipBodyAction
>(),
552 "struct skipMe { skipMe(); };"
553 "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
555 EXPECT_TRUE(runToolOnCodeWithArgs(
556 std::make_unique
<SkipBodyAction
>(), "struct skipMe { skipMe() : bases()... { error } };",
559 EXPECT_FALSE(runToolOnCodeWithArgs(
560 std::make_unique
<SkipBodyAction
>(), "struct skipMeNot { skipMeNot() : an_error() { } };",
562 EXPECT_FALSE(runToolOnCodeWithArgs(std::make_unique
<SkipBodyAction
>(),
563 "struct skipMeNot { skipMeNot(); };"
564 "skipMeNot::skipMeNot() : an_error() { }",
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 } };"));
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
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
));
590 EXPECT_TRUE(runToolOnCodeWithArgs(
591 std::make_unique
<SkipBodyAction
>(),
592 "__inline __attribute__((__gnu_inline__)) void skipMe() {}",
593 {"--cuda-host-only", "-nocudainc", "-xcuda"}));
596 TEST(runToolOnCodeWithArgs
, TestNoDepFile
) {
597 llvm::SmallString
<32> DepFilePath
;
598 ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d",
600 std::vector
<std::string
> Args
;
601 Args
.push_back("-MMD");
602 Args
.push_back("-MT");
603 Args
.push_back(std::string(DepFilePath
.str()));
604 Args
.push_back("-MF");
605 Args
.push_back(std::string(DepFilePath
.str()));
606 EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique
<SkipBodyAction
>(), "", Args
));
607 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath
.str()));
608 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath
.str()));
611 struct CheckColoredDiagnosticsAction
: public clang::ASTFrontendAction
{
612 CheckColoredDiagnosticsAction(bool ShouldShowColor
)
613 : ShouldShowColor(ShouldShowColor
) {}
614 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&Compiler
,
615 StringRef
) override
{
616 if (Compiler
.getDiagnosticOpts().ShowColors
!= ShouldShowColor
)
617 Compiler
.getDiagnostics().Report(
618 Compiler
.getDiagnostics().getCustomDiagID(
619 DiagnosticsEngine::Fatal
,
620 "getDiagnosticOpts().ShowColors != ShouldShowColor"));
621 return std::make_unique
<ASTConsumer
>();
625 bool ShouldShowColor
= true;
628 TEST(runToolOnCodeWithArgs
, DiagnosticsColor
) {
629 EXPECT_TRUE(runToolOnCodeWithArgs(
630 std::make_unique
<CheckColoredDiagnosticsAction
>(true), "",
631 {"-fcolor-diagnostics"}));
632 EXPECT_TRUE(runToolOnCodeWithArgs(
633 std::make_unique
<CheckColoredDiagnosticsAction
>(false), "",
634 {"-fno-color-diagnostics"}));
635 EXPECT_TRUE(runToolOnCodeWithArgs(
636 std::make_unique
<CheckColoredDiagnosticsAction
>(true), "",
637 {"-fno-color-diagnostics", "-fcolor-diagnostics"}));
638 EXPECT_TRUE(runToolOnCodeWithArgs(
639 std::make_unique
<CheckColoredDiagnosticsAction
>(false), "",
640 {"-fcolor-diagnostics", "-fno-color-diagnostics"}));
641 EXPECT_TRUE(runToolOnCodeWithArgs(
642 std::make_unique
<CheckColoredDiagnosticsAction
>(true), "",
643 {"-fno-color-diagnostics", "-fdiagnostics-color=always"}));
645 // Check that this test would fail if ShowColors is not what it should.
646 EXPECT_FALSE(runToolOnCodeWithArgs(
647 std::make_unique
<CheckColoredDiagnosticsAction
>(false), "",
648 {"-fcolor-diagnostics"}));
651 TEST(ClangToolTest
, ArgumentAdjusters
) {
652 FixedCompilationDatabase
Compilations("/", std::vector
<std::string
>());
654 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
655 Tool
.mapVirtualFile("/a.cc", "void a() {}");
657 std::unique_ptr
<FrontendActionFactory
> Action(
658 newFrontendActionFactory
<SyntaxOnlyAction
>());
662 ArgumentsAdjuster CheckSyntaxOnlyAdjuster
=
663 [&Found
, &Ran
](const CommandLineArguments
&Args
, StringRef
/*unused*/) {
665 if (llvm::is_contained(Args
, "-fsyntax-only"))
669 Tool
.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster
);
670 Tool
.run(Action
.get());
675 Tool
.clearArgumentsAdjusters();
676 Tool
.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster
);
677 Tool
.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
678 Tool
.run(Action
.get());
683 TEST(ClangToolTest
, NoDoubleSyntaxOnly
) {
684 FixedCompilationDatabase
Compilations("/", {"-fsyntax-only"});
686 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
687 Tool
.mapVirtualFile("/a.cc", "void a() {}");
689 std::unique_ptr
<FrontendActionFactory
> Action(
690 newFrontendActionFactory
<SyntaxOnlyAction
>());
692 size_t SyntaxOnlyCount
= 0;
693 ArgumentsAdjuster CheckSyntaxOnlyAdjuster
=
694 [&SyntaxOnlyCount
](const CommandLineArguments
&Args
,
695 StringRef
/*unused*/) {
696 for (llvm::StringRef Arg
: Args
) {
697 if (Arg
== "-fsyntax-only")
703 Tool
.clearArgumentsAdjusters();
704 Tool
.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
705 Tool
.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster
);
706 Tool
.run(Action
.get());
707 EXPECT_EQ(SyntaxOnlyCount
, 1U);
710 TEST(ClangToolTest
, NoOutputCommands
) {
711 FixedCompilationDatabase
Compilations("/", {"-save-temps", "-save-temps=cwd",
713 "--save-temps=somedir"});
715 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
716 Tool
.mapVirtualFile("/a.cc", "void a() {}");
718 std::unique_ptr
<FrontendActionFactory
> Action(
719 newFrontendActionFactory
<SyntaxOnlyAction
>());
721 const std::vector
<llvm::StringRef
> OutputCommands
= {"-save-temps"};
723 ArgumentsAdjuster CheckSyntaxOnlyAdjuster
=
724 [&OutputCommands
, &Ran
](const CommandLineArguments
&Args
,
725 StringRef
/*unused*/) {
726 for (llvm::StringRef Arg
: Args
) {
727 for (llvm::StringRef OutputCommand
: OutputCommands
)
728 EXPECT_FALSE(Arg
.contains(OutputCommand
));
734 Tool
.clearArgumentsAdjusters();
735 Tool
.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
736 Tool
.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster
);
737 Tool
.run(Action
.get());
741 TEST(ClangToolTest
, BaseVirtualFileSystemUsage
) {
742 FixedCompilationDatabase
Compilations("/", std::vector
<std::string
>());
743 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFileSystem(
744 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
745 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem(
746 new llvm::vfs::InMemoryFileSystem
);
747 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
749 InMemoryFileSystem
->addFile(
750 "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}"));
752 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "a.cpp"),
753 std::make_shared
<PCHContainerOperations
>(), OverlayFileSystem
);
754 std::unique_ptr
<FrontendActionFactory
> Action(
755 newFrontendActionFactory
<SyntaxOnlyAction
>());
756 EXPECT_EQ(0, Tool
.run(Action
.get()));
759 // Check getClangStripDependencyFileAdjuster doesn't strip args after -MD/-MMD.
760 TEST(ClangToolTest
, StripDependencyFileAdjuster
) {
761 FixedCompilationDatabase
Compilations("/", {"-MD", "-c", "-MMD", "-w"});
763 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
764 Tool
.mapVirtualFile("/a.cc", "void a() {}");
766 std::unique_ptr
<FrontendActionFactory
> Action(
767 newFrontendActionFactory
<SyntaxOnlyAction
>());
769 CommandLineArguments FinalArgs
;
770 ArgumentsAdjuster CheckFlagsAdjuster
=
771 [&FinalArgs
](const CommandLineArguments
&Args
, StringRef
/*unused*/) {
775 Tool
.clearArgumentsAdjusters();
776 Tool
.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
777 Tool
.appendArgumentsAdjuster(CheckFlagsAdjuster
);
778 Tool
.run(Action
.get());
780 auto HasFlag
= [&FinalArgs
](const std::string
&Flag
) {
781 return llvm::find(FinalArgs
, Flag
) != FinalArgs
.end();
783 EXPECT_FALSE(HasFlag("-MD"));
784 EXPECT_FALSE(HasFlag("-MMD"));
785 EXPECT_TRUE(HasFlag("-c"));
786 EXPECT_TRUE(HasFlag("-w"));
789 // Check getClangStripDependencyFileAdjuster strips /showIncludes and variants
790 TEST(ClangToolTest
, StripDependencyFileAdjusterShowIncludes
) {
791 FixedCompilationDatabase
Compilations(
792 "/", {"/showIncludes", "/showIncludes:user", "-showIncludes",
793 "-showIncludes:user", "-c"});
795 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
796 Tool
.mapVirtualFile("/a.cc", "void a() {}");
798 std::unique_ptr
<FrontendActionFactory
> Action(
799 newFrontendActionFactory
<SyntaxOnlyAction
>());
801 CommandLineArguments FinalArgs
;
802 ArgumentsAdjuster CheckFlagsAdjuster
=
803 [&FinalArgs
](const CommandLineArguments
&Args
, StringRef
/*unused*/) {
807 Tool
.clearArgumentsAdjusters();
808 Tool
.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
809 Tool
.appendArgumentsAdjuster(CheckFlagsAdjuster
);
810 Tool
.run(Action
.get());
812 auto HasFlag
= [&FinalArgs
](const std::string
&Flag
) {
813 return llvm::find(FinalArgs
, Flag
) != FinalArgs
.end();
815 EXPECT_FALSE(HasFlag("/showIncludes"));
816 EXPECT_FALSE(HasFlag("/showIncludes:user"));
817 EXPECT_FALSE(HasFlag("-showIncludes"));
818 EXPECT_FALSE(HasFlag("-showIncludes:user"));
819 EXPECT_TRUE(HasFlag("-c"));
822 // Check getClangStripDependencyFileAdjuster doesn't strip args when using the
823 // MSVC cl.exe driver
824 TEST(ClangToolTest
, StripDependencyFileAdjusterMsvc
) {
825 FixedCompilationDatabase
Compilations(
826 "/", {"--driver-mode=cl", "-MD", "-MDd", "-MT", "-O1", "-MTd", "-MP"});
828 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
829 Tool
.mapVirtualFile("/a.cc", "void a() {}");
831 std::unique_ptr
<FrontendActionFactory
> Action(
832 newFrontendActionFactory
<SyntaxOnlyAction
>());
834 CommandLineArguments FinalArgs
;
835 ArgumentsAdjuster CheckFlagsAdjuster
=
836 [&FinalArgs
](const CommandLineArguments
&Args
, StringRef
/*unused*/) {
840 Tool
.clearArgumentsAdjusters();
841 Tool
.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
842 Tool
.appendArgumentsAdjuster(CheckFlagsAdjuster
);
843 Tool
.run(Action
.get());
845 auto HasFlag
= [&FinalArgs
](const std::string
&Flag
) {
846 return llvm::find(FinalArgs
, Flag
) != FinalArgs
.end();
848 EXPECT_TRUE(HasFlag("-MD"));
849 EXPECT_TRUE(HasFlag("-MDd"));
850 EXPECT_TRUE(HasFlag("-MT"));
851 EXPECT_TRUE(HasFlag("-O1"));
852 EXPECT_TRUE(HasFlag("-MTd"));
853 EXPECT_TRUE(HasFlag("-MP"));
856 // Check getClangStripPluginsAdjuster strips plugin related args.
857 TEST(ClangToolTest
, StripPluginsAdjuster
) {
858 FixedCompilationDatabase
Compilations(
859 "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"});
861 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
862 Tool
.mapVirtualFile("/a.cc", "void a() {}");
864 std::unique_ptr
<FrontendActionFactory
> Action(
865 newFrontendActionFactory
<SyntaxOnlyAction
>());
867 CommandLineArguments FinalArgs
;
868 ArgumentsAdjuster CheckFlagsAdjuster
=
869 [&FinalArgs
](const CommandLineArguments
&Args
, StringRef
/*unused*/) {
873 Tool
.clearArgumentsAdjusters();
874 Tool
.appendArgumentsAdjuster(getStripPluginsAdjuster());
875 Tool
.appendArgumentsAdjuster(CheckFlagsAdjuster
);
876 Tool
.run(Action
.get());
878 auto HasFlag
= [&FinalArgs
](const std::string
&Flag
) {
879 return llvm::find(FinalArgs
, Flag
) != FinalArgs
.end();
881 EXPECT_FALSE(HasFlag("-Xclang"));
882 EXPECT_FALSE(HasFlag("-add-plugin"));
883 EXPECT_FALSE(HasFlag("-random-plugin"));
886 TEST(addTargetAndModeForProgramName
, AddsTargetAndMode
) {
887 llvm::InitializeAllTargets();
889 std::string Target
= getAnyTargetForTesting();
890 ASSERT_FALSE(Target
.empty());
892 std::vector
<std::string
> Args
= {"clang", "-foo"};
893 addTargetAndModeForProgramName(Args
, "");
894 EXPECT_EQ((std::vector
<std::string
>{"clang", "-foo"}), Args
);
895 addTargetAndModeForProgramName(Args
, Target
+ "-g++");
896 EXPECT_EQ((std::vector
<std::string
>{"clang", "--target=" + Target
,
897 "--driver-mode=g++", "-foo"}),
901 TEST(addTargetAndModeForProgramName
, PathIgnored
) {
902 llvm::InitializeAllTargets();
903 std::string Target
= getAnyTargetForTesting();
904 ASSERT_FALSE(Target
.empty());
906 SmallString
<32> ToolPath
;
907 llvm::sys::path::append(ToolPath
, "foo", "bar", Target
+ "-g++");
909 std::vector
<std::string
> Args
= {"clang", "-foo"};
910 addTargetAndModeForProgramName(Args
, ToolPath
);
911 EXPECT_EQ((std::vector
<std::string
>{"clang", "--target=" + Target
,
912 "--driver-mode=g++", "-foo"}),
916 TEST(addTargetAndModeForProgramName
, IgnoresExistingTarget
) {
917 llvm::InitializeAllTargets();
918 std::string Target
= getAnyTargetForTesting();
919 ASSERT_FALSE(Target
.empty());
921 std::vector
<std::string
> Args
= {"clang", "-foo", "-target", "something"};
922 addTargetAndModeForProgramName(Args
, Target
+ "-g++");
923 EXPECT_EQ((std::vector
<std::string
>{"clang", "--driver-mode=g++", "-foo",
924 "-target", "something"}),
927 std::vector
<std::string
> ArgsAlt
= {"clang", "-foo", "--target=something"};
928 addTargetAndModeForProgramName(ArgsAlt
, Target
+ "-g++");
929 EXPECT_EQ((std::vector
<std::string
>{"clang", "--driver-mode=g++", "-foo",
930 "--target=something"}),
934 TEST(addTargetAndModeForProgramName
, IgnoresExistingMode
) {
935 llvm::InitializeAllTargets();
936 std::string Target
= getAnyTargetForTesting();
937 ASSERT_FALSE(Target
.empty());
939 std::vector
<std::string
> Args
= {"clang", "-foo", "--driver-mode=abc"};
940 addTargetAndModeForProgramName(Args
, Target
+ "-g++");
941 EXPECT_EQ((std::vector
<std::string
>{"clang", "--target=" + Target
, "-foo",
942 "--driver-mode=abc"}),
947 TEST(ClangToolTest
, BuildASTs
) {
948 FixedCompilationDatabase
Compilations("/", std::vector
<std::string
>());
950 std::vector
<std::string
> Sources
;
951 Sources
.push_back("/a.cc");
952 Sources
.push_back("/b.cc");
953 ClangTool
Tool(Compilations
, Sources
);
955 Tool
.mapVirtualFile("/a.cc", "void a() {}");
956 Tool
.mapVirtualFile("/b.cc", "void b() {}");
958 std::vector
<std::unique_ptr
<ASTUnit
>> ASTs
;
959 EXPECT_EQ(0, Tool
.buildASTs(ASTs
));
960 EXPECT_EQ(2u, ASTs
.size());
963 TEST(ClangToolTest
, InjectDiagnosticConsumer
) {
964 FixedCompilationDatabase
Compilations("/", std::vector
<std::string
>());
965 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
966 Tool
.mapVirtualFile("/a.cc", "int x = undeclared;");
967 TestDiagnosticConsumer Consumer
;
968 Tool
.setDiagnosticConsumer(&Consumer
);
969 std::unique_ptr
<FrontendActionFactory
> Action(
970 newFrontendActionFactory
<SyntaxOnlyAction
>());
971 Tool
.run(Action
.get());
972 EXPECT_EQ(1u, Consumer
.NumDiagnosticsSeen
);
975 TEST(ClangToolTest
, InjectDiagnosticConsumerInBuildASTs
) {
976 FixedCompilationDatabase
Compilations("/", std::vector
<std::string
>());
977 ClangTool
Tool(Compilations
, std::vector
<std::string
>(1, "/a.cc"));
978 Tool
.mapVirtualFile("/a.cc", "int x = undeclared;");
979 TestDiagnosticConsumer Consumer
;
980 Tool
.setDiagnosticConsumer(&Consumer
);
981 std::vector
<std::unique_ptr
<ASTUnit
>> ASTs
;
982 Tool
.buildASTs(ASTs
);
983 EXPECT_EQ(1u, ASTs
.size());
984 EXPECT_EQ(1u, Consumer
.NumDiagnosticsSeen
);
988 TEST(runToolOnCode
, TestResetDiagnostics
) {
989 // This is a tool that resets the diagnostic during the compilation.
990 struct ResetDiagnosticAction
: public clang::ASTFrontendAction
{
991 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&Compiler
,
992 StringRef
) override
{
993 struct Consumer
: public clang::ASTConsumer
{
994 bool HandleTopLevelDecl(clang::DeclGroupRef D
) override
{
995 auto &Diags
= (*D
.begin())->getASTContext().getDiagnostics();
998 // Disable warnings because computing the CFG might crash.
999 Diags
.setIgnoreAllWarnings(true);
1003 return std::make_unique
<Consumer
>();
1009 runToolOnCode(std::make_unique
<ResetDiagnosticAction
>(),
1010 "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };"
1011 "void func() { long x; Foo f(x); }"));
1014 } // end namespace tooling
1015 } // end namespace clang