CompilationDatabaseTest.cpp 32 KB

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