| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | #include "clang/AST/ASTConsumer.h" |
| 10 | #include "clang/AST/DeclCXX.h" |
| 11 | #include "clang/AST/DeclGroup.h" |
| 12 | #include "clang/Frontend/ASTUnit.h" |
| 13 | #include "clang/Frontend/CompilerInstance.h" |
| 14 | #include "clang/Frontend/FrontendAction.h" |
| 15 | #include "clang/Frontend/FrontendActions.h" |
| 16 | #include "clang/Tooling/CompilationDatabase.h" |
| 17 | #include "clang/Tooling/Tooling.h" |
| 18 | #include "llvm/ADT/STLExtras.h" |
| 19 | #include "llvm/Support/Path.h" |
| 20 | #include "llvm/Support/TargetRegistry.h" |
| 21 | #include "llvm/Support/TargetSelect.h" |
| 22 | #include "gtest/gtest.h" |
| 23 | #include <algorithm> |
| 24 | #include <string> |
| 25 | |
| 26 | namespace clang { |
| 27 | namespace tooling { |
| 28 | |
| 29 | namespace { |
| 30 | |
| 31 | |
| 32 | class TestAction : public clang::ASTFrontendAction { |
| 33 | public: |
| 34 | |
| 35 | explicit TestAction(std::unique_ptr<clang::ASTConsumer> TestConsumer) |
| 36 | : TestConsumer(std::move(TestConsumer)) {} |
| 37 | |
| 38 | protected: |
| 39 | std::unique_ptr<clang::ASTConsumer> |
| 40 | CreateASTConsumer(clang::CompilerInstance &compiler, |
| 41 | StringRef dummy) override { |
| 42 | |
| 43 | return std::move(TestConsumer); |
| 44 | } |
| 45 | |
| 46 | private: |
| 47 | std::unique_ptr<clang::ASTConsumer> TestConsumer; |
| 48 | }; |
| 49 | |
| 50 | class FindTopLevelDeclConsumer : public clang::ASTConsumer { |
| 51 | public: |
| 52 | explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) |
| 53 | : FoundTopLevelDecl(FoundTopLevelDecl) {} |
| 54 | bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) override { |
| 55 | *FoundTopLevelDecl = true; |
| 56 | return true; |
| 57 | } |
| 58 | private: |
| 59 | bool * const FoundTopLevelDecl; |
| 60 | }; |
| 61 | } |
| 62 | |
| 63 | TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { |
| 64 | bool FoundTopLevelDecl = false; |
| 65 | EXPECT_TRUE( |
| 66 | runToolOnCode(new TestAction(llvm::make_unique<FindTopLevelDeclConsumer>( |
| 67 | &FoundTopLevelDecl)), |
| 68 | "")); |
| 69 | EXPECT_FALSE(FoundTopLevelDecl); |
| 70 | } |
| 71 | |
| 72 | namespace { |
| 73 | class FindClassDeclXConsumer : public clang::ASTConsumer { |
| 74 | public: |
| 75 | FindClassDeclXConsumer(bool *FoundClassDeclX) |
| 76 | : FoundClassDeclX(FoundClassDeclX) {} |
| 77 | bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) override { |
| 78 | if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( |
| 79 | *GroupRef.begin())) { |
| 80 | if (Record->getName() == "X") { |
| 81 | *FoundClassDeclX = true; |
| 82 | } |
| 83 | } |
| 84 | return true; |
| 85 | } |
| 86 | private: |
| 87 | bool *FoundClassDeclX; |
| 88 | }; |
| 89 | bool FindClassDeclX(ASTUnit *AST) { |
| 90 | for (std::vector<Decl *>::iterator i = AST->top_level_begin(), |
| 91 | e = AST->top_level_end(); |
| 92 | i != e; ++i) { |
| 93 | if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) { |
| 94 | if (Record->getName() == "X") { |
| 95 | return true; |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | return false; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | TEST(runToolOnCode, FindsClassDecl) { |
| 104 | bool FoundClassDeclX = false; |
| 105 | EXPECT_TRUE( |
| 106 | runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( |
| 107 | &FoundClassDeclX)), |
| 108 | "class X;")); |
| 109 | EXPECT_TRUE(FoundClassDeclX); |
| 110 | |
| 111 | FoundClassDeclX = false; |
| 112 | EXPECT_TRUE( |
| 113 | runToolOnCode(new TestAction(llvm::make_unique<FindClassDeclXConsumer>( |
| 114 | &FoundClassDeclX)), |
| 115 | "class Y;")); |
| 116 | EXPECT_FALSE(FoundClassDeclX); |
| 117 | } |
| 118 | |
| 119 | TEST(buildASTFromCode, FindsClassDecl) { |
| 120 | std::unique_ptr<ASTUnit> AST = buildASTFromCode("class X;"); |
| 121 | ASSERT_TRUE(AST.get()); |
| 122 | EXPECT_TRUE(FindClassDeclX(AST.get())); |
| 123 | |
| 124 | AST = buildASTFromCode("class Y;"); |
| 125 | ASSERT_TRUE(AST.get()); |
| 126 | EXPECT_FALSE(FindClassDeclX(AST.get())); |
| 127 | } |
| 128 | |
| 129 | TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { |
| 130 | std::unique_ptr<FrontendActionFactory> Factory( |
| 131 | newFrontendActionFactory<SyntaxOnlyAction>()); |
| 132 | std::unique_ptr<FrontendAction> Action(Factory->create()); |
| 133 | EXPECT_TRUE(Action.get() != nullptr); |
| 134 | } |
| 135 | |
| 136 | struct IndependentFrontendActionCreator { |
| 137 | std::unique_ptr<ASTConsumer> newASTConsumer() { |
| 138 | return llvm::make_unique<FindTopLevelDeclConsumer>(nullptr); |
| 139 | } |
| 140 | }; |
| 141 | |
| 142 | TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { |
| 143 | IndependentFrontendActionCreator Creator; |
| 144 | std::unique_ptr<FrontendActionFactory> Factory( |
| 145 | newFrontendActionFactory(&Creator)); |
| 146 | std::unique_ptr<FrontendAction> Action(Factory->create()); |
| 147 | EXPECT_TRUE(Action.get() != nullptr); |
| 148 | } |
| 149 | |
| 150 | TEST(ToolInvocation, TestMapVirtualFile) { |
| 151 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
| 152 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
| 153 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
| 154 | new llvm::vfs::InMemoryFileSystem); |
| 155 | OverlayFileSystem->pushOverlay(InMemoryFileSystem); |
| 156 | llvm::IntrusiveRefCntPtr<FileManager> Files( |
| 157 | new FileManager(FileSystemOptions(), OverlayFileSystem)); |
| 158 | std::vector<std::string> Args; |
| 159 | Args.push_back("tool-executable"); |
| 160 | Args.push_back("-Idef"); |
| 161 | Args.push_back("-fsyntax-only"); |
| 162 | Args.push_back("test.cpp"); |
| 163 | clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, |
| 164 | Files.get()); |
| 165 | InMemoryFileSystem->addFile( |
| 166 | "test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("#include <abc>\n")); |
| 167 | InMemoryFileSystem->addFile("def/abc", 0, |
| 168 | llvm::MemoryBuffer::getMemBuffer("\n")); |
| 169 | EXPECT_TRUE(Invocation.run()); |
| 170 | } |
| 171 | |
| 172 | TEST(ToolInvocation, TestVirtualModulesCompilation) { |
| 173 | |
| 174 | |
| 175 | |
| 176 | |
| 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(Args, new SyntaxOnlyAction, |
| 190 | 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 | |
| 196 | |
| 197 | InMemoryFileSystem->addFile("def/module.map", 0, |
| 198 | llvm::MemoryBuffer::getMemBuffer("\n")); |
| 199 | EXPECT_TRUE(Invocation.run()); |
| 200 | } |
| 201 | |
| 202 | struct VerifyEndCallback : public SourceFileCallbacks { |
| 203 | VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} |
| 204 | bool handleBeginSource(CompilerInstance &CI) override { |
| 205 | ++BeginCalled; |
| 206 | return true; |
| 207 | } |
| 208 | void handleEndSource() override { ++EndCalled; } |
| 209 | std::unique_ptr<ASTConsumer> newASTConsumer() { |
| 210 | return llvm::make_unique<FindTopLevelDeclConsumer>(&Matched); |
| 211 | } |
| 212 | unsigned BeginCalled; |
| 213 | unsigned EndCalled; |
| 214 | bool Matched; |
| 215 | }; |
| 216 | |
| 217 | #if !defined(_WIN32) |
| 218 | TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { |
| 219 | VerifyEndCallback EndCallback; |
| 220 | |
| 221 | FixedCompilationDatabase Compilations("/", std::vector<std::string>()); |
| 222 | std::vector<std::string> Sources; |
| 223 | Sources.push_back("/a.cc"); |
| 224 | Sources.push_back("/b.cc"); |
| 225 | ClangTool Tool(Compilations, Sources); |
| 226 | |
| 227 | Tool.mapVirtualFile("/a.cc", "void a() {}"); |
| 228 | Tool.mapVirtualFile("/b.cc", "void b() {}"); |
| 229 | |
| 230 | std::unique_ptr<FrontendActionFactory> Action( |
| 231 | newFrontendActionFactory(&EndCallback, &EndCallback)); |
| 232 | Tool.run(Action.get()); |
| 233 | |
| 234 | EXPECT_TRUE(EndCallback.Matched); |
| 235 | EXPECT_EQ(2u, EndCallback.BeginCalled); |
| 236 | EXPECT_EQ(2u, EndCallback.EndCalled); |
| 237 | } |
| 238 | #endif |
| 239 | |
| 240 | struct SkipBodyConsumer : public clang::ASTConsumer { |
| 241 | |
| 242 | bool shouldSkipFunctionBody(Decl *D) override { |
| 243 | NamedDecl *F = dyn_cast<NamedDecl>(D); |
| 244 | return F && F->getNameAsString() == "skipMe"; |
| 245 | } |
| 246 | }; |
| 247 | |
| 248 | struct SkipBodyAction : public clang::ASTFrontendAction { |
| 249 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
| 250 | StringRef) override { |
| 251 | Compiler.getFrontendOpts().SkipFunctionBodies = true; |
| 252 | return llvm::make_unique<SkipBodyConsumer>(); |
| 253 | } |
| 254 | }; |
| 255 | |
| 256 | TEST(runToolOnCode, TestSkipFunctionBody) { |
| 257 | std::vector<std::string> Args = {"-std=c++11"}; |
| 258 | std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"}; |
| 259 | |
| 260 | EXPECT_TRUE(runToolOnCode(new SkipBodyAction, |
| 261 | "int skipMe() { an_error_here }")); |
| 262 | EXPECT_FALSE(runToolOnCode(new SkipBodyAction, |
| 263 | "int skipMeNot() { an_error_here }")); |
| 264 | |
| 265 | |
| 266 | EXPECT_TRUE(runToolOnCodeWithArgs( |
| 267 | new SkipBodyAction, |
| 268 | "struct skipMe { skipMe() : an_error() { more error } };", Args)); |
| 269 | EXPECT_TRUE(runToolOnCodeWithArgs( |
| 270 | new SkipBodyAction, "struct skipMe { skipMe(); };" |
| 271 | "skipMe::skipMe() : an_error([](){;}) { more error }", |
| 272 | Args)); |
| 273 | EXPECT_TRUE(runToolOnCodeWithArgs( |
| 274 | new SkipBodyAction, "struct skipMe { skipMe(); };" |
| 275 | "skipMe::skipMe() : an_error{[](){;}} { more error }", |
| 276 | Args)); |
| 277 | EXPECT_TRUE(runToolOnCodeWithArgs( |
| 278 | new SkipBodyAction, |
| 279 | "struct skipMe { skipMe(); };" |
| 280 | "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }", |
| 281 | Args)); |
| 282 | EXPECT_TRUE(runToolOnCodeWithArgs( |
| 283 | new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };", |
| 284 | Args)); |
| 285 | |
| 286 | EXPECT_FALSE(runToolOnCodeWithArgs( |
| 287 | new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };", |
| 288 | Args)); |
| 289 | EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction, |
| 290 | "struct skipMeNot { skipMeNot(); };" |
| 291 | "skipMeNot::skipMeNot() : an_error() { }", |
| 292 | Args)); |
| 293 | |
| 294 | |
| 295 | EXPECT_TRUE(runToolOnCode( |
| 296 | new SkipBodyAction, |
| 297 | "void skipMe() try { an_error() } catch(error) { error };")); |
| 298 | EXPECT_TRUE(runToolOnCode( |
| 299 | new SkipBodyAction, |
| 300 | "struct S { void skipMe() try { an_error() } catch(error) { error } };")); |
| 301 | EXPECT_TRUE( |
| 302 | runToolOnCode(new SkipBodyAction, |
| 303 | "void skipMe() try { an_error() } catch(error) { error; }" |
| 304 | "catch(error) { error } catch (error) { }")); |
| 305 | EXPECT_FALSE(runToolOnCode( |
| 306 | new SkipBodyAction, |
| 307 | "void skipMe() try something;")); |
| 308 | |
| 309 | |
| 310 | EXPECT_TRUE(runToolOnCode( |
| 311 | new SkipBodyAction, "template<typename T> int skipMe() { an_error_here }" |
| 312 | "int x = skipMe<int>();")); |
| 313 | EXPECT_FALSE(runToolOnCodeWithArgs( |
| 314 | new SkipBodyAction, |
| 315 | "template<typename T> int skipMeNot() { an_error_here }", Args2)); |
| 316 | } |
| 317 | |
| 318 | TEST(runToolOnCodeWithArgs, TestNoDepFile) { |
| 319 | llvm::SmallString<32> DepFilePath; |
| 320 | ASSERT_FALSE(llvm::sys::fs::getPotentiallyUniqueTempFileName("depfile", "d", |
| 321 | DepFilePath)); |
| 322 | std::vector<std::string> Args; |
| 323 | Args.push_back("-MMD"); |
| 324 | Args.push_back("-MT"); |
| 325 | Args.push_back(DepFilePath.str()); |
| 326 | Args.push_back("-MF"); |
| 327 | Args.push_back(DepFilePath.str()); |
| 328 | EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args)); |
| 329 | EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); |
| 330 | EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); |
| 331 | } |
| 332 | |
| 333 | struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction { |
| 334 | CheckColoredDiagnosticsAction(bool ShouldShowColor) |
| 335 | : ShouldShowColor(ShouldShowColor) {} |
| 336 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
| 337 | StringRef) override { |
| 338 | if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor) |
| 339 | Compiler.getDiagnostics().Report( |
| 340 | Compiler.getDiagnostics().getCustomDiagID( |
| 341 | DiagnosticsEngine::Fatal, |
| 342 | "getDiagnosticOpts().ShowColors != ShouldShowColor")); |
| 343 | return llvm::make_unique<ASTConsumer>(); |
| 344 | } |
| 345 | |
| 346 | private: |
| 347 | bool ShouldShowColor = true; |
| 348 | }; |
| 349 | |
| 350 | TEST(runToolOnCodeWithArgs, DiagnosticsColor) { |
| 351 | |
| 352 | EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "", |
| 353 | {"-fcolor-diagnostics"})); |
| 354 | EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), |
| 355 | "", {"-fno-color-diagnostics"})); |
| 356 | EXPECT_TRUE( |
| 357 | runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "", |
| 358 | {"-fno-color-diagnostics", "-fcolor-diagnostics"})); |
| 359 | EXPECT_TRUE( |
| 360 | runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), "", |
| 361 | {"-fcolor-diagnostics", "-fno-color-diagnostics"})); |
| 362 | EXPECT_TRUE(runToolOnCodeWithArgs( |
| 363 | new CheckColoredDiagnosticsAction(true), "", |
| 364 | {"-fno-color-diagnostics", "-fdiagnostics-color=always"})); |
| 365 | |
| 366 | |
| 367 | EXPECT_FALSE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), |
| 368 | "", {"-fcolor-diagnostics"})); |
| 369 | } |
| 370 | |
| 371 | TEST(ClangToolTest, ArgumentAdjusters) { |
| 372 | FixedCompilationDatabase Compilations("/", std::vector<std::string>()); |
| 373 | |
| 374 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); |
| 375 | Tool.mapVirtualFile("/a.cc", "void a() {}"); |
| 376 | |
| 377 | std::unique_ptr<FrontendActionFactory> Action( |
| 378 | newFrontendActionFactory<SyntaxOnlyAction>()); |
| 379 | |
| 380 | bool Found = false; |
| 381 | bool Ran = false; |
| 382 | ArgumentsAdjuster CheckSyntaxOnlyAdjuster = |
| 383 | [&Found, &Ran](const CommandLineArguments &Args, StringRef ) { |
| 384 | Ran = true; |
| 385 | if (llvm::is_contained(Args, "-fsyntax-only")) |
| 386 | Found = true; |
| 387 | return Args; |
| 388 | }; |
| 389 | Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); |
| 390 | Tool.run(Action.get()); |
| 391 | EXPECT_TRUE(Ran); |
| 392 | EXPECT_TRUE(Found); |
| 393 | |
| 394 | Ran = Found = false; |
| 395 | Tool.clearArgumentsAdjusters(); |
| 396 | Tool.appendArgumentsAdjuster(CheckSyntaxOnlyAdjuster); |
| 397 | Tool.appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster()); |
| 398 | Tool.run(Action.get()); |
| 399 | EXPECT_TRUE(Ran); |
| 400 | EXPECT_FALSE(Found); |
| 401 | } |
| 402 | |
| 403 | TEST(ClangToolTest, BaseVirtualFileSystemUsage) { |
| 404 | FixedCompilationDatabase Compilations("/", std::vector<std::string>()); |
| 405 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
| 406 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
| 407 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
| 408 | new llvm::vfs::InMemoryFileSystem); |
| 409 | OverlayFileSystem->pushOverlay(InMemoryFileSystem); |
| 410 | |
| 411 | InMemoryFileSystem->addFile( |
| 412 | "a.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int main() {}")); |
| 413 | |
| 414 | ClangTool Tool(Compilations, std::vector<std::string>(1, "a.cpp"), |
| 415 | std::make_shared<PCHContainerOperations>(), OverlayFileSystem); |
| 416 | std::unique_ptr<FrontendActionFactory> Action( |
| 417 | newFrontendActionFactory<SyntaxOnlyAction>()); |
| 418 | EXPECT_EQ(0, Tool.run(Action.get())); |
| 419 | } |
| 420 | |
| 421 | |
| 422 | TEST(ClangToolTest, StripDependencyFileAdjuster) { |
| 423 | FixedCompilationDatabase Compilations("/", {"-MD", "-c", "-MMD", "-w"}); |
| 424 | |
| 425 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); |
| 426 | Tool.mapVirtualFile("/a.cc", "void a() {}"); |
| 427 | |
| 428 | std::unique_ptr<FrontendActionFactory> Action( |
| 429 | newFrontendActionFactory<SyntaxOnlyAction>()); |
| 430 | |
| 431 | CommandLineArguments FinalArgs; |
| 432 | ArgumentsAdjuster CheckFlagsAdjuster = |
| 433 | [&FinalArgs](const CommandLineArguments &Args, StringRef ) { |
| 434 | FinalArgs = Args; |
| 435 | return Args; |
| 436 | }; |
| 437 | Tool.clearArgumentsAdjusters(); |
| 438 | Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); |
| 439 | Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); |
| 440 | Tool.run(Action.get()); |
| 441 | |
| 442 | auto HasFlag = [&FinalArgs](const std::string &Flag) { |
| 443 | return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) != |
| 444 | FinalArgs.end(); |
| 445 | }; |
| 446 | EXPECT_FALSE(HasFlag("-MD")); |
| 447 | EXPECT_FALSE(HasFlag("-MMD")); |
| 448 | EXPECT_TRUE(HasFlag("-c")); |
| 449 | EXPECT_TRUE(HasFlag("-w")); |
| 450 | } |
| 451 | |
| 452 | |
| 453 | TEST(ClangToolTest, StripPluginsAdjuster) { |
| 454 | FixedCompilationDatabase Compilations( |
| 455 | "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"}); |
| 456 | |
| 457 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); |
| 458 | Tool.mapVirtualFile("/a.cc", "void a() {}"); |
| 459 | |
| 460 | std::unique_ptr<FrontendActionFactory> Action( |
| 461 | newFrontendActionFactory<SyntaxOnlyAction>()); |
| 462 | |
| 463 | CommandLineArguments FinalArgs; |
| 464 | ArgumentsAdjuster CheckFlagsAdjuster = |
| 465 | [&FinalArgs](const CommandLineArguments &Args, StringRef ) { |
| 466 | FinalArgs = Args; |
| 467 | return Args; |
| 468 | }; |
| 469 | Tool.clearArgumentsAdjusters(); |
| 470 | Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); |
| 471 | Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); |
| 472 | Tool.run(Action.get()); |
| 473 | |
| 474 | auto HasFlag = [&FinalArgs](const std::string &Flag) { |
| 475 | return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) != |
| 476 | FinalArgs.end(); |
| 477 | }; |
| 478 | EXPECT_FALSE(HasFlag("-Xclang")); |
| 479 | EXPECT_FALSE(HasFlag("-add-plugin")); |
| 480 | EXPECT_FALSE(HasFlag("-random-plugin")); |
| 481 | } |
| 482 | |
| 483 | namespace { |
| 484 | |
| 485 | |
| 486 | |
| 487 | std::string getAnyTarget() { |
| 488 | llvm::InitializeAllTargets(); |
| 489 | for (const auto &Target : llvm::TargetRegistry::targets()) { |
| 490 | std::string Error; |
| 491 | StringRef TargetName(Target.getName()); |
| 492 | if (TargetName == "x86-64") |
| 493 | TargetName = "x86_64"; |
| 494 | if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) { |
| 495 | return TargetName; |
| 496 | } |
| 497 | } |
| 498 | return ""; |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | TEST(addTargetAndModeForProgramName, AddsTargetAndMode) { |
| 503 | std::string Target = getAnyTarget(); |
| 504 | ASSERT_FALSE(Target.empty()); |
| 505 | |
| 506 | std::vector<std::string> Args = {"clang", "-foo"}; |
| 507 | addTargetAndModeForProgramName(Args, ""); |
| 508 | EXPECT_EQ((std::vector<std::string>{"clang", "-foo"}), Args); |
| 509 | addTargetAndModeForProgramName(Args, Target + "-g++"); |
| 510 | EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, |
| 511 | "--driver-mode=g++", "-foo"}), |
| 512 | Args); |
| 513 | } |
| 514 | |
| 515 | TEST(addTargetAndModeForProgramName, PathIgnored) { |
| 516 | std::string Target = getAnyTarget(); |
| 517 | ASSERT_FALSE(Target.empty()); |
| 518 | |
| 519 | SmallString<32> ToolPath; |
| 520 | llvm::sys::path::append(ToolPath, "foo", "bar", Target + "-g++"); |
| 521 | |
| 522 | std::vector<std::string> Args = {"clang", "-foo"}; |
| 523 | addTargetAndModeForProgramName(Args, ToolPath); |
| 524 | EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, |
| 525 | "--driver-mode=g++", "-foo"}), |
| 526 | Args); |
| 527 | } |
| 528 | |
| 529 | TEST(addTargetAndModeForProgramName, IgnoresExistingTarget) { |
| 530 | std::string Target = getAnyTarget(); |
| 531 | ASSERT_FALSE(Target.empty()); |
| 532 | |
| 533 | std::vector<std::string> Args = {"clang", "-foo", "-target", "something"}; |
| 534 | addTargetAndModeForProgramName(Args, Target + "-g++"); |
| 535 | EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", |
| 536 | "-target", "something"}), |
| 537 | Args); |
| 538 | |
| 539 | std::vector<std::string> ArgsAlt = {"clang", "-foo", "-target=something"}; |
| 540 | addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); |
| 541 | EXPECT_EQ((std::vector<std::string>{"clang", "--driver-mode=g++", "-foo", |
| 542 | "-target=something"}), |
| 543 | ArgsAlt); |
| 544 | } |
| 545 | |
| 546 | TEST(addTargetAndModeForProgramName, IgnoresExistingMode) { |
| 547 | std::string Target = getAnyTarget(); |
| 548 | ASSERT_FALSE(Target.empty()); |
| 549 | |
| 550 | std::vector<std::string> Args = {"clang", "-foo", "--driver-mode=abc"}; |
| 551 | addTargetAndModeForProgramName(Args, Target + "-g++"); |
| 552 | EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", |
| 553 | "--driver-mode=abc"}), |
| 554 | Args); |
| 555 | |
| 556 | std::vector<std::string> ArgsAlt = {"clang", "-foo", "--driver-mode", "abc"}; |
| 557 | addTargetAndModeForProgramName(ArgsAlt, Target + "-g++"); |
| 558 | EXPECT_EQ((std::vector<std::string>{"clang", "-target", Target, "-foo", |
| 559 | "--driver-mode", "abc"}), |
| 560 | ArgsAlt); |
| 561 | } |
| 562 | |
| 563 | #ifndef _WIN32 |
| 564 | TEST(ClangToolTest, BuildASTs) { |
| 565 | FixedCompilationDatabase Compilations("/", std::vector<std::string>()); |
| 566 | |
| 567 | std::vector<std::string> Sources; |
| 568 | Sources.push_back("/a.cc"); |
| 569 | Sources.push_back("/b.cc"); |
| 570 | ClangTool Tool(Compilations, Sources); |
| 571 | |
| 572 | Tool.mapVirtualFile("/a.cc", "void a() {}"); |
| 573 | Tool.mapVirtualFile("/b.cc", "void b() {}"); |
| 574 | |
| 575 | std::vector<std::unique_ptr<ASTUnit>> ASTs; |
| 576 | EXPECT_EQ(0, Tool.buildASTs(ASTs)); |
| 577 | EXPECT_EQ(2u, ASTs.size()); |
| 578 | } |
| 579 | |
| 580 | struct TestDiagnosticConsumer : public DiagnosticConsumer { |
| 581 | TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} |
| 582 | void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, |
| 583 | const Diagnostic &Info) override { |
| 584 | ++NumDiagnosticsSeen; |
| 585 | } |
| 586 | unsigned NumDiagnosticsSeen; |
| 587 | }; |
| 588 | |
| 589 | TEST(ClangToolTest, InjectDiagnosticConsumer) { |
| 590 | FixedCompilationDatabase Compilations("/", std::vector<std::string>()); |
| 591 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); |
| 592 | Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); |
| 593 | TestDiagnosticConsumer Consumer; |
| 594 | Tool.setDiagnosticConsumer(&Consumer); |
| 595 | std::unique_ptr<FrontendActionFactory> Action( |
| 596 | newFrontendActionFactory<SyntaxOnlyAction>()); |
| 597 | Tool.run(Action.get()); |
| 598 | EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); |
| 599 | } |
| 600 | |
| 601 | TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { |
| 602 | FixedCompilationDatabase Compilations("/", std::vector<std::string>()); |
| 603 | ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); |
| 604 | Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); |
| 605 | TestDiagnosticConsumer Consumer; |
| 606 | Tool.setDiagnosticConsumer(&Consumer); |
| 607 | std::vector<std::unique_ptr<ASTUnit>> ASTs; |
| 608 | Tool.buildASTs(ASTs); |
| 609 | EXPECT_EQ(1u, ASTs.size()); |
| 610 | EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); |
| 611 | } |
| 612 | #endif |
| 613 | |
| 614 | TEST(runToolOnCode, TestResetDiagnostics) { |
| 615 | |
| 616 | struct ResetDiagnosticAction : public clang::ASTFrontendAction { |
| 617 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
| 618 | StringRef) override { |
| 619 | struct Consumer : public clang::ASTConsumer { |
| 620 | bool HandleTopLevelDecl(clang::DeclGroupRef D) override { |
| 621 | auto &Diags = (*D.begin())->getASTContext().getDiagnostics(); |
| 622 | |
| 623 | Diags.Reset(); |
| 624 | |
| 625 | Diags.setIgnoreAllWarnings(true); |
| 626 | return true; |
| 627 | } |
| 628 | }; |
| 629 | return llvm::make_unique<Consumer>(); |
| 630 | } |
| 631 | }; |
| 632 | |
| 633 | |
| 634 | EXPECT_FALSE( |
| 635 | runToolOnCode(new ResetDiagnosticAction, |
| 636 | "struct Foo { Foo(int); ~Foo(); struct Fwd _fwd; };" |
| 637 | "void func() { long x; Foo f(x); }")); |
| 638 | } |
| 639 | |
| 640 | } |
| 641 | } |
| 642 | |