CompilationDatabaseTest.cpp 29 KB

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