CompilationDatabaseTest.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  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. static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
  338. std::string JsonDatabase =
  339. ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
  340. Command + "\"}]").str();
  341. std::string ErrorMessage;
  342. CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
  343. "//net/root/test", JsonDatabase, ErrorMessage);
  344. EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
  345. return FoundCommand.CommandLine;
  346. }
  347. TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
  348. std::vector<std::string> Result = unescapeJsonCommandLine("");
  349. EXPECT_TRUE(Result.empty());
  350. }
  351. TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
  352. std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
  353. ASSERT_EQ(3ul, Result.size());
  354. EXPECT_EQ("a", Result[0]);
  355. EXPECT_EQ("b", Result[1]);
  356. EXPECT_EQ("c", Result[2]);
  357. }
  358. TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
  359. std::vector<std::string> Result = unescapeJsonCommandLine(" a b ");
  360. ASSERT_EQ(2ul, Result.size());
  361. EXPECT_EQ("a", Result[0]);
  362. EXPECT_EQ("b", Result[1]);
  363. }
  364. TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
  365. std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
  366. ASSERT_EQ(1ul, Backslash.size());
  367. EXPECT_EQ("a\\", Backslash[0]);
  368. std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
  369. ASSERT_EQ(1ul, Quote.size());
  370. EXPECT_EQ("a\"", Quote[0]);
  371. }
  372. TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
  373. std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\"");
  374. ASSERT_EQ(1ul, Result.size());
  375. EXPECT_EQ(" a b ", Result[0]);
  376. }
  377. TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
  378. std::vector<std::string> Result = unescapeJsonCommandLine(
  379. " \\\" a \\\" \\\" b \\\" ");
  380. ASSERT_EQ(2ul, Result.size());
  381. EXPECT_EQ(" a ", Result[0]);
  382. EXPECT_EQ(" b ", Result[1]);
  383. }
  384. TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
  385. std::vector<std::string> Result = unescapeJsonCommandLine(
  386. "\\\"\\\"\\\"\\\"");
  387. ASSERT_EQ(1ul, Result.size());
  388. EXPECT_TRUE(Result[0].empty()) << Result[0];
  389. }
  390. TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
  391. std::vector<std::string> Result = unescapeJsonCommandLine(
  392. "\\\"\\\\\\\"\\\"");
  393. ASSERT_EQ(1ul, Result.size());
  394. EXPECT_EQ("\"", Result[0]);
  395. }
  396. TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
  397. std::vector<std::string> Result = unescapeJsonCommandLine(
  398. " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
  399. ASSERT_EQ(4ul, Result.size());
  400. EXPECT_EQ("\"", Result[0]);
  401. EXPECT_EQ("a \" b ", Result[1]);
  402. EXPECT_EQ("and\\c", Result[2]);
  403. EXPECT_EQ("\"", Result[3]);
  404. }
  405. TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
  406. std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
  407. "\\\"a\\\"\\\"b\\\"");
  408. ASSERT_EQ(1ul, QuotedNoSpaces.size());
  409. EXPECT_EQ("ab", QuotedNoSpaces[0]);
  410. std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
  411. "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
  412. ASSERT_EQ(1ul, MixedNoSpaces.size());
  413. EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
  414. }
  415. TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
  416. std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
  417. ASSERT_EQ(1ul, Unclosed.size());
  418. EXPECT_EQ("abc", Unclosed[0]);
  419. std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
  420. ASSERT_EQ(1ul, Empty.size());
  421. EXPECT_EQ("", Empty[0]);
  422. }
  423. TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) {
  424. std::vector<std::string> Args = unescapeJsonCommandLine("a'\\\\b \\\"c\\\"'");
  425. ASSERT_EQ(1ul, Args.size());
  426. EXPECT_EQ("a\\b \"c\"", Args[0]);
  427. }
  428. TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
  429. FixedCompilationDatabase Database(".", /*CommandLine*/ {"one", "two"});
  430. StringRef FileName("source");
  431. std::vector<CompileCommand> Result =
  432. Database.getCompileCommands(FileName);
  433. ASSERT_EQ(1ul, Result.size());
  434. EXPECT_EQ(".", Result[0].Directory);
  435. EXPECT_EQ(FileName, Result[0].Filename);
  436. EXPECT_THAT(Result[0].CommandLine,
  437. ElementsAre(EndsWith("clang-tool"), "one", "two", "source"));
  438. }
  439. TEST(FixedCompilationDatabase, GetAllFiles) {
  440. std::vector<std::string> CommandLine;
  441. CommandLine.push_back("one");
  442. CommandLine.push_back("two");
  443. FixedCompilationDatabase Database(".", CommandLine);
  444. EXPECT_EQ(0ul, Database.getAllFiles().size());
  445. }
  446. TEST(FixedCompilationDatabase, GetAllCompileCommands) {
  447. std::vector<std::string> CommandLine;
  448. CommandLine.push_back("one");
  449. CommandLine.push_back("two");
  450. FixedCompilationDatabase Database(".", CommandLine);
  451. EXPECT_EQ(0ul, Database.getAllCompileCommands().size());
  452. }
  453. TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
  454. int Argc = 0;
  455. std::string ErrorMsg;
  456. std::unique_ptr<FixedCompilationDatabase> Database =
  457. FixedCompilationDatabase::loadFromCommandLine(Argc, nullptr, ErrorMsg);
  458. EXPECT_FALSE(Database);
  459. EXPECT_TRUE(ErrorMsg.empty());
  460. EXPECT_EQ(0, Argc);
  461. }
  462. TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
  463. int Argc = 2;
  464. const char *Argv[] = { "1", "2" };
  465. std::string ErrorMsg;
  466. std::unique_ptr<FixedCompilationDatabase> Database(
  467. FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
  468. EXPECT_FALSE(Database);
  469. EXPECT_TRUE(ErrorMsg.empty());
  470. EXPECT_EQ(2, Argc);
  471. }
  472. TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
  473. int Argc = 5;
  474. const char *Argv[] = {
  475. "1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4"
  476. };
  477. std::string ErrorMsg;
  478. std::unique_ptr<FixedCompilationDatabase> Database(
  479. FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
  480. ASSERT_TRUE((bool)Database);
  481. ASSERT_TRUE(ErrorMsg.empty());
  482. std::vector<CompileCommand> Result =
  483. Database->getCompileCommands("source");
  484. ASSERT_EQ(1ul, Result.size());
  485. ASSERT_EQ(".", Result[0].Directory);
  486. ASSERT_THAT(Result[0].CommandLine, ElementsAre(EndsWith("clang-tool"),
  487. "-DDEF3", "-DDEF4", "source"));
  488. EXPECT_EQ(2, Argc);
  489. }
  490. TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
  491. int Argc = 3;
  492. const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
  493. std::string ErrorMsg;
  494. std::unique_ptr<FixedCompilationDatabase> Database =
  495. FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
  496. ASSERT_TRUE((bool)Database);
  497. ASSERT_TRUE(ErrorMsg.empty());
  498. std::vector<CompileCommand> Result =
  499. Database->getCompileCommands("source");
  500. ASSERT_EQ(1ul, Result.size());
  501. ASSERT_EQ(".", Result[0].Directory);
  502. ASSERT_THAT(Result[0].CommandLine,
  503. ElementsAre(EndsWith("clang-tool"), "source"));
  504. EXPECT_EQ(2, Argc);
  505. }
  506. TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
  507. const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"};
  508. int Argc = sizeof(Argv) / sizeof(char*);
  509. std::string ErrorMsg;
  510. std::unique_ptr<FixedCompilationDatabase> Database =
  511. FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
  512. ASSERT_TRUE((bool)Database);
  513. ASSERT_TRUE(ErrorMsg.empty());
  514. std::vector<CompileCommand> Result =
  515. Database->getCompileCommands("source");
  516. ASSERT_EQ(1ul, Result.size());
  517. ASSERT_EQ(".", Result[0].Directory);
  518. ASSERT_THAT(Result[0].CommandLine,
  519. ElementsAre(EndsWith("clang-tool"), "-c", "-DDEF3", "source"));
  520. EXPECT_EQ(2, Argc);
  521. }
  522. TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) {
  523. // Adjust the given command line arguments to ensure that any positional
  524. // arguments in them are stripped.
  525. const char *Argv[] = {"--", "somefile.cpp", "-fsyntax-only", "-DDEF3"};
  526. int Argc = llvm::array_lengthof(Argv);
  527. std::string ErrorMessage;
  528. std::unique_ptr<CompilationDatabase> Database =
  529. FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMessage);
  530. ASSERT_TRUE((bool)Database);
  531. ASSERT_TRUE(ErrorMessage.empty());
  532. std::vector<CompileCommand> Result = Database->getCompileCommands("source");
  533. ASSERT_EQ(1ul, Result.size());
  534. ASSERT_EQ(".", Result[0].Directory);
  535. ASSERT_THAT(
  536. Result[0].CommandLine,
  537. ElementsAre(EndsWith("clang-tool"), "-fsyntax-only", "-DDEF3", "source"));
  538. }
  539. TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
  540. const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
  541. int Argc = sizeof(Argv) / sizeof(char*);
  542. std::string ErrorMsg;
  543. std::unique_ptr<FixedCompilationDatabase> Database =
  544. FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
  545. ASSERT_TRUE((bool)Database);
  546. ASSERT_TRUE(ErrorMsg.empty());
  547. std::vector<CompileCommand> Result =
  548. Database->getCompileCommands("source");
  549. ASSERT_EQ(1ul, Result.size());
  550. ASSERT_EQ(".", Result[0].Directory);
  551. std::vector<std::string> Expected;
  552. ASSERT_THAT(Result[0].CommandLine,
  553. ElementsAre(EndsWith("clang-tool"), "source"));
  554. EXPECT_EQ(2, Argc);
  555. }
  556. struct MemCDB : public CompilationDatabase {
  557. using EntryMap = llvm::StringMap<SmallVector<CompileCommand, 1>>;
  558. EntryMap Entries;
  559. MemCDB(const EntryMap &E) : Entries(E) {}
  560. std::vector<CompileCommand> getCompileCommands(StringRef F) const override {
  561. auto Ret = Entries.lookup(F);
  562. return {Ret.begin(), Ret.end()};
  563. }
  564. std::vector<std::string> getAllFiles() const override {
  565. std::vector<std::string> Result;
  566. for (const auto &Entry : Entries)
  567. Result.push_back(Entry.first());
  568. return Result;
  569. }
  570. };
  571. class MemDBTest : public ::testing::Test {
  572. protected:
  573. // Adds an entry to the underlying compilation database.
  574. // A flag is injected: -D <File>, so the command used can be identified.
  575. void add(StringRef File, StringRef Clang, StringRef Flags) {
  576. SmallVector<StringRef, 8> Argv = {Clang, File, "-D", File};
  577. llvm::SplitString(Flags, Argv);
  578. SmallString<32> Dir;
  579. llvm::sys::path::system_temp_directory(false, Dir);
  580. Entries[path(File)].push_back(
  581. {Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"});
  582. }
  583. void add(StringRef File, StringRef Flags = "") { add(File, "clang", Flags); }
  584. // Turn a unix path fragment (foo/bar.h) into a native path (C:\tmp\foo\bar.h)
  585. std::string path(llvm::SmallString<32> File) {
  586. llvm::SmallString<32> Dir;
  587. llvm::sys::path::system_temp_directory(false, Dir);
  588. llvm::sys::path::native(File);
  589. llvm::SmallString<64> Result;
  590. llvm::sys::path::append(Result, Dir, File);
  591. return Result.str();
  592. }
  593. MemCDB::EntryMap Entries;
  594. };
  595. class InterpolateTest : public MemDBTest {
  596. protected:
  597. // Look up the command from a relative path, and return it in string form.
  598. // The input file is not included in the returned command.
  599. std::string getCommand(llvm::StringRef F) {
  600. auto Results =
  601. inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries))
  602. ->getCompileCommands(path(F));
  603. if (Results.empty())
  604. return "none";
  605. // drop the input file argument, so tests don't have to deal with path().
  606. EXPECT_EQ(Results[0].CommandLine.back(), path(F))
  607. << "Last arg should be the file";
  608. Results[0].CommandLine.pop_back();
  609. return llvm::join(Results[0].CommandLine, " ");
  610. }
  611. // Parse the file whose command was used out of the Heuristic string.
  612. std::string getProxy(llvm::StringRef F) {
  613. auto Results =
  614. inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries))
  615. ->getCompileCommands(path(F));
  616. if (Results.empty())
  617. return "none";
  618. StringRef Proxy = Results.front().Heuristic;
  619. if (!Proxy.consume_front("inferred from "))
  620. return "";
  621. // We have a proxy file, convert back to a unix relative path.
  622. // This is a bit messy, but we do need to test these strings somehow...
  623. llvm::SmallString<32> TempDir;
  624. llvm::sys::path::system_temp_directory(false, TempDir);
  625. Proxy.consume_front(TempDir);
  626. Proxy.consume_front(llvm::sys::path::get_separator());
  627. llvm::SmallString<32> Result = Proxy;
  628. llvm::sys::path::native(Result, llvm::sys::path::Style::posix);
  629. return Result.str();
  630. }
  631. };
  632. TEST_F(InterpolateTest, Nearby) {
  633. add("dir/foo.cpp");
  634. add("dir/bar.cpp");
  635. add("an/other/foo.cpp");
  636. // great: dir and name both match (prefix or full, case insensitive)
  637. EXPECT_EQ(getProxy("dir/f.cpp"), "dir/foo.cpp");
  638. EXPECT_EQ(getProxy("dir/FOO.cpp"), "dir/foo.cpp");
  639. // no name match. prefer matching dir, break ties by alpha
  640. EXPECT_EQ(getProxy("dir/a.cpp"), "dir/bar.cpp");
  641. // an exact name match beats one segment of directory match
  642. EXPECT_EQ(getProxy("some/other/bar.h"), "dir/bar.cpp");
  643. // two segments of directory match beat a prefix name match
  644. EXPECT_EQ(getProxy("an/other/b.cpp"), "an/other/foo.cpp");
  645. // if nothing matches at all, we still get the closest alpha match
  646. EXPECT_EQ(getProxy("below/some/obscure/path.cpp"), "an/other/foo.cpp");
  647. }
  648. TEST_F(InterpolateTest, Language) {
  649. add("dir/foo.cpp", "-std=c++17");
  650. add("dir/bar.c", "");
  651. add("dir/baz.cee", "-x c");
  652. // .h is ambiguous, so we add explicit language flags
  653. EXPECT_EQ(getCommand("foo.h"),
  654. "clang -D dir/foo.cpp -x c++-header -std=c++17");
  655. // Same thing if we have no extension. (again, we treat as header).
  656. EXPECT_EQ(getCommand("foo"), "clang -D dir/foo.cpp -x c++-header -std=c++17");
  657. // and invalid extensions.
  658. EXPECT_EQ(getCommand("foo.cce"),
  659. "clang -D dir/foo.cpp -x c++-header -std=c++17");
  660. // and don't add -x if the inferred language is correct.
  661. EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17");
  662. // respect -x if it's already there.
  663. EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header");
  664. // prefer a worse match with the right extension.
  665. EXPECT_EQ(getCommand("foo.c"), "clang -D dir/bar.c");
  666. Entries.erase(path(StringRef("dir/bar.c")));
  667. // Now we transfer across languages, so drop -std too.
  668. EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp");
  669. }
  670. TEST_F(InterpolateTest, Strip) {
  671. add("dir/foo.cpp", "-o foo.o -Wall");
  672. // the -o option and the input file are removed, but -Wall is preserved.
  673. EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall");
  674. }
  675. TEST_F(InterpolateTest, Case) {
  676. add("FOO/BAR/BAZ/SHOUT.cc");
  677. add("foo/bar/baz/quiet.cc");
  678. // Case mismatches are completely ignored, so we choose the name match.
  679. EXPECT_EQ(getProxy("foo/bar/baz/shout.C"), "FOO/BAR/BAZ/SHOUT.cc");
  680. }
  681. TEST_F(InterpolateTest, Aliasing) {
  682. add("foo.cpp", "-faligned-new");
  683. // The interpolated command should keep the given flag as written, even though
  684. // the flag is internally represented as an alias.
  685. EXPECT_EQ(getCommand("foo.hpp"), "clang -D foo.cpp -faligned-new");
  686. }
  687. TEST_F(InterpolateTest, ClangCL) {
  688. add("foo.cpp", "clang-cl", "/W4");
  689. // Language flags should be added with CL syntax.
  690. EXPECT_EQ(getCommand("foo.h"), "clang-cl -D foo.cpp /W4 /TP");
  691. }
  692. TEST_F(InterpolateTest, DriverModes) {
  693. add("foo.cpp", "clang-cl", "--driver-mode=gcc");
  694. add("bar.cpp", "clang", "--driver-mode=cl");
  695. // --driver-mode overrides should be respected.
  696. EXPECT_EQ(getCommand("foo.h"), "clang-cl -D foo.cpp --driver-mode=gcc -x c++-header");
  697. EXPECT_EQ(getCommand("bar.h"), "clang -D bar.cpp --driver-mode=cl /TP");
  698. }
  699. TEST(CompileCommandTest, EqualityOperator) {
  700. CompileCommand CCRef("/foo/bar", "hello.c", {"a", "b"}, "hello.o");
  701. CompileCommand CCTest = CCRef;
  702. EXPECT_TRUE(CCRef == CCTest);
  703. EXPECT_FALSE(CCRef != CCTest);
  704. CCTest = CCRef;
  705. CCTest.Directory = "/foo/baz";
  706. EXPECT_FALSE(CCRef == CCTest);
  707. EXPECT_TRUE(CCRef != CCTest);
  708. CCTest = CCRef;
  709. CCTest.Filename = "bonjour.c";
  710. EXPECT_FALSE(CCRef == CCTest);
  711. EXPECT_TRUE(CCRef != CCTest);
  712. CCTest = CCRef;
  713. CCTest.CommandLine.push_back("c");
  714. EXPECT_FALSE(CCRef == CCTest);
  715. EXPECT_TRUE(CCRef != CCTest);
  716. CCTest = CCRef;
  717. CCTest.Output = "bonjour.o";
  718. EXPECT_FALSE(CCRef == CCTest);
  719. EXPECT_TRUE(CCRef != CCTest);
  720. }
  721. class TargetAndModeTest : public MemDBTest {
  722. public:
  723. TargetAndModeTest() { llvm::InitializeAllTargetInfos(); }
  724. protected:
  725. // Look up the command from a relative path, and return it in string form.
  726. std::string getCommand(llvm::StringRef F) {
  727. auto Results = inferTargetAndDriverMode(llvm::make_unique<MemCDB>(Entries))
  728. ->getCompileCommands(path(F));
  729. if (Results.empty())
  730. return "none";
  731. return llvm::join(Results[0].CommandLine, " ");
  732. }
  733. };
  734. TEST_F(TargetAndModeTest, TargetAndMode) {
  735. add("foo.cpp", "clang-cl", "");
  736. add("bar.cpp", "x86_64-linux-clang", "");
  737. EXPECT_EQ(getCommand("foo.cpp"),
  738. "clang-cl --driver-mode=cl foo.cpp -D foo.cpp");
  739. EXPECT_EQ(getCommand("bar.cpp"),
  740. "x86_64-linux-clang -target x86_64-linux bar.cpp -D bar.cpp");
  741. }
  742. } // end namespace tooling
  743. } // end namespace clang