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/Tooling/CompilationDatabase.h"
10#include "clang/Tooling/FileMatchTrie.h"
11#include "clang/Tooling/JSONCompilationDatabase.h"
12#include "clang/Tooling/Tooling.h"
13#include "llvm/Support/Path.h"
14#include "llvm/Support/TargetSelect.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17#include <algorithm>
18
19namespace clang {
20namespace tooling {
21
22using testing::ElementsAre;
23using testing::EndsWith;
24using testing::IsEmpty;
25using testing::UnorderedElementsAreArray;
26
27static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
28 std::string ErrorMessage;
29 EXPECT_EQ(nullptr,
30 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
31 JSONCommandLineSyntax::Gnu))
32 << "Expected an error because of: " << Explanation.str();
33}
34
35TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
36 expectFailure(JSONDatabase: "", Explanation: "Empty database");
37 expectFailure(JSONDatabase: "{", Explanation: "Invalid JSON");
38 expectFailure(JSONDatabase: "[[]]", Explanation: "Array instead of object");
39 expectFailure(JSONDatabase: "[{\"a\":[]}]", Explanation: "Array instead of value");
40 expectFailure(JSONDatabase: "[{\"a\":\"b\"}]", Explanation: "Unknown key");
41 expectFailure(JSONDatabase: "[{[]:\"\"}]", Explanation: "Incorrectly typed entry");
42 expectFailure(JSONDatabase: "[{}]", Explanation: "Empty entry");
43 expectFailure(JSONDatabase: "[{\"directory\":\"\",\"command\":\"\"}]", Explanation: "Missing file");
44 expectFailure(JSONDatabase: "[{\"directory\":\"\",\"file\":\"\"}]", Explanation: "Missing command or arguments");
45 expectFailure(JSONDatabase: "[{\"command\":\"\",\"file\":\"\"}]", Explanation: "Missing directory");
46 expectFailure(JSONDatabase: "[{\"directory\":\"\",\"arguments\":[]}]", Explanation: "Missing file");
47 expectFailure(JSONDatabase: "[{\"arguments\":\"\",\"file\":\"\"}]", Explanation: "Missing directory");
48 expectFailure(JSONDatabase: "[{\"directory\":\"\",\"arguments\":\"\",\"file\":\"\"}]", Explanation: "Arguments not array");
49 expectFailure(JSONDatabase: "[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", Explanation: "Command not string");
50 expectFailure(JSONDatabase: "[{\"directory\":\"\",\"arguments\":[[]],\"file\":\"\"}]",
51 Explanation: "Arguments contain non-string");
52 expectFailure(JSONDatabase: "[{\"output\":[]}]", Explanation: "Expected strings as value.");
53}
54
55static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
56 std::string &ErrorMessage,
57 JSONCommandLineSyntax Syntax) {
58 std::unique_ptr<CompilationDatabase> Database(
59 JSONCompilationDatabase::loadFromBuffer(DatabaseString: JSONDatabase, ErrorMessage,
60 Syntax));
61 if (!Database) {
62 ADD_FAILURE() << ErrorMessage;
63 return std::vector<std::string>();
64 }
65 auto Result = Database->getAllFiles();
66 std::sort(first: Result.begin(), last: Result.end());
67 return Result;
68}
69
70static std::vector<CompileCommand>
71getAllCompileCommands(JSONCommandLineSyntax Syntax, StringRef JSONDatabase,
72 std::string &ErrorMessage) {
73 std::unique_ptr<CompilationDatabase> Database(
74 JSONCompilationDatabase::loadFromBuffer(DatabaseString: JSONDatabase, ErrorMessage,
75 Syntax));
76 if (!Database) {
77 ADD_FAILURE() << ErrorMessage;
78 return std::vector<CompileCommand>();
79 }
80 return Database->getAllCompileCommands();
81}
82
83TEST(JSONCompilationDatabase, GetAllFiles) {
84 std::string ErrorMessage;
85 EXPECT_THAT(getAllFiles("[]", ErrorMessage, JSONCommandLineSyntax::Gnu),
86 IsEmpty())
87 << ErrorMessage;
88
89 std::vector<std::string> expected_files;
90 SmallString<16> PathStorage;
91 llvm::sys::path::native(path: "//net/dir/file1", result&: PathStorage);
92 expected_files.push_back(x: std::string(PathStorage.str()));
93 llvm::sys::path::native(path: "//net/dir/file2", result&: PathStorage);
94 expected_files.push_back(x: std::string(PathStorage.str()));
95 llvm::sys::path::native(path: "//net/dir/file3", result&: PathStorage);
96 expected_files.push_back(x: std::string(PathStorage.str()));
97 llvm::sys::path::native(path: "//net/file1", result&: PathStorage);
98 expected_files.push_back(x: std::string(PathStorage.str()));
99 EXPECT_THAT(getAllFiles(R"json(
100 [
101 {
102 "directory": "//net/dir",
103 "command": "command",
104 "file": "file1"
105 },
106 {
107 "directory": "//net/dir",
108 "command": "command",
109 "file": "../file1"
110 },
111 {
112 "directory": "//net/dir",
113 "command": "command",
114 "file": "file2"
115 },
116 {
117 "directory": "//net/dir",
118 "command": "command",
119 "file": "//net/dir/foo/../file3"
120 }
121 ])json",
122 ErrorMessage, JSONCommandLineSyntax::Gnu),
123 UnorderedElementsAreArray(expected_files))
124 << ErrorMessage;
125}
126
127TEST(JSONCompilationDatabase, GetAllCompileCommands) {
128 std::string ErrorMessage;
129 EXPECT_EQ(
130 0u, getAllCompileCommands(JSONCommandLineSyntax::Gnu, "[]", ErrorMessage)
131 .size())
132 << ErrorMessage;
133
134 StringRef Directory1("//net/dir1");
135 StringRef FileName1("file1");
136 StringRef Command1("command1");
137 StringRef Output1("file1.o");
138 StringRef Directory2("//net/dir2");
139 StringRef FileName2("file2");
140 StringRef Command2("command2");
141 StringRef Output2("");
142
143 std::vector<CompileCommand> Commands = getAllCompileCommands(
144 Syntax: JSONCommandLineSyntax::Gnu,
145 JSONDatabase: ("[{\"directory\":\"" + Directory1 + "\"," + "\"command\":\"" + Command1 +
146 "\","
147 "\"file\":\"" +
148 FileName1 + "\", \"output\":\"" +
149 Output1 + "\"},"
150 " {\"directory\":\"" +
151 Directory2 + "\"," + "\"command\":\"" + Command2 + "\","
152 "\"file\":\"" +
153 FileName2 + "\"}]")
154 .str(),
155 ErrorMessage);
156 EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
157 EXPECT_EQ(Directory1, Commands[0].Directory) << ErrorMessage;
158 EXPECT_EQ(FileName1, Commands[0].Filename) << ErrorMessage;
159 EXPECT_EQ(Output1, Commands[0].Output) << ErrorMessage;
160 ASSERT_EQ(1u, Commands[0].CommandLine.size());
161 EXPECT_EQ(Command1, Commands[0].CommandLine[0]) << ErrorMessage;
162 EXPECT_EQ(Directory2, Commands[1].Directory) << ErrorMessage;
163 EXPECT_EQ(FileName2, Commands[1].Filename) << ErrorMessage;
164 EXPECT_EQ(Output2, Commands[1].Output) << ErrorMessage;
165 ASSERT_EQ(1u, Commands[1].CommandLine.size());
166 EXPECT_EQ(Command2, Commands[1].CommandLine[0]) << ErrorMessage;
167
168 // Check that order is preserved.
169 Commands = getAllCompileCommands(
170 Syntax: JSONCommandLineSyntax::Gnu,
171 JSONDatabase: ("[{\"directory\":\"" + Directory2 + "\"," + "\"command\":\"" + Command2 +
172 "\","
173 "\"file\":\"" +
174 FileName2 + "\"},"
175 " {\"directory\":\"" +
176 Directory1 + "\"," + "\"command\":\"" + Command1 + "\","
177 "\"file\":\"" +
178 FileName1 + "\"}]")
179 .str(),
180 ErrorMessage);
181 EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
182 EXPECT_EQ(Directory2, Commands[0].Directory) << ErrorMessage;
183 EXPECT_EQ(FileName2, Commands[0].Filename) << ErrorMessage;
184 ASSERT_EQ(1u, Commands[0].CommandLine.size());
185 EXPECT_EQ(Command2, Commands[0].CommandLine[0]) << ErrorMessage;
186 EXPECT_EQ(Directory1, Commands[1].Directory) << ErrorMessage;
187 EXPECT_EQ(FileName1, Commands[1].Filename) << ErrorMessage;
188 ASSERT_EQ(1u, Commands[1].CommandLine.size());
189 EXPECT_EQ(Command1, Commands[1].CommandLine[0]) << ErrorMessage;
190}
191
192static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
193 std::string JSONDatabase,
194 std::string &ErrorMessage) {
195 std::unique_ptr<CompilationDatabase> Database(
196 JSONCompilationDatabase::loadFromBuffer(DatabaseString: JSONDatabase, ErrorMessage,
197 Syntax: JSONCommandLineSyntax::Gnu));
198 if (!Database)
199 return CompileCommand();
200 // Overwrite the string to verify we're not reading from it later.
201 JSONDatabase.assign(n: JSONDatabase.size(), c: '*');
202 std::vector<CompileCommand> Commands = Database->getCompileCommands(FilePath: FileName);
203 EXPECT_LE(Commands.size(), 1u);
204 if (Commands.empty())
205 return CompileCommand();
206 return Commands[0];
207}
208
209TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) {
210 StringRef Directory("//net/dir");
211 StringRef FileName("//net/dir/filename");
212 StringRef Command("command");
213 StringRef Arguments = "arguments";
214 Twine ArgumentsAccumulate;
215 std::string ErrorMessage;
216 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
217 FileName,
218 JSONDatabase: ("[{\"directory\":\"" + Directory + "\","
219 "\"arguments\":[\"" + Arguments + "\"],"
220 "\"command\":\"" + Command + "\","
221 "\"file\":\"" + FileName + "\"}]").str(),
222 ErrorMessage);
223 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
224 EXPECT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
225 EXPECT_EQ(Arguments, FoundCommand.CommandLine[0]) << ErrorMessage;
226}
227
228struct FakeComparator : public PathComparator {
229 ~FakeComparator() override {}
230 bool equivalent(StringRef FileA, StringRef FileB) const override {
231 return FileA.equals_insensitive(RHS: FileB);
232 }
233};
234
235class FileMatchTrieTest : public ::testing::Test {
236protected:
237 FileMatchTrieTest() : Trie(new FakeComparator()) {}
238
239 StringRef find(StringRef Path) {
240 llvm::raw_string_ostream ES(Error);
241 return Trie.findEquivalent(FileName: Path, Error&: ES);
242 }
243
244 FileMatchTrie Trie;
245 std::string Error;
246};
247
248TEST_F(FileMatchTrieTest, InsertingRelativePath) {
249 Trie.insert(NewPath: "//net/path/file.cc");
250 Trie.insert(NewPath: "file.cc");
251 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
252}
253
254TEST_F(FileMatchTrieTest, MatchingRelativePath) {
255 EXPECT_EQ("", find("file.cc"));
256}
257
258TEST_F(FileMatchTrieTest, ReturnsBestResults) {
259 Trie.insert(NewPath: "//net/d/c/b.cc");
260 Trie.insert(NewPath: "//net/d/b/b.cc");
261 EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc"));
262}
263
264TEST_F(FileMatchTrieTest, HandlesSymlinks) {
265 Trie.insert(NewPath: "//net/AA/file.cc");
266 EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc"));
267}
268
269TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) {
270 Trie.insert(NewPath: "//net/Aa/file.cc");
271 Trie.insert(NewPath: "//net/aA/file.cc");
272 EXPECT_TRUE(find("//net/aa/file.cc").empty());
273 EXPECT_EQ("Path is ambiguous", Error);
274}
275
276TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) {
277 Trie.insert(NewPath: "//net/src/Aa/file.cc");
278 Trie.insert(NewPath: "//net/src/aA/file.cc");
279 Trie.insert(NewPath: "//net/SRC/aa/file.cc");
280 EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc"));
281}
282
283TEST_F(FileMatchTrieTest, EmptyTrie) {
284 EXPECT_TRUE(find("//net/some/path").empty());
285}
286
287TEST_F(FileMatchTrieTest, NoResult) {
288 Trie.insert(NewPath: "//net/somepath/otherfile.cc");
289 Trie.insert(NewPath: "//net/otherpath/somefile.cc");
290 EXPECT_EQ("", find("//net/somepath/somefile.cc"));
291}
292
293TEST_F(FileMatchTrieTest, RootElementDifferent) {
294 Trie.insert(NewPath: "//net/path/file.cc");
295 Trie.insert(NewPath: "//net/otherpath/file.cc");
296 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
297}
298
299TEST_F(FileMatchTrieTest, CannotResolveRelativePath) {
300 EXPECT_EQ("", find("relative-path.cc"));
301 EXPECT_EQ("Cannot resolve relative paths", Error);
302}
303
304TEST_F(FileMatchTrieTest, SingleFile) {
305 Trie.insert(NewPath: "/root/RootFile.cc");
306 EXPECT_EQ("", find("/root/rootfile.cc"));
307 // Add subpath to avoid `if (Children.empty())` special case
308 // which we hit at previous `find()`.
309 Trie.insert(NewPath: "/root/otherpath/OtherFile.cc");
310 EXPECT_EQ("", find("/root/rootfile.cc"));
311}
312
313TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
314 std::string ErrorMessage;
315 CompileCommand NotFound = findCompileArgsInJsonDatabase(
316 FileName: "a-file.cpp", JSONDatabase: "", ErrorMessage);
317 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
318 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
319}
320
321TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
322 StringRef Directory("//net/some/directory");
323 StringRef FileName("//net/path/to/a-file.cpp");
324 StringRef Command("//net/path/to/compiler and some arguments");
325 std::string ErrorMessage;
326 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
327 FileName,
328 JSONDatabase: ("[{\"directory\":\"" + Directory + "\"," +
329 "\"command\":\"" + Command + "\","
330 "\"file\":\"" + FileName + "\"}]").str(),
331 ErrorMessage);
332 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
333 ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
334 EXPECT_EQ("//net/path/to/compiler",
335 FoundCommand.CommandLine[0]) << ErrorMessage;
336 EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
337 EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
338 EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
339
340 CompileCommand NotFound = findCompileArgsInJsonDatabase(
341 FileName: "a-file.cpp",
342 JSONDatabase: ("[{\"directory\":\"" + Directory + "\"," +
343 "\"command\":\"" + Command + "\","
344 "\"file\":\"" + FileName + "\"}]").str(),
345 ErrorMessage);
346 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
347 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
348}
349
350TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
351 StringRef Directory("//net/some/directory");
352 StringRef FileName("//net/path/to/a-file.cpp");
353 StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\"");
354 std::string ErrorMessage;
355 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
356 FileName,
357 JSONDatabase: ("[{\"directory\":\"" + Directory + "\"," +
358 "\"command\":\"" + Command + "\","
359 "\"file\":\"" + FileName + "\"}]").str(),
360 ErrorMessage);
361 ASSERT_EQ(2u, FoundCommand.CommandLine.size());
362 EXPECT_EQ("//net/path to compiler",
363 FoundCommand.CommandLine[0]) << ErrorMessage;
364 EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
365}
366
367TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
368 StringRef Directory("//net/some directory / with spaces");
369 StringRef FileName("//net/path/to/a-file.cpp");
370 StringRef Command("a command");
371 std::string ErrorMessage;
372 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
373 FileName,
374 JSONDatabase: ("[{\"directory\":\"" + Directory + "\"," +
375 "\"command\":\"" + Command + "\","
376 "\"file\":\"" + FileName + "\"}]").str(),
377 ErrorMessage);
378 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
379}
380
381TEST(findCompileArgsInJsonDatabase, FindsEntry) {
382 StringRef Directory("//net/directory");
383 StringRef FileName("file");
384 StringRef Command("command");
385 std::string JsonDatabase = "[";
386 for (int I = 0; I < 10; ++I) {
387 if (I > 0) JsonDatabase += ",";
388 JsonDatabase +=
389 ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
390 "\"command\":\"" + Command + Twine(I) + "\","
391 "\"file\":\"" + FileName + Twine(I) + "\"}").str();
392 }
393 JsonDatabase += "]";
394 std::string ErrorMessage;
395 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
396 FileName: "//net/directory4/file4", JSONDatabase: JsonDatabase, ErrorMessage);
397 EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage;
398 ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
399 EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
400}
401
402TEST(findCompileArgsInJsonDatabase, ParsesCompilerWrappers) {
403 std::vector<std::pair<std::string, std::string>> Cases = {
404 {"distcc gcc foo.c", "gcc foo.c"},
405 {"gomacc clang++ foo.c", "clang++ foo.c"},
406 {"sccache clang++ foo.c", "clang++ foo.c"},
407 {"ccache gcc foo.c", "gcc foo.c"},
408 {"ccache.exe gcc foo.c", "gcc foo.c"},
409 {"ccache g++.exe foo.c", "g++.exe foo.c"},
410 {"ccache distcc gcc foo.c", "gcc foo.c"},
411
412 {"distcc foo.c", "distcc foo.c"},
413 {"distcc -I/foo/bar foo.c", "distcc -I/foo/bar foo.c"},
414 };
415 std::string ErrorMessage;
416
417 for (const auto &Case : Cases) {
418 std::string DB =
419 R"([{"directory":"//net/dir", "file":"//net/dir/foo.c", "command":")" +
420 Case.first + "\"}]";
421 CompileCommand FoundCommand =
422 findCompileArgsInJsonDatabase(FileName: "//net/dir/foo.c", JSONDatabase: DB, ErrorMessage);
423 EXPECT_EQ(Case.second, llvm::join(FoundCommand.CommandLine, " "))
424 << Case.first;
425 }
426}
427
428static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
429 std::string JsonDatabase =
430 ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
431 Command + "\"}]").str();
432 std::string ErrorMessage;
433 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
434 FileName: "//net/root/test", JSONDatabase: JsonDatabase, ErrorMessage);
435 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
436 return FoundCommand.CommandLine;
437}
438
439TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
440 std::vector<std::string> Result = unescapeJsonCommandLine(Command: "");
441 EXPECT_TRUE(Result.empty());
442}
443
444TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
445 std::vector<std::string> Result = unescapeJsonCommandLine(Command: "a b c");
446 ASSERT_EQ(3ul, Result.size());
447 EXPECT_EQ("a", Result[0]);
448 EXPECT_EQ("b", Result[1]);
449 EXPECT_EQ("c", Result[2]);
450}
451
452TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
453 std::vector<std::string> Result = unescapeJsonCommandLine(Command: " a b ");
454 ASSERT_EQ(2ul, Result.size());
455 EXPECT_EQ("a", Result[0]);
456 EXPECT_EQ("b", Result[1]);
457}
458
459TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
460 std::vector<std::string> Backslash = unescapeJsonCommandLine(Command: "a\\\\\\\\");
461 ASSERT_EQ(1ul, Backslash.size());
462 EXPECT_EQ("a\\", Backslash[0]);
463 std::vector<std::string> Quote = unescapeJsonCommandLine(Command: "a\\\\\\\"");
464 ASSERT_EQ(1ul, Quote.size());
465 EXPECT_EQ("a\"", Quote[0]);
466}
467
468TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
469 std::vector<std::string> Result = unescapeJsonCommandLine(Command: "\\\" a b \\\"");
470 ASSERT_EQ(1ul, Result.size());
471 EXPECT_EQ(" a b ", Result[0]);
472}
473
474TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
475 std::vector<std::string> Result = unescapeJsonCommandLine(
476 Command: " \\\" a \\\" \\\" b \\\" ");
477 ASSERT_EQ(2ul, Result.size());
478 EXPECT_EQ(" a ", Result[0]);
479 EXPECT_EQ(" b ", Result[1]);
480}
481
482TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
483 std::vector<std::string> Result = unescapeJsonCommandLine(
484 Command: "\\\"\\\"\\\"\\\"");
485 ASSERT_EQ(1ul, Result.size());
486 EXPECT_TRUE(Result[0].empty()) << Result[0];
487}
488
489TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
490 std::vector<std::string> Result = unescapeJsonCommandLine(
491 Command: "\\\"\\\\\\\"\\\"");
492 ASSERT_EQ(1ul, Result.size());
493 EXPECT_EQ("\"", Result[0]);
494}
495
496TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
497 std::vector<std::string> Result = unescapeJsonCommandLine(
498 Command: " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
499 ASSERT_EQ(4ul, Result.size());
500 EXPECT_EQ("\"", Result[0]);
501 EXPECT_EQ("a \" b ", Result[1]);
502 EXPECT_EQ("and\\c", Result[2]);
503 EXPECT_EQ("\"", Result[3]);
504}
505
506TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
507 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
508 Command: "\\\"a\\\"\\\"b\\\"");
509 ASSERT_EQ(1ul, QuotedNoSpaces.size());
510 EXPECT_EQ("ab", QuotedNoSpaces[0]);
511
512 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
513 Command: "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
514 ASSERT_EQ(1ul, MixedNoSpaces.size());
515 EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
516}
517
518TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
519 std::vector<std::string> Unclosed = unescapeJsonCommandLine(Command: "\\\"abc");
520 ASSERT_EQ(1ul, Unclosed.size());
521 EXPECT_EQ("abc", Unclosed[0]);
522
523 std::vector<std::string> Empty = unescapeJsonCommandLine(Command: "\\\"");
524 ASSERT_EQ(1ul, Empty.size());
525 EXPECT_EQ("", Empty[0]);
526}
527
528TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) {
529 std::vector<std::string> Args = unescapeJsonCommandLine(Command: "a'\\\\b \\\"c\\\"'");
530 ASSERT_EQ(1ul, Args.size());
531 EXPECT_EQ("a\\b \"c\"", Args[0]);
532}
533
534TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
535 FixedCompilationDatabase Database(".", /*CommandLine*/ {"one", "two"});
536 StringRef FileName("source");
537 std::vector<CompileCommand> Result =
538 Database.getCompileCommands(FilePath: FileName);
539 ASSERT_EQ(1ul, Result.size());
540 EXPECT_EQ(".", Result[0].Directory);
541 EXPECT_EQ(FileName, Result[0].Filename);
542 EXPECT_THAT(Result[0].CommandLine,
543 ElementsAre(EndsWith("clang-tool"), "one", "two", "source"));
544}
545
546TEST(FixedCompilationDatabase, GetAllFiles) {
547 std::vector<std::string> CommandLine;
548 CommandLine.push_back(x: "one");
549 CommandLine.push_back(x: "two");
550 FixedCompilationDatabase Database(".", CommandLine);
551
552 EXPECT_THAT(Database.getAllFiles(), IsEmpty());
553}
554
555TEST(FixedCompilationDatabase, GetAllCompileCommands) {
556 std::vector<std::string> CommandLine;
557 CommandLine.push_back(x: "one");
558 CommandLine.push_back(x: "two");
559 FixedCompilationDatabase Database(".", CommandLine);
560
561 EXPECT_EQ(0ul, Database.getAllCompileCommands().size());
562}
563
564TEST(FixedCompilationDatabase, FromBuffer) {
565 const char *Data = R"(
566
567 -DFOO=BAR
568
569--baz
570
571 )";
572 std::string ErrorMsg;
573 auto CDB =
574 FixedCompilationDatabase::loadFromBuffer(Directory: "/cdb/dir", Data, ErrorMsg);
575
576 std::vector<CompileCommand> Result = CDB->getCompileCommands(FilePath: "/foo/bar.cc");
577 ASSERT_EQ(1ul, Result.size());
578 EXPECT_EQ("/cdb/dir", Result.front().Directory);
579 EXPECT_EQ("/foo/bar.cc", Result.front().Filename);
580 EXPECT_THAT(
581 Result.front().CommandLine,
582 ElementsAre(EndsWith("clang-tool"), "-DFOO=BAR", "--baz", "/foo/bar.cc"));
583}
584
585TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
586 int Argc = 0;
587 std::string ErrorMsg;
588 std::unique_ptr<FixedCompilationDatabase> Database =
589 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv: nullptr, ErrorMsg);
590 EXPECT_FALSE(Database);
591 EXPECT_TRUE(ErrorMsg.empty());
592 EXPECT_EQ(0, Argc);
593}
594
595TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
596 int Argc = 2;
597 const char *Argv[] = { "1", "2" };
598 std::string ErrorMsg;
599 std::unique_ptr<FixedCompilationDatabase> Database(
600 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
601 EXPECT_FALSE(Database);
602 EXPECT_TRUE(ErrorMsg.empty());
603 EXPECT_EQ(2, Argc);
604}
605
606TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
607 int Argc = 5;
608 const char *Argv[] = {
609 "1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4"
610 };
611 std::string ErrorMsg;
612 std::unique_ptr<FixedCompilationDatabase> Database(
613 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
614 ASSERT_TRUE((bool)Database);
615 ASSERT_TRUE(ErrorMsg.empty());
616 std::vector<CompileCommand> Result =
617 Database->getCompileCommands(FilePath: "source");
618 ASSERT_EQ(1ul, Result.size());
619 ASSERT_EQ(".", Result[0].Directory);
620 ASSERT_THAT(Result[0].CommandLine, ElementsAre(EndsWith("clang-tool"),
621 "-DDEF3", "-DDEF4", "source"));
622 EXPECT_EQ(2, Argc);
623}
624
625TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
626 int Argc = 3;
627 const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
628 std::string ErrorMsg;
629 std::unique_ptr<FixedCompilationDatabase> Database =
630 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
631 ASSERT_TRUE((bool)Database);
632 ASSERT_TRUE(ErrorMsg.empty());
633 std::vector<CompileCommand> Result =
634 Database->getCompileCommands(FilePath: "source");
635 ASSERT_EQ(1ul, Result.size());
636 ASSERT_EQ(".", Result[0].Directory);
637 ASSERT_THAT(Result[0].CommandLine,
638 ElementsAre(EndsWith("clang-tool"), "source"));
639 EXPECT_EQ(2, Argc);
640}
641
642TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
643 const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"};
644 int Argc = std::size(Argv);
645 std::string ErrorMsg;
646 std::unique_ptr<FixedCompilationDatabase> Database =
647 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
648 ASSERT_TRUE((bool)Database);
649 ASSERT_TRUE(ErrorMsg.empty());
650 std::vector<CompileCommand> Result = Database->getCompileCommands(FilePath: "source");
651 ASSERT_EQ(1ul, Result.size());
652 ASSERT_EQ(".", Result[0].Directory);
653 ASSERT_THAT(Result[0].CommandLine,
654 ElementsAre(EndsWith("clang-tool"), "-c", "-DDEF3", "source"));
655 EXPECT_EQ(2, Argc);
656}
657
658TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsHeader) {
659 const char *Argv[] = {"1", "2", "--", "-xc++-header",
660 "-c", "somefile.h", "-DDEF3"};
661 int Argc = std::size(Argv);
662 std::string ErrorMsg;
663 std::unique_ptr<FixedCompilationDatabase> Database =
664 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
665 ASSERT_TRUE((bool)Database);
666 ASSERT_TRUE(ErrorMsg.empty());
667 std::vector<CompileCommand> Result =
668 Database->getCompileCommands(FilePath: "source");
669 ASSERT_EQ(1ul, Result.size());
670 ASSERT_EQ(".", Result[0].Directory);
671 ASSERT_THAT(Result[0].CommandLine,
672 ElementsAre(EndsWith("clang-tool"), "-xc++-header", "-c",
673 "-DDEF3", "source"));
674 EXPECT_EQ(2, Argc);
675}
676
677TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) {
678 // Adjust the given command line arguments to ensure that any positional
679 // arguments in them are stripped.
680 const char *Argv[] = {"--", "somefile.cpp", "-fsyntax-only", "-DDEF3"};
681 int Argc = std::size(Argv);
682 std::string ErrorMessage;
683 std::unique_ptr<CompilationDatabase> Database =
684 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg&: ErrorMessage);
685 ASSERT_TRUE((bool)Database);
686 ASSERT_TRUE(ErrorMessage.empty());
687 std::vector<CompileCommand> Result = Database->getCompileCommands(FilePath: "source");
688 ASSERT_EQ(1ul, Result.size());
689 ASSERT_EQ(".", Result[0].Directory);
690 ASSERT_THAT(
691 Result[0].CommandLine,
692 ElementsAre(EndsWith("clang-tool"), "-fsyntax-only", "-DDEF3", "source"));
693}
694
695TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
696 const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
697 int Argc = std::size(Argv);
698 std::string ErrorMsg;
699 std::unique_ptr<FixedCompilationDatabase> Database =
700 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
701 ASSERT_TRUE((bool)Database);
702 ASSERT_TRUE(ErrorMsg.empty());
703 std::vector<CompileCommand> Result =
704 Database->getCompileCommands(FilePath: "source");
705 ASSERT_EQ(1ul, Result.size());
706 ASSERT_EQ(".", Result[0].Directory);
707 std::vector<std::string> Expected;
708 ASSERT_THAT(Result[0].CommandLine,
709 ElementsAre(EndsWith("clang-tool"), "source"));
710 EXPECT_EQ(2, Argc);
711}
712
713struct MemCDB : public CompilationDatabase {
714 using EntryMap = llvm::StringMap<SmallVector<CompileCommand, 1>>;
715 EntryMap Entries;
716 MemCDB(const EntryMap &E) : Entries(E) {}
717
718 std::vector<CompileCommand> getCompileCommands(StringRef F) const override {
719 auto Ret = Entries.lookup(Key: F);
720 return {Ret.begin(), Ret.end()};
721 }
722
723 std::vector<std::string> getAllFiles() const override {
724 std::vector<std::string> Result;
725 for (const auto &Entry : Entries)
726 Result.push_back(x: std::string(Entry.first()));
727 return Result;
728 }
729};
730
731class MemDBTest : public ::testing::Test {
732protected:
733 // Adds an entry to the underlying compilation database.
734 // A flag is injected: -D <File>, so the command used can be identified.
735 void add(StringRef File, StringRef Clang, StringRef Flags) {
736 SmallVector<StringRef, 8> Argv = {Clang, File, "-D", File};
737 llvm::SplitString(Source: Flags, OutFragments&: Argv);
738
739 // Trim double quotation from the argumnets if any.
740 for (auto *It = Argv.begin(); It != Argv.end(); ++It)
741 *It = It->trim(Chars: "\"");
742
743 SmallString<32> Dir;
744 llvm::sys::path::system_temp_directory(erasedOnReboot: false, result&: Dir);
745
746 Entries[path(File)].push_back(
747 Elt: {Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"});
748 }
749 void add(StringRef File, StringRef Flags = "") { add(File, Clang: "clang", Flags); }
750
751 // Turn a unix path fragment (foo/bar.h) into a native path (C:\tmp\foo\bar.h)
752 std::string path(llvm::SmallString<32> File) {
753 llvm::SmallString<32> Dir;
754 llvm::sys::path::system_temp_directory(erasedOnReboot: false, result&: Dir);
755 llvm::sys::path::native(path&: File);
756 llvm::SmallString<64> Result;
757 llvm::sys::path::append(path&: Result, a: Dir, b: File);
758 return std::string(Result.str());
759 }
760
761 MemCDB::EntryMap Entries;
762};
763
764class InterpolateTest : public MemDBTest {
765protected:
766 // Look up the command from a relative path, and return it in string form.
767 // The input file is not included in the returned command.
768 std::string getCommand(llvm::StringRef F, bool MakeNative = true) {
769 auto Results =
770 inferMissingCompileCommands(std::make_unique<MemCDB>(args&: Entries))
771 ->getCompileCommands(FilePath: MakeNative ? path(File: F) : F);
772 if (Results.empty())
773 return "none";
774 // drop the input file argument, so tests don't have to deal with path().
775 EXPECT_EQ(Results[0].CommandLine.back(), MakeNative ? path(F) : F)
776 << "Last arg should be the file";
777 Results[0].CommandLine.pop_back();
778 EXPECT_EQ(Results[0].CommandLine.back(), "--")
779 << "Second-last arg should be --";
780 Results[0].CommandLine.pop_back();
781 return llvm::join(R&: Results[0].CommandLine, Separator: " ");
782 }
783
784 // Parse the file whose command was used out of the Heuristic string.
785 std::string getProxy(llvm::StringRef F) {
786 auto Results =
787 inferMissingCompileCommands(std::make_unique<MemCDB>(args&: Entries))
788 ->getCompileCommands(FilePath: path(File: F));
789 if (Results.empty())
790 return "none";
791 StringRef Proxy = Results.front().Heuristic;
792 if (!Proxy.consume_front(Prefix: "inferred from "))
793 return "";
794 // We have a proxy file, convert back to a unix relative path.
795 // This is a bit messy, but we do need to test these strings somehow...
796 llvm::SmallString<32> TempDir;
797 llvm::sys::path::system_temp_directory(erasedOnReboot: false, result&: TempDir);
798 Proxy.consume_front(Prefix: TempDir);
799 Proxy.consume_front(Prefix: llvm::sys::path::get_separator());
800 llvm::SmallString<32> Result = Proxy;
801 llvm::sys::path::native(path&: Result, style: llvm::sys::path::Style::posix);
802 return std::string(Result.str());
803 }
804};
805
806TEST_F(InterpolateTest, Nearby) {
807 add(File: "dir/foo.cpp");
808 add(File: "dir/bar.cpp");
809 add(File: "an/other/foo.cpp");
810
811 // great: dir and name both match (prefix or full, case insensitive)
812 EXPECT_EQ(getProxy("dir/f.cpp"), "dir/foo.cpp");
813 EXPECT_EQ(getProxy("dir/FOO.cpp"), "dir/foo.cpp");
814 // no name match. prefer matching dir, break ties by alpha
815 EXPECT_EQ(getProxy("dir/a.cpp"), "dir/bar.cpp");
816 // an exact name match beats one segment of directory match
817 EXPECT_EQ(getProxy("some/other/bar.h"), "dir/bar.cpp");
818 // two segments of directory match beat a prefix name match
819 EXPECT_EQ(getProxy("an/other/b.cpp"), "an/other/foo.cpp");
820 // if nothing matches at all, we still get the closest alpha match
821 EXPECT_EQ(getProxy("below/some/obscure/path.cpp"), "an/other/foo.cpp");
822}
823
824TEST_F(InterpolateTest, Language) {
825 add(File: "dir/foo.cpp", Flags: "-std=c++17");
826 add(File: "dir/bar.c", Flags: "");
827 add(File: "dir/baz.cee", Flags: "-x c");
828 add(File: "dir/aux.cpp", Flags: "-std=c++17 -x objective-c++");
829
830 // .h is ambiguous, so we add explicit language flags
831 EXPECT_EQ(getCommand("foo.h"),
832 "clang -D dir/foo.cpp -x c++-header -std=c++17");
833 // Same thing if we have no extension. (again, we treat as header).
834 EXPECT_EQ(getCommand("foo"), "clang -D dir/foo.cpp -x c++-header -std=c++17");
835 // and invalid extensions.
836 EXPECT_EQ(getCommand("foo.cce"),
837 "clang -D dir/foo.cpp -x c++-header -std=c++17");
838 // and don't add -x if the inferred language is correct.
839 EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17");
840 // respect -x if it's already there.
841 EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header");
842 // prefer a worse match with the right extension.
843 EXPECT_EQ(getCommand("foo.c"), "clang -D dir/bar.c");
844 Entries.erase(Key: path(File: StringRef("dir/bar.c")));
845 // Now we transfer across languages, so drop -std too.
846 EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp");
847 // Prefer -x over -std when overriding language.
848 EXPECT_EQ(getCommand("aux.h"),
849 "clang -D dir/aux.cpp -x objective-c++-header -std=c++17");
850}
851
852TEST_F(InterpolateTest, Strip) {
853 add(File: "dir/foo.cpp", Flags: "-o foo.o -Wall");
854 // the -o option and the input file are removed, but -Wall is preserved.
855 EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall");
856}
857
858TEST_F(InterpolateTest, StripDoubleDash) {
859 add(File: "dir/foo.cpp", Flags: "-o foo.o -std=c++14 -Wall -- dir/foo.cpp");
860 // input file and output option are removed
861 // -Wall flag isn't
862 // -std option gets re-added as the last argument before the input file
863 // -- is removed as it's not necessary - the new input file doesn't start with
864 // a dash
865 EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall -std=c++14");
866}
867
868TEST_F(InterpolateTest, Case) {
869 add(File: "FOO/BAR/BAZ/SHOUT.cc");
870 add(File: "foo/bar/baz/quiet.cc");
871 // Case mismatches are completely ignored, so we choose the name match.
872 EXPECT_EQ(getProxy("foo/bar/baz/shout.C"), "FOO/BAR/BAZ/SHOUT.cc");
873}
874
875TEST_F(InterpolateTest, LanguagePreference) {
876 add(File: "foo/bar/baz/exact.C");
877 add(File: "foo/bar/baz/exact.c");
878 add(File: "other/random/path.cpp");
879 // Proxies for ".H" files are ".C" files, and not ".c files".
880 EXPECT_EQ(getProxy("foo/bar/baz/exact.H"), "foo/bar/baz/exact.C");
881}
882
883TEST_F(InterpolateTest, Aliasing) {
884 add(File: "foo.cpp", Flags: "-faligned-new");
885
886 // The interpolated command should keep the given flag as written, even though
887 // the flag is internally represented as an alias.
888 EXPECT_EQ(getCommand("foo.hpp"), "clang -D foo.cpp -faligned-new");
889}
890
891TEST_F(InterpolateTest, ClangCL) {
892 add(File: "foo.cpp", Clang: "clang-cl", Flags: "/W4");
893
894 // Language flags should be added with CL syntax.
895 EXPECT_EQ(getCommand("foo.h", false), "clang-cl -D foo.cpp /W4 /TP");
896}
897
898TEST_F(InterpolateTest, DriverModes) {
899 add(File: "foo.cpp", Clang: "clang-cl", Flags: "--driver-mode=gcc");
900 add(File: "bar.cpp", Clang: "clang", Flags: "--driver-mode=cl");
901
902 // --driver-mode overrides should be respected.
903 EXPECT_EQ(getCommand("foo.h"),
904 "clang-cl -D foo.cpp --driver-mode=gcc -x c++-header");
905 EXPECT_EQ(getCommand("bar.h", false),
906 "clang -D bar.cpp --driver-mode=cl /TP");
907}
908
909TEST(TransferCompileCommandTest, Smoke) {
910 CompileCommand Cmd;
911 Cmd.Filename = "foo.cc";
912 Cmd.CommandLine = {"clang", "-Wall", "foo.cc"};
913 Cmd.Directory = "dir";
914 CompileCommand Transferred = transferCompileCommand(std::move(Cmd), Filename: "foo.h");
915 EXPECT_EQ(Transferred.Filename, "foo.h");
916 EXPECT_THAT(Transferred.CommandLine,
917 ElementsAre("clang", "-Wall", "-x", "c++-header", "--", "foo.h"));
918 EXPECT_EQ(Transferred.Directory, "dir");
919}
920
921TEST(CompileCommandTest, EqualityOperator) {
922 CompileCommand CCRef("/foo/bar", "hello.c", {"a", "b"}, "hello.o");
923 CompileCommand CCTest = CCRef;
924
925 EXPECT_TRUE(CCRef == CCTest);
926 EXPECT_FALSE(CCRef != CCTest);
927
928 CCTest = CCRef;
929 CCTest.Directory = "/foo/baz";
930 EXPECT_FALSE(CCRef == CCTest);
931 EXPECT_TRUE(CCRef != CCTest);
932
933 CCTest = CCRef;
934 CCTest.Filename = "bonjour.c";
935 EXPECT_FALSE(CCRef == CCTest);
936 EXPECT_TRUE(CCRef != CCTest);
937
938 CCTest = CCRef;
939 CCTest.CommandLine.push_back(x: "c");
940 EXPECT_FALSE(CCRef == CCTest);
941 EXPECT_TRUE(CCRef != CCTest);
942
943 CCTest = CCRef;
944 CCTest.Output = "bonjour.o";
945 EXPECT_FALSE(CCRef == CCTest);
946 EXPECT_TRUE(CCRef != CCTest);
947}
948
949class TargetAndModeTest : public MemDBTest {
950public:
951 TargetAndModeTest() { llvm::InitializeAllTargetInfos(); }
952
953protected:
954 // Look up the command from a relative path, and return it in string form.
955 std::string getCommand(llvm::StringRef F) {
956 auto Results = inferTargetAndDriverMode(Base: std::make_unique<MemCDB>(args&: Entries))
957 ->getCompileCommands(FilePath: path(File: F));
958 if (Results.empty())
959 return "none";
960 return llvm::join(R&: Results[0].CommandLine, Separator: " ");
961 }
962};
963
964TEST_F(TargetAndModeTest, TargetAndMode) {
965 add(File: "foo.cpp", Clang: "clang-cl", Flags: "");
966 add(File: "bar.cpp", Clang: "clang++", Flags: "");
967
968 EXPECT_EQ(getCommand("foo.cpp"),
969 "clang-cl --driver-mode=cl foo.cpp -D foo.cpp");
970 EXPECT_EQ(getCommand("bar.cpp"),
971 "clang++ --driver-mode=g++ bar.cpp -D bar.cpp");
972}
973
974class ExpandResponseFilesTest : public MemDBTest {
975public:
976 ExpandResponseFilesTest() : FS(new llvm::vfs::InMemoryFileSystem) {}
977
978protected:
979 void addFile(StringRef File, StringRef Content) {
980 ASSERT_TRUE(
981 FS->addFile(File, 0, llvm::MemoryBuffer::getMemBufferCopy(Content)));
982 }
983
984 std::string getCommand(llvm::StringRef F) {
985 auto Results = expandResponseFiles(Base: std::make_unique<MemCDB>(args&: Entries), FS)
986 ->getCompileCommands(FilePath: path(File: F));
987 if (Results.empty())
988 return "none";
989 return llvm::join(R&: Results[0].CommandLine, Separator: " ");
990 }
991
992 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS;
993};
994
995TEST_F(ExpandResponseFilesTest, ExpandResponseFiles) {
996 addFile(File: path(File: StringRef("rsp1.rsp")), Content: "-Dflag");
997
998 add(File: "foo.cpp", Clang: "clang", Flags: "@rsp1.rsp");
999 add(File: "bar.cpp", Clang: "clang", Flags: "-Dflag");
1000 EXPECT_EQ(getCommand("foo.cpp"), "clang foo.cpp -D foo.cpp -Dflag");
1001 EXPECT_EQ(getCommand("bar.cpp"), "clang bar.cpp -D bar.cpp -Dflag");
1002}
1003
1004TEST_F(ExpandResponseFilesTest, ExpandResponseFilesEmptyArgument) {
1005 addFile(File: path(File: StringRef("rsp1.rsp")), Content: "-Dflag");
1006
1007 add(File: "foo.cpp", Clang: "clang", Flags: "@rsp1.rsp \"\"");
1008 EXPECT_EQ(getCommand("foo.cpp"), "clang foo.cpp -D foo.cpp -Dflag ");
1009}
1010
1011} // end namespace tooling
1012} // end namespace clang
1013

source code of clang/unittests/Tooling/CompilationDatabaseTest.cpp