Clang Project

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