CompilationDatabaseTest.cpp 29 KB

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