CompilationDatabaseTest.cpp 30 KB

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