1 //===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===// 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/DeclCXX.h" 11 #include "clang/AST/DeclGroup.h" 12 #include "clang/Frontend/FrontendAction.h" 13 #include "clang/Tooling/FileMatchTrie.h" 14 #include "clang/Tooling/JSONCompilationDatabase.h" 15 #include "clang/Tooling/Tooling.h" 16 #include "llvm/Support/Path.h" 17 #include "gtest/gtest.h" 18 19 namespace clang { 20 namespace tooling { 21 22 static void expectFailure(StringRef JSONDatabase, StringRef Explanation) { 23 std::string ErrorMessage; 24 EXPECT_EQ(nullptr, 25 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, 26 JSONCommandLineSyntax::Gnu)) 27 << "Expected an error because of: " << Explanation.str(); 28 } 29 30 TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) { 31 expectFailure("", "Empty database"); 32 expectFailure("{", "Invalid JSON"); 33 expectFailure("[[]]", "Array instead of object"); 34 expectFailure("[{\"a\":[]}]", "Array instead of value"); 35 expectFailure("[{\"a\":\"b\"}]", "Unknown key"); 36 expectFailure("[{[]:\"\"}]", "Incorrectly typed entry"); 37 expectFailure("[{}]", "Empty entry"); 38 expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file"); 39 expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command or arguments"); 40 expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory"); 41 expectFailure("[{\"directory\":\"\",\"arguments\":[]}]", "Missing file"); 42 expectFailure("[{\"arguments\":\"\",\"file\":\"\"}]", "Missing directory"); 43 expectFailure("[{\"directory\":\"\",\"arguments\":\"\",\"file\":\"\"}]", "Arguments not array"); 44 expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string"); 45 expectFailure("[{\"directory\":\"\",\"arguments\":[[]],\"file\":\"\"}]", 46 "Arguments contain non-string"); 47 expectFailure("[{\"output\":[]}]", "Expected strings as value."); 48 } 49 50 static std::vector<std::string> getAllFiles(StringRef JSONDatabase, 51 std::string &ErrorMessage, 52 JSONCommandLineSyntax Syntax) { 53 std::unique_ptr<CompilationDatabase> Database( 54 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, 55 Syntax)); 56 if (!Database) { 57 ADD_FAILURE() << ErrorMessage; 58 return std::vector<std::string>(); 59 } 60 return Database->getAllFiles(); 61 } 62 63 static std::vector<CompileCommand> 64 getAllCompileCommands(JSONCommandLineSyntax Syntax, StringRef JSONDatabase, 65 std::string &ErrorMessage) { 66 std::unique_ptr<CompilationDatabase> Database( 67 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, 68 Syntax)); 69 if (!Database) { 70 ADD_FAILURE() << ErrorMessage; 71 return std::vector<CompileCommand>(); 72 } 73 return Database->getAllCompileCommands(); 74 } 75 76 TEST(JSONCompilationDatabase, GetAllFiles) { 77 std::string ErrorMessage; 78 EXPECT_EQ(std::vector<std::string>(), 79 getAllFiles("[]", ErrorMessage, JSONCommandLineSyntax::Gnu)) 80 << ErrorMessage; 81 82 std::vector<std::string> expected_files; 83 SmallString<16> PathStorage; 84 llvm::sys::path::native("//net/dir/file1", PathStorage); 85 expected_files.push_back(PathStorage.str()); 86 llvm::sys::path::native("//net/dir/file2", PathStorage); 87 expected_files.push_back(PathStorage.str()); 88 EXPECT_EQ(expected_files, 89 getAllFiles("[{\"directory\":\"//net/dir\"," 90 "\"command\":\"command\"," 91 "\"file\":\"file1\"}," 92 " {\"directory\":\"//net/dir\"," 93 "\"command\":\"command\"," 94 "\"file\":\"file2\"}]", 95 ErrorMessage, JSONCommandLineSyntax::Gnu)) 96 << ErrorMessage; 97 } 98 99 TEST(JSONCompilationDatabase, GetAllCompileCommands) { 100 std::string ErrorMessage; 101 EXPECT_EQ( 102 0u, getAllCompileCommands(JSONCommandLineSyntax::Gnu, "[]", ErrorMessage) 103 .size()) 104 << ErrorMessage; 105 106 StringRef Directory1("//net/dir1"); 107 StringRef FileName1("file1"); 108 StringRef Command1("command1"); 109 StringRef Output1("file1.o"); 110 StringRef Directory2("//net/dir2"); 111 StringRef FileName2("file2"); 112 StringRef Command2("command2"); 113 StringRef Output2(""); 114 115 std::vector<CompileCommand> Commands = getAllCompileCommands( 116 JSONCommandLineSyntax::Gnu, 117 ("[{\"directory\":\"" + Directory1 + "\"," + "\"command\":\"" + Command1 + 118 "\"," 119 "\"file\":\"" + 120 FileName1 + "\", \"output\":\"" + 121 Output1 + "\"}," 122 " {\"directory\":\"" + 123 Directory2 + "\"," + "\"command\":\"" + Command2 + "\"," 124 "\"file\":\"" + 125 FileName2 + "\"}]") 126 .str(), 127 ErrorMessage); 128 EXPECT_EQ(2U, Commands.size()) << ErrorMessage; 129 EXPECT_EQ(Directory1, Commands[0].Directory) << ErrorMessage; 130 EXPECT_EQ(FileName1, Commands[0].Filename) << ErrorMessage; 131 EXPECT_EQ(Output1, Commands[0].Output) << ErrorMessage; 132 ASSERT_EQ(1u, Commands[0].CommandLine.size()); 133 EXPECT_EQ(Command1, Commands[0].CommandLine[0]) << ErrorMessage; 134 EXPECT_EQ(Directory2, Commands[1].Directory) << ErrorMessage; 135 EXPECT_EQ(FileName2, Commands[1].Filename) << ErrorMessage; 136 EXPECT_EQ(Output2, Commands[1].Output) << ErrorMessage; 137 ASSERT_EQ(1u, Commands[1].CommandLine.size()); 138 EXPECT_EQ(Command2, Commands[1].CommandLine[0]) << ErrorMessage; 139 140 // Check that order is preserved. 141 Commands = getAllCompileCommands( 142 JSONCommandLineSyntax::Gnu, 143 ("[{\"directory\":\"" + Directory2 + "\"," + "\"command\":\"" + Command2 + 144 "\"," 145 "\"file\":\"" + 146 FileName2 + "\"}," 147 " {\"directory\":\"" + 148 Directory1 + "\"," + "\"command\":\"" + Command1 + "\"," 149 "\"file\":\"" + 150 FileName1 + "\"}]") 151 .str(), 152 ErrorMessage); 153 EXPECT_EQ(2U, Commands.size()) << ErrorMessage; 154 EXPECT_EQ(Directory2, Commands[0].Directory) << ErrorMessage; 155 EXPECT_EQ(FileName2, Commands[0].Filename) << ErrorMessage; 156 ASSERT_EQ(1u, Commands[0].CommandLine.size()); 157 EXPECT_EQ(Command2, Commands[0].CommandLine[0]) << ErrorMessage; 158 EXPECT_EQ(Directory1, Commands[1].Directory) << ErrorMessage; 159 EXPECT_EQ(FileName1, Commands[1].Filename) << ErrorMessage; 160 ASSERT_EQ(1u, Commands[1].CommandLine.size()); 161 EXPECT_EQ(Command1, Commands[1].CommandLine[0]) << ErrorMessage; 162 } 163 164 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, 165 StringRef JSONDatabase, 166 std::string &ErrorMessage) { 167 std::unique_ptr<CompilationDatabase> Database( 168 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, 169 JSONCommandLineSyntax::Gnu)); 170 if (!Database) 171 return CompileCommand(); 172 std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName); 173 EXPECT_LE(Commands.size(), 1u); 174 if (Commands.empty()) 175 return CompileCommand(); 176 return Commands[0]; 177 } 178 179 TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) { 180 StringRef Directory("//net/dir"); 181 StringRef FileName("//net/dir/filename"); 182 StringRef Command("command"); 183 StringRef Arguments = "arguments"; 184 Twine ArgumentsAccumulate; 185 std::string ErrorMessage; 186 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 187 FileName, 188 ("[{\"directory\":\"" + Directory + "\"," 189 "\"arguments\":[\"" + Arguments + "\"]," 190 "\"command\":\"" + Command + "\"," 191 "\"file\":\"" + FileName + "\"}]").str(), 192 ErrorMessage); 193 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 194 EXPECT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage; 195 EXPECT_EQ(Arguments, FoundCommand.CommandLine[0]) << ErrorMessage; 196 } 197 198 struct FakeComparator : public PathComparator { 199 ~FakeComparator() override {} 200 bool equivalent(StringRef FileA, StringRef FileB) const override { 201 return FileA.equals_lower(FileB); 202 } 203 }; 204 205 class FileMatchTrieTest : public ::testing::Test { 206 protected: 207 FileMatchTrieTest() : Trie(new FakeComparator()) {} 208 209 StringRef find(StringRef Path) { 210 llvm::raw_string_ostream ES(Error); 211 return Trie.findEquivalent(Path, ES); 212 } 213 214 FileMatchTrie Trie; 215 std::string Error; 216 }; 217 218 TEST_F(FileMatchTrieTest, InsertingRelativePath) { 219 Trie.insert("//net/path/file.cc"); 220 Trie.insert("file.cc"); 221 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc")); 222 } 223 224 TEST_F(FileMatchTrieTest, MatchingRelativePath) { 225 EXPECT_EQ("", find("file.cc")); 226 } 227 228 TEST_F(FileMatchTrieTest, ReturnsBestResults) { 229 Trie.insert("//net/d/c/b.cc"); 230 Trie.insert("//net/d/b/b.cc"); 231 EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc")); 232 } 233 234 TEST_F(FileMatchTrieTest, HandlesSymlinks) { 235 Trie.insert("//net/AA/file.cc"); 236 EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc")); 237 } 238 239 TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) { 240 Trie.insert("//net/Aa/file.cc"); 241 Trie.insert("//net/aA/file.cc"); 242 EXPECT_TRUE(find("//net/aa/file.cc").empty()); 243 EXPECT_EQ("Path is ambiguous", Error); 244 } 245 246 TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) { 247 Trie.insert("//net/src/Aa/file.cc"); 248 Trie.insert("//net/src/aA/file.cc"); 249 Trie.insert("//net/SRC/aa/file.cc"); 250 EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc")); 251 } 252 253 TEST_F(FileMatchTrieTest, EmptyTrie) { 254 EXPECT_TRUE(find("//net/some/path").empty()); 255 } 256 257 TEST_F(FileMatchTrieTest, NoResult) { 258 Trie.insert("//net/somepath/otherfile.cc"); 259 Trie.insert("//net/otherpath/somefile.cc"); 260 EXPECT_EQ("", find("//net/somepath/somefile.cc")); 261 } 262 263 TEST_F(FileMatchTrieTest, RootElementDifferent) { 264 Trie.insert("//net/path/file.cc"); 265 Trie.insert("//net/otherpath/file.cc"); 266 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc")); 267 } 268 269 TEST_F(FileMatchTrieTest, CannotResolveRelativePath) { 270 EXPECT_EQ("", find("relative-path.cc")); 271 EXPECT_EQ("Cannot resolve relative paths", Error); 272 } 273 274 TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) { 275 std::string ErrorMessage; 276 CompileCommand NotFound = findCompileArgsInJsonDatabase( 277 "a-file.cpp", "", ErrorMessage); 278 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; 279 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; 280 } 281 282 TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) { 283 StringRef Directory("//net/some/directory"); 284 StringRef FileName("//net/path/to/a-file.cpp"); 285 StringRef Command("//net/path/to/compiler and some arguments"); 286 std::string ErrorMessage; 287 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 288 FileName, 289 ("[{\"directory\":\"" + Directory + "\"," + 290 "\"command\":\"" + Command + "\"," 291 "\"file\":\"" + FileName + "\"}]").str(), 292 ErrorMessage); 293 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 294 ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage; 295 EXPECT_EQ("//net/path/to/compiler", 296 FoundCommand.CommandLine[0]) << ErrorMessage; 297 EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage; 298 EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage; 299 EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage; 300 301 CompileCommand NotFound = findCompileArgsInJsonDatabase( 302 "a-file.cpp", 303 ("[{\"directory\":\"" + Directory + "\"," + 304 "\"command\":\"" + Command + "\"," 305 "\"file\":\"" + FileName + "\"}]").str(), 306 ErrorMessage); 307 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; 308 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; 309 } 310 311 TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) { 312 StringRef Directory("//net/some/directory"); 313 StringRef FileName("//net/path/to/a-file.cpp"); 314 StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\""); 315 std::string ErrorMessage; 316 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 317 FileName, 318 ("[{\"directory\":\"" + Directory + "\"," + 319 "\"command\":\"" + Command + "\"," 320 "\"file\":\"" + FileName + "\"}]").str(), 321 ErrorMessage); 322 ASSERT_EQ(2u, FoundCommand.CommandLine.size()); 323 EXPECT_EQ("//net/path to compiler", 324 FoundCommand.CommandLine[0]) << ErrorMessage; 325 EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage; 326 } 327 328 TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) { 329 StringRef Directory("//net/some directory / with spaces"); 330 StringRef FileName("//net/path/to/a-file.cpp"); 331 StringRef Command("a command"); 332 std::string ErrorMessage; 333 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 334 FileName, 335 ("[{\"directory\":\"" + Directory + "\"," + 336 "\"command\":\"" + Command + "\"," 337 "\"file\":\"" + FileName + "\"}]").str(), 338 ErrorMessage); 339 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 340 } 341 342 TEST(findCompileArgsInJsonDatabase, FindsEntry) { 343 StringRef Directory("//net/directory"); 344 StringRef FileName("file"); 345 StringRef Command("command"); 346 std::string JsonDatabase = "["; 347 for (int I = 0; I < 10; ++I) { 348 if (I > 0) JsonDatabase += ","; 349 JsonDatabase += 350 ("{\"directory\":\"" + Directory + Twine(I) + "\"," + 351 "\"command\":\"" + Command + Twine(I) + "\"," 352 "\"file\":\"" + FileName + Twine(I) + "\"}").str(); 353 } 354 JsonDatabase += "]"; 355 std::string ErrorMessage; 356 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 357 "//net/directory4/file4", JsonDatabase, ErrorMessage); 358 EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage; 359 ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage; 360 EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage; 361 } 362 363 static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) { 364 std::string JsonDatabase = 365 ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" + 366 Command + "\"}]").str(); 367 std::string ErrorMessage; 368 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 369 "//net/root/test", JsonDatabase, ErrorMessage); 370 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage; 371 return FoundCommand.CommandLine; 372 } 373 374 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) { 375 std::vector<std::string> Result = unescapeJsonCommandLine(""); 376 EXPECT_TRUE(Result.empty()); 377 } 378 379 TEST(unescapeJsonCommandLine, SplitsOnSpaces) { 380 std::vector<std::string> Result = unescapeJsonCommandLine("a b c"); 381 ASSERT_EQ(3ul, Result.size()); 382 EXPECT_EQ("a", Result[0]); 383 EXPECT_EQ("b", Result[1]); 384 EXPECT_EQ("c", Result[2]); 385 } 386 387 TEST(unescapeJsonCommandLine, MungesMultipleSpaces) { 388 std::vector<std::string> Result = unescapeJsonCommandLine(" a b "); 389 ASSERT_EQ(2ul, Result.size()); 390 EXPECT_EQ("a", Result[0]); 391 EXPECT_EQ("b", Result[1]); 392 } 393 394 TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) { 395 std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\"); 396 ASSERT_EQ(1ul, Backslash.size()); 397 EXPECT_EQ("a\\", Backslash[0]); 398 std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\""); 399 ASSERT_EQ(1ul, Quote.size()); 400 EXPECT_EQ("a\"", Quote[0]); 401 } 402 403 TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) { 404 std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\""); 405 ASSERT_EQ(1ul, Result.size()); 406 EXPECT_EQ(" a b ", Result[0]); 407 } 408 409 TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) { 410 std::vector<std::string> Result = unescapeJsonCommandLine( 411 " \\\" a \\\" \\\" b \\\" "); 412 ASSERT_EQ(2ul, Result.size()); 413 EXPECT_EQ(" a ", Result[0]); 414 EXPECT_EQ(" b ", Result[1]); 415 } 416 417 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) { 418 std::vector<std::string> Result = unescapeJsonCommandLine( 419 "\\\"\\\"\\\"\\\""); 420 ASSERT_EQ(1ul, Result.size()); 421 EXPECT_TRUE(Result[0].empty()) << Result[0]; 422 } 423 424 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) { 425 std::vector<std::string> Result = unescapeJsonCommandLine( 426 "\\\"\\\\\\\"\\\""); 427 ASSERT_EQ(1ul, Result.size()); 428 EXPECT_EQ("\"", Result[0]); 429 } 430 431 TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) { 432 std::vector<std::string> Result = unescapeJsonCommandLine( 433 " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\""); 434 ASSERT_EQ(4ul, Result.size()); 435 EXPECT_EQ("\"", Result[0]); 436 EXPECT_EQ("a \" b ", Result[1]); 437 EXPECT_EQ("and\\c", Result[2]); 438 EXPECT_EQ("\"", Result[3]); 439 } 440 441 TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) { 442 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine( 443 "\\\"a\\\"\\\"b\\\""); 444 ASSERT_EQ(1ul, QuotedNoSpaces.size()); 445 EXPECT_EQ("ab", QuotedNoSpaces[0]); 446 447 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine( 448 "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\""); 449 ASSERT_EQ(1ul, MixedNoSpaces.size()); 450 EXPECT_EQ("abcdefg", MixedNoSpaces[0]); 451 } 452 453 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) { 454 std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc"); 455 ASSERT_EQ(1ul, Unclosed.size()); 456 EXPECT_EQ("abc", Unclosed[0]); 457 458 std::vector<std::string> Empty = unescapeJsonCommandLine("\\\""); 459 ASSERT_EQ(1ul, Empty.size()); 460 EXPECT_EQ("", Empty[0]); 461 } 462 463 TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) { 464 std::vector<std::string> Args = unescapeJsonCommandLine("a'\\\\b \\\"c\\\"'"); 465 ASSERT_EQ(1ul, Args.size()); 466 EXPECT_EQ("a\\b \"c\"", Args[0]); 467 } 468 469 TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) { 470 std::vector<std::string> CommandLine; 471 CommandLine.push_back("one"); 472 CommandLine.push_back("two"); 473 FixedCompilationDatabase Database(".", CommandLine); 474 StringRef FileName("source"); 475 std::vector<CompileCommand> Result = 476 Database.getCompileCommands(FileName); 477 ASSERT_EQ(1ul, Result.size()); 478 std::vector<std::string> ExpectedCommandLine(1, "clang-tool"); 479 ExpectedCommandLine.insert(ExpectedCommandLine.end(), 480 CommandLine.begin(), CommandLine.end()); 481 ExpectedCommandLine.push_back("source"); 482 EXPECT_EQ(".", Result[0].Directory); 483 EXPECT_EQ(FileName, Result[0].Filename); 484 EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine); 485 } 486 487 TEST(FixedCompilationDatabase, GetAllFiles) { 488 std::vector<std::string> CommandLine; 489 CommandLine.push_back("one"); 490 CommandLine.push_back("two"); 491 FixedCompilationDatabase Database(".", CommandLine); 492 493 EXPECT_EQ(0ul, Database.getAllFiles().size()); 494 } 495 496 TEST(FixedCompilationDatabase, GetAllCompileCommands) { 497 std::vector<std::string> CommandLine; 498 CommandLine.push_back("one"); 499 CommandLine.push_back("two"); 500 FixedCompilationDatabase Database(".", CommandLine); 501 502 EXPECT_EQ(0ul, Database.getAllCompileCommands().size()); 503 } 504 505 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) { 506 int Argc = 0; 507 std::string ErrorMsg; 508 std::unique_ptr<FixedCompilationDatabase> Database = 509 FixedCompilationDatabase::loadFromCommandLine(Argc, nullptr, ErrorMsg); 510 EXPECT_FALSE(Database); 511 EXPECT_TRUE(ErrorMsg.empty()); 512 EXPECT_EQ(0, Argc); 513 } 514 515 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) { 516 int Argc = 2; 517 const char *Argv[] = { "1", "2" }; 518 std::string ErrorMsg; 519 std::unique_ptr<FixedCompilationDatabase> Database( 520 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg)); 521 EXPECT_FALSE(Database); 522 EXPECT_TRUE(ErrorMsg.empty()); 523 EXPECT_EQ(2, Argc); 524 } 525 526 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) { 527 int Argc = 5; 528 const char *Argv[] = { 529 "1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4" 530 }; 531 std::string ErrorMsg; 532 std::unique_ptr<FixedCompilationDatabase> Database( 533 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg)); 534 ASSERT_TRUE((bool)Database); 535 ASSERT_TRUE(ErrorMsg.empty()); 536 std::vector<CompileCommand> Result = 537 Database->getCompileCommands("source"); 538 ASSERT_EQ(1ul, Result.size()); 539 ASSERT_EQ(".", Result[0].Directory); 540 std::vector<std::string> CommandLine; 541 CommandLine.push_back("clang-tool"); 542 CommandLine.push_back("-DDEF3"); 543 CommandLine.push_back("-DDEF4"); 544 CommandLine.push_back("source"); 545 ASSERT_EQ(CommandLine, Result[0].CommandLine); 546 EXPECT_EQ(2, Argc); 547 } 548 549 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) { 550 int Argc = 3; 551 const char *Argv[] = { "1", "2", "--\0no-constant-folding" }; 552 std::string ErrorMsg; 553 std::unique_ptr<FixedCompilationDatabase> Database = 554 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg); 555 ASSERT_TRUE((bool)Database); 556 ASSERT_TRUE(ErrorMsg.empty()); 557 std::vector<CompileCommand> Result = 558 Database->getCompileCommands("source"); 559 ASSERT_EQ(1ul, Result.size()); 560 ASSERT_EQ(".", Result[0].Directory); 561 std::vector<std::string> CommandLine; 562 CommandLine.push_back("clang-tool"); 563 CommandLine.push_back("source"); 564 ASSERT_EQ(CommandLine, Result[0].CommandLine); 565 EXPECT_EQ(2, Argc); 566 } 567 568 TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) { 569 const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"}; 570 int Argc = sizeof(Argv) / sizeof(char*); 571 std::string ErrorMsg; 572 std::unique_ptr<FixedCompilationDatabase> Database = 573 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg); 574 ASSERT_TRUE((bool)Database); 575 ASSERT_TRUE(ErrorMsg.empty()); 576 std::vector<CompileCommand> Result = 577 Database->getCompileCommands("source"); 578 ASSERT_EQ(1ul, Result.size()); 579 ASSERT_EQ(".", Result[0].Directory); 580 std::vector<std::string> Expected; 581 Expected.push_back("clang-tool"); 582 Expected.push_back("-c"); 583 Expected.push_back("-DDEF3"); 584 Expected.push_back("source"); 585 ASSERT_EQ(Expected, Result[0].CommandLine); 586 EXPECT_EQ(2, Argc); 587 } 588 589 TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) { 590 // Adjust the given command line arguments to ensure that any positional 591 // arguments in them are stripped. 592 const char *Argv[] = {"--", "somefile.cpp", "-fsyntax-only", "-DDEF3"}; 593 int Argc = llvm::array_lengthof(Argv); 594 std::string ErrorMessage; 595 std::unique_ptr<CompilationDatabase> Database = 596 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMessage); 597 ASSERT_TRUE((bool)Database); 598 ASSERT_TRUE(ErrorMessage.empty()); 599 std::vector<CompileCommand> Result = Database->getCompileCommands("source"); 600 ASSERT_EQ(1ul, Result.size()); 601 ASSERT_EQ(".", Result[0].Directory); 602 std::vector<std::string> Expected; 603 Expected.push_back("clang-tool"); 604 Expected.push_back("-fsyntax-only"); 605 Expected.push_back("-DDEF3"); 606 Expected.push_back("source"); 607 ASSERT_EQ(Expected, Result[0].CommandLine); 608 } 609 610 TEST(ParseFixedCompilationDatabase, HandlesArgv0) { 611 const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"}; 612 int Argc = sizeof(Argv) / sizeof(char*); 613 std::string ErrorMsg; 614 std::unique_ptr<FixedCompilationDatabase> Database = 615 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg); 616 ASSERT_TRUE((bool)Database); 617 ASSERT_TRUE(ErrorMsg.empty()); 618 std::vector<CompileCommand> Result = 619 Database->getCompileCommands("source"); 620 ASSERT_EQ(1ul, Result.size()); 621 ASSERT_EQ(".", Result[0].Directory); 622 std::vector<std::string> Expected; 623 Expected.push_back("clang-tool"); 624 Expected.push_back("source"); 625 ASSERT_EQ(Expected, Result[0].CommandLine); 626 EXPECT_EQ(2, Argc); 627 } 628 629 struct MemCDB : public CompilationDatabase { 630 using EntryMap = llvm::StringMap<SmallVector<CompileCommand, 1>>; 631 EntryMap Entries; 632 MemCDB(const EntryMap &E) : Entries(E) {} 633 634 std::vector<CompileCommand> getCompileCommands(StringRef F) const override { 635 auto Ret = Entries.lookup(F); 636 return {Ret.begin(), Ret.end()}; 637 } 638 639 std::vector<std::string> getAllFiles() const override { 640 std::vector<std::string> Result; 641 for (const auto &Entry : Entries) 642 Result.push_back(Entry.first()); 643 return Result; 644 } 645 }; 646 647 class InterpolateTest : public ::testing::Test { 648 protected: 649 // Adds an entry to the underlying compilation database. 650 // A flag is injected: -D <File>, so the command used can be identified. 651 void add(llvm::StringRef File, llvm::StringRef Flags = "") { 652 llvm::SmallVector<StringRef, 8> Argv = {"clang", File, "-D", File}; 653 llvm::SplitString(Flags, Argv); 654 llvm::SmallString<32> Dir; 655 llvm::sys::path::system_temp_directory(false, Dir); 656 Entries[path(File)].push_back( 657 {Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"}); 658 } 659 660 // Turn a unix path fragment (foo/bar.h) into a native path (C:\tmp\foo\bar.h) 661 std::string path(llvm::SmallString<32> File) { 662 llvm::SmallString<32> Dir; 663 llvm::sys::path::system_temp_directory(false, Dir); 664 llvm::sys::path::native(File); 665 llvm::SmallString<64> Result; 666 llvm::sys::path::append(Result, Dir, File); 667 return Result.str(); 668 } 669 670 // Look up the command from a relative path, and return it in string form. 671 // The input file is not included in the returned command. 672 std::string getCommand(llvm::StringRef F) { 673 auto Results = 674 inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries)) 675 ->getCompileCommands(path(F)); 676 if (Results.empty()) 677 return "none"; 678 // drop the input file argument, so tests don't have to deal with path(). 679 EXPECT_EQ(Results[0].CommandLine.back(), path(F)) 680 << "Last arg should be the file"; 681 Results[0].CommandLine.pop_back(); 682 return llvm::join(Results[0].CommandLine, " "); 683 } 684 685 MemCDB::EntryMap Entries; 686 }; 687 688 TEST_F(InterpolateTest, Nearby) { 689 add("dir/foo.cpp"); 690 add("dir/bar.cpp"); 691 add("an/other/foo.cpp"); 692 693 // great: dir and name both match (prefix or full, case insensitive) 694 EXPECT_EQ(getCommand("dir/f.cpp"), "clang -D dir/foo.cpp"); 695 EXPECT_EQ(getCommand("dir/FOO.cpp"), "clang -D dir/foo.cpp"); 696 // no name match. prefer matching dir, break ties by alpha 697 EXPECT_EQ(getCommand("dir/a.cpp"), "clang -D dir/bar.cpp"); 698 // an exact name match beats one segment of directory match 699 EXPECT_EQ(getCommand("some/other/bar.h"), 700 "clang -D dir/bar.cpp -x c++-header"); 701 // two segments of directory match beat a prefix name match 702 EXPECT_EQ(getCommand("an/other/b.cpp"), "clang -D an/other/foo.cpp"); 703 // if nothing matches at all, we still get the closest alpha match 704 EXPECT_EQ(getCommand("below/some/obscure/path.cpp"), 705 "clang -D an/other/foo.cpp"); 706 } 707 708 TEST_F(InterpolateTest, Language) { 709 add("dir/foo.cpp", "-std=c++17"); 710 add("dir/bar.c", ""); 711 add("dir/baz.cee", "-x c"); 712 713 // .h is ambiguous, so we add explicit language flags 714 EXPECT_EQ(getCommand("foo.h"), 715 "clang -D dir/foo.cpp -x c++-header -std=c++17"); 716 // and don't add -x if the inferred language is correct. 717 EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17"); 718 // respect -x if it's already there. 719 EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header"); 720 // prefer a worse match with the right extension. 721 EXPECT_EQ(getCommand("foo.c"), "clang -D dir/bar.c"); 722 // make sure we don't crash on queries with invalid extensions. 723 EXPECT_EQ(getCommand("foo.cce"), "clang -D dir/foo.cpp"); 724 Entries.erase(path(StringRef("dir/bar.c"))); 725 // Now we transfer across languages, so drop -std too. 726 EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp"); 727 } 728 729 TEST_F(InterpolateTest, Strip) { 730 add("dir/foo.cpp", "-o foo.o -Wall"); 731 // the -o option and the input file are removed, but -Wall is preserved. 732 EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall"); 733 } 734 735 TEST_F(InterpolateTest, Case) { 736 add("FOO/BAR/BAZ/SHOUT.cc"); 737 add("foo/bar/baz/quiet.cc"); 738 // Case mismatches are completely ignored, so we choose the name match. 739 EXPECT_EQ(getCommand("foo/bar/baz/shout.C"), "clang -D FOO/BAR/BAZ/SHOUT.cc"); 740 } 741 742 TEST(CompileCommandTest, EqualityOperator) { 743 CompileCommand CCRef("/foo/bar", "hello.c", {"a", "b"}, "hello.o"); 744 CompileCommand CCTest = CCRef; 745 746 EXPECT_TRUE(CCRef == CCTest); 747 EXPECT_FALSE(CCRef != CCTest); 748 749 CCTest = CCRef; 750 CCTest.Directory = "/foo/baz"; 751 EXPECT_FALSE(CCRef == CCTest); 752 EXPECT_TRUE(CCRef != CCTest); 753 754 CCTest = CCRef; 755 CCTest.Filename = "bonjour.c"; 756 EXPECT_FALSE(CCRef == CCTest); 757 EXPECT_TRUE(CCRef != CCTest); 758 759 CCTest = CCRef; 760 CCTest.CommandLine.push_back("c"); 761 EXPECT_FALSE(CCRef == CCTest); 762 EXPECT_TRUE(CCRef != CCTest); 763 764 CCTest = CCRef; 765 CCTest.Output = "bonjour.o"; 766 EXPECT_FALSE(CCRef == CCTest); 767 EXPECT_TRUE(CCRef != CCTest); 768 } 769 770 } // end namespace tooling 771 } // end namespace clang 772