1//===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "clang/AST/ASTConsumer.h" 11#include "clang/AST/DeclCXX.h" 12#include "clang/AST/DeclGroup.h" 13#include "clang/Frontend/ASTUnit.h" 14#include "clang/Frontend/CompilerInstance.h" 15#include "clang/Frontend/FrontendAction.h" 16#include "clang/Frontend/FrontendActions.h" 17#include "clang/Tooling/CompilationDatabase.h" 18#include "clang/Tooling/Tooling.h" 19#include "llvm/ADT/STLExtras.h" 20#include "llvm/Config/llvm-config.h" 21#include "llvm/Support/TargetSelect.h" 22#include "llvm/Support/TargetRegistry.h" 23#include "gtest/gtest.h" 24#include <algorithm> 25#include <string> 26 27namespace clang { 28namespace tooling { 29 30namespace { 31/// Takes an ast consumer and returns it from CreateASTConsumer. This only 32/// works with single translation unit compilations. 33class TestAction : public clang::ASTFrontendAction { 34public: 35 /// Takes ownership of TestConsumer. 36 explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer) 37 : TestConsumer(std::move(TestConsumer)) {} 38 39protected: 40 std::unique_ptr<clang::ASTConsumer> 41 CreateASTConsumer(clang::CompilerInstance &compiler, 42 StringRef dummy) override { 43 /// TestConsumer will be deleted by the framework calling us. 44 return std::move(TestConsumer); 45 } 46 47private: 48 std::unique_ptr<clang::ASTConsumer> TestConsumer; 49}; 50 51class FindTopLevelDeclConsumer : public clang::ASTConsumer { 52 public: 53 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) 54 : FoundTopLevelDecl(FoundTopLevelDecl) {} 55 bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override { 56 *FoundTopLevelDecl = true; 57 return true; 58 } 59 private: 60 bool * const FoundTopLevelDecl; 61}; 62} // end namespace 63 64TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 65 bool FoundTopLevelDecl = false; 66 EXPECT_TRUE( 67 runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>( 68 &FoundTopLevelDecl)), 69 "")); 70 EXPECT_FALSE(FoundTopLevelDecl); 71} 72 73namespace { 74class FindClassDeclXConsumer : public clang::ASTConsumer { 75 public: 76 FindClassDeclXConsumer(bool *FoundClassDeclX) 77 : FoundClassDeclX(FoundClassDeclX) {} 78 bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override { 79 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( 80 *GroupRef.begin())) { 81 if (Record->getName() == "X") { 82 *FoundClassDeclX = true; 83 } 84 } 85 return true; 86 } 87 private: 88 bool *FoundClassDeclX; 89}; 90bool FindClassDeclX(ASTUnit *AST) { 91 for (std::vector<Decl *>::iterator i = AST->top_level_begin(), 92 e = AST->top_level_end(); 93 i != e; ++i) { 94 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) { 95 if (Record->getName() == "X") { 96 return true; 97 } 98 } 99 } 100 return false; 101} 102} // end namespace 103 104TEST(runToolOnCode, FindsClassDecl) { 105 bool FoundClassDeclX = false; 106 EXPECT_TRUE( 107 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( 108 &FoundClassDeclX)), 109 "class X;")); 110 EXPECT_TRUE(FoundClassDeclX); 111 112 FoundClassDeclX = false; 113 EXPECT_TRUE( 114 runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( 115 &FoundClassDeclX)), 116 "class Y;")); 117 EXPECT_FALSE(FoundClassDeclX); 118} 119 120TEST(buildASTFromCode, FindsClassDecl) { 121 std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;"); 122 ASSERT_TRUE(AST.get()); 123 EXPECT_TRUE(FindClassDeclX(AST.get())); 124 125 AST = buildASTFromCode("class Y;"); 126 ASSERT_TRUE(AST.get()); 127 EXPECT_FALSE(FindClassDeclX(AST.get())); 128} 129 130TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 131 std::unique_ptr<FrontendActionFactory> Factory( 132 newFrontendActionFactory<SyntaxOnlyAction>()); 133 std::unique_ptr<FrontendAction> Action(Factory->create()); 134 EXPECT_TRUE(Action.get() != nullptr); 135} 136 137struct IndependentFrontendActionCreator { 138 std::unique_ptr<ASTConsumer> newASTConsumer() { 139 return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr); 140 } 141}; 142 143TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { 144 IndependentFrontendActionCreator Creator; 145 std::unique_ptr<FrontendActionFactory> Factory( 146 newFrontendActionFactory(&Creator)); 147 std::unique_ptr<FrontendAction> Action(Factory->create()); 148 EXPECT_TRUE(Action.get() != nullptr); 149} 150 151TEST(ToolInvocation, TestMapVirtualFile) { 152 llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem( 153 new vfs::OverlayFileSystem(vfs::getRealFileSystem())); 154 llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( 155 new vfs::InMemoryFileSystem); 156 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 157 llvm::IntrusiveRefCntPtr<FileManager> Files( 158 new FileManager(FileSystemOptions(), OverlayFileSystem)); 159 std::vector<std::string> Args; 160 Args.push_back("tool-executable"); 161 Args.push_back("-Idef"); 162 Args.push_back("-fsyntax-only"); 163 Args.push_back("test.cpp"); 164 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, 165 Files.get()); 166 InMemoryFileSystem->addFile( 167 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 168 InMemoryFileSystem->addFile("def/abc", 0, 169 llvm::MemoryBuffer::getMemBuffer("\n")); 170 EXPECT_TRUE(Invocation.run()); 171} 172 173TEST(ToolInvocation, TestVirtualModulesCompilation) { 174 // FIXME: Currently, this only tests that we don't exit with an error if a 175 // mapped module.map is found on the include path. In the future, expand this 176 // test to run a full modules enabled compilation, so we make sure we can 177 // rerun modules compilations with a virtual file system. 178 llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem( 179 new vfs::OverlayFileSystem(vfs::getRealFileSystem())); 180 llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( 181 new vfs::InMemoryFileSystem); 182 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 183 llvm::IntrusiveRefCntPtr<FileManager> Files( 184 new FileManager(FileSystemOptions(), OverlayFileSystem)); 185 std::vector<std::string> Args; 186 Args.push_back("tool-executable"); 187 Args.push_back("-Idef"); 188 Args.push_back("-fsyntax-only"); 189 Args.push_back("test.cpp"); 190 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, 191 Files.get()); 192 InMemoryFileSystem->addFile( 193 "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); 194 InMemoryFileSystem->addFile("def/abc", 0, 195 llvm::MemoryBuffer::getMemBuffer("\n")); 196 // Add a module.map file in the include directory of our header, so we trigger 197 // the module.map header search logic. 198 InMemoryFileSystem->addFile("def/module.map", 0, 199 llvm::MemoryBuffer::getMemBuffer("\n")); 200 EXPECT_TRUE(Invocation.run()); 201} 202 203struct VerifyEndCallback : public SourceFileCallbacks { 204 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 205 bool handleBeginSource(CompilerInstance &CI, StringRef Filename) override { 206 ++BeginCalled; 207 return true; 208 } 209 void handleEndSource() override { ++EndCalled; } 210 std::unique_ptr<ASTConsumer> newASTConsumer() { 211 return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched); 212 } 213 unsigned BeginCalled; 214 unsigned EndCalled; 215 bool Matched; 216}; 217 218#if !defined(LLVM_ON_WIN32) 219TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 220 VerifyEndCallback EndCallback; 221 222 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 223 std::vector<std::string> Sources; 224 Sources.push_back("/a.cc"); 225 Sources.push_back("/b.cc"); 226 ClangTool Tool(Compilations, Sources); 227 228 Tool.mapVirtualFile("/a.cc", "void a() {}"); 229 Tool.mapVirtualFile("/b.cc", "void b() {}"); 230 231 std::unique_ptr<FrontendActionFactory> Action( 232 newFrontendActionFactory(&EndCallback, &EndCallback)); 233 Tool.run(Action.get()); 234 235 EXPECT_TRUE(EndCallback.Matched); 236 EXPECT_EQ(2u, EndCallback.BeginCalled); 237 EXPECT_EQ(2u, EndCallback.EndCalled); 238} 239#endif 240 241struct SkipBodyConsumer : public clang::ASTConsumer { 242 /// Skip the 'skipMe' function. 243 bool shouldSkipFunctionBody(Decl *D) override { 244 NamedDecl *F = dyn_cast<NamedDecl>(D); 245 return F && F->getNameAsString() == "skipMe"; 246 } 247}; 248 249struct SkipBodyAction : public clang::ASTFrontendAction { 250 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 251 StringRef) override { 252 Compiler.getFrontendOpts().SkipFunctionBodies = true; 253 return llvm::make_unique<SkipBodyConsumer>(); 254 } 255}; 256 257TEST(runToolOnCode, TestSkipFunctionBody) { 258 std::vector<std::string> Args = {"-std=c++11"}; 259 std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"}; 260 261 EXPECT_TRUE(runToolOnCode(new SkipBodyAction, 262 "int skipMe() { an_error_here }")); 263 EXPECT_FALSE(runToolOnCode(new SkipBodyAction, 264 "int skipMeNot() { an_error_here }")); 265 266 // Test constructors with initializers 267 EXPECT_TRUE(runToolOnCodeWithArgs( 268 new SkipBodyAction, 269 "struct skipMe { skipMe() : an_error() { more error } };", Args)); 270 EXPECT_TRUE(runToolOnCodeWithArgs( 271 new SkipBodyAction, "struct skipMe { skipMe(); };" 272 "skipMe::skipMe() : an_error([](){;}) { more error }", 273 Args)); 274 EXPECT_TRUE(runToolOnCodeWithArgs( 275 new SkipBodyAction, "struct skipMe { skipMe(); };" 276 "skipMe::skipMe() : an_error{[](){;}} { more error }", 277 Args)); 278 EXPECT_TRUE(runToolOnCodeWithArgs( 279 new SkipBodyAction, 280 "struct skipMe { skipMe(); };" 281 "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }", 282 Args)); 283 EXPECT_TRUE(runToolOnCodeWithArgs( 284 new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };", 285 Args)); 286 287 EXPECT_FALSE(runToolOnCodeWithArgs( 288 new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };", 289 Args)); 290 EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction, 291 "struct skipMeNot { skipMeNot(); };" 292 "skipMeNot::skipMeNot() : an_error() { }", 293 Args)); 294 295 // Try/catch 296 EXPECT_TRUE(runToolOnCode( 297 new SkipBodyAction, 298 "void skipMe() try { an_error() } catch(error) { error };")); 299 EXPECT_TRUE(runToolOnCode( 300 new SkipBodyAction, 301 "struct S { void skipMe() try { an_error() } catch(error) { error } };")); 302 EXPECT_TRUE( 303 runToolOnCode(new SkipBodyAction, 304 "void skipMe() try { an_error() } catch(error) { error; }" 305 "catch(error) { error } catch (error) { }")); 306 EXPECT_FALSE(runToolOnCode( 307 new SkipBodyAction, 308 "void skipMe() try something;")); // don't crash while parsing 309 310 // Template 311 EXPECT_TRUE(runToolOnCode( 312 new SkipBodyAction, "template<typename T> int skipMe() { an_error_here }" 313 "int x = skipMe<int>();")); 314 EXPECT_FALSE(runToolOnCodeWithArgs( 315 new SkipBodyAction, 316 "template<typename T> int skipMeNot() { an_error_here }", Args2)); 317} 318 319TEST(runToolOnCodeWithArgs, TestNoDepFile) { 320 llvm::SmallString<32> DepFilePath; 321 ASSERT_FALSE( 322 llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath)); 323 std::vector<std::string> Args; 324 Args.push_back("-MMD"); 325 Args.push_back("-MT"); 326 Args.push_back(DepFilePath.str()); 327 Args.push_back("-MF"); 328 Args.push_back(DepFilePath.str()); 329 EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args)); 330 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); 331 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); 332} 333 334TEST(ClangToolTest, ArgumentAdjusters) { 335 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 336 337 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 338 Tool.mapVirtualFile("/a.cc", "void a() {}"); 339 340 std::unique_ptr<FrontendActionFactory> Action( 341 newFrontendActionFactory<SyntaxOnlyAction>()); 342 343 bool Found = false; 344 bool Ran = false; 345 ArgumentsAdjuster CheckSyntaxOnlyAdjuster = 346 [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { 347 Ran = true; 348 if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end()) 349 Found = true; 350 return Args; 351 }; 352 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 353 Tool.run(Action.get()); 354 EXPECT_TRUE(Ran); 355 EXPECT_TRUE(Found); 356 357 Ran = Found = false; 358 Tool.clearArgumentsAdjusters(); 359 Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); 360 Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); 361 Tool.run(Action.get()); 362 EXPECT_TRUE(Ran); 363 EXPECT_FALSE(Found); 364} 365 366namespace { 367/// Find a target name such that looking for it in TargetRegistry by that name 368/// returns the same target. We expect that there is at least one target 369/// configured with this property. 370std::string getAnyTarget() { 371 llvm::InitializeAllTargets(); 372 for (const auto &Target : llvm::TargetRegistry::targets()) { 373 std::string Error; 374 StringRef TargetName(Target.getName()); 375 if (TargetName == "x86-64") 376 TargetName = "x86_64"; 377 if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) { 378 return TargetName; 379 } 380 } 381 return ""; 382} 383} 384 385TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { 386 std::string Target = getAnyTarget(); 387 ASSERT_FALSE(Target.empty()); 388 389 std::vector<std::string> Args = {"clang", "-foo"}; 390 addTargetAndModeForProgramName(Args, ""); 391 EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args); 392 addTargetAndModeForProgramName(Args, Target + "-g++"); 393 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 394 "--driver-mode=g++", "-foo"}), 395 Args); 396} 397 398TEST(addTargetAndModeForProgramName, PathIgnored) { 399 std::string Target = getAnyTarget(); 400 ASSERT_FALSE(Target.empty()); 401 402 SmallString<32> ToolPath; 403 llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++"); 404 405 std::vector<std::string> Args = {"clang", "-foo"}; 406 addTargetAndModeForProgramName(Args, ToolPath); 407 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, 408 "--driver-mode=g++", "-foo"}), 409 Args); 410} 411 412TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { 413 std::string Target = getAnyTarget(); 414 ASSERT_FALSE(Target.empty()); 415 416 std::vector<std::string> Args = {"clang", "-foo", "-target", "something"}; 417 addTargetAndModeForProgramName(Args, Target + "-g++"); 418 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 419 "-target", "something"}), 420 Args); 421 422 std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"}; 423 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 424 EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", 425 "-target=something"}), 426 ArgsAlt); 427} 428 429TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { 430 std::string Target = getAnyTarget(); 431 ASSERT_FALSE(Target.empty()); 432 433 std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"}; 434 addTargetAndModeForProgramName(Args, Target + "-g++"); 435 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 436 "--driver-mode=abc"}), 437 Args); 438 439 std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"}; 440 addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); 441 EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", 442 "--driver-mode", "abc"}), 443 ArgsAlt); 444} 445 446#ifndef LLVM_ON_WIN32 447TEST(ClangToolTest, BuildASTs) { 448 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 449 450 std::vector<std::string> Sources; 451 Sources.push_back("/a.cc"); 452 Sources.push_back("/b.cc"); 453 ClangTool Tool(Compilations, Sources); 454 455 Tool.mapVirtualFile("/a.cc", "void a() {}"); 456 Tool.mapVirtualFile("/b.cc", "void b() {}"); 457 458 std::vector<std::unique_ptr<ASTUnit>> ASTs; 459 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 460 EXPECT_EQ(2u, ASTs.size()); 461} 462 463struct TestDiagnosticConsumer : public DiagnosticConsumer { 464 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 465 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 466 const Diagnostic &Info) override { 467 ++NumDiagnosticsSeen; 468 } 469 unsigned NumDiagnosticsSeen; 470}; 471 472TEST(ClangToolTest, InjectDiagnosticConsumer) { 473 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 474 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 475 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 476 TestDiagnosticConsumer Consumer; 477 Tool.setDiagnosticConsumer(&Consumer); 478 std::unique_ptr<FrontendActionFactory> Action( 479 newFrontendActionFactory<SyntaxOnlyAction>()); 480 Tool.run(Action.get()); 481 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 482} 483 484TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 485 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 486 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 487 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 488 TestDiagnosticConsumer Consumer; 489 Tool.setDiagnosticConsumer(&Consumer); 490 std::vector<std::unique_ptr<ASTUnit>> ASTs; 491 Tool.buildASTs(ASTs); 492 EXPECT_EQ(1u, ASTs.size()); 493 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 494} 495#endif 496 497} // end namespace tooling 498} // end namespace clang 499