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 {"sccache clang++ foo.c", "clang++ foo.c"},
406 {"ccache gcc foo.c", "gcc foo.c"},
407 {"ccache.exe gcc foo.c", "gcc foo.c"},
408 {"ccache g++.exe foo.c", "g++.exe foo.c"},
409 {"ccache distcc gcc foo.c", "gcc foo.c"},
410
411 {"distcc foo.c", "distcc foo.c"},
412 {"distcc -I/foo/bar foo.c", "distcc -I/foo/bar foo.c"},
413 };
414 std::string ErrorMessage;
415
416 for (const auto &Case : Cases) {
417 std::string DB =
418 R"([{"directory":"//net/dir", "file":"//net/dir/foo.c", "command":")" +
419 Case.first + "\"}]";
420 CompileCommand FoundCommand =
421 findCompileArgsInJsonDatabase(FileName: "//net/dir/foo.c", JSONDatabase: DB, ErrorMessage);
422 EXPECT_EQ(Case.second, llvm::join(FoundCommand.CommandLine, " "))
423 << Case.first;
424 }
425}
426
427static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
428 std::string JsonDatabase =
429 ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
430 Command + "\"}]").str();
431 std::string ErrorMessage;
432 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
433 FileName: "//net/root/test", JSONDatabase: JsonDatabase, ErrorMessage);
434 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
435 return FoundCommand.CommandLine;
436}
437
438TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
439 std::vector<std::string> Result = unescapeJsonCommandLine(Command: "");
440 EXPECT_TRUE(Result.empty());
441}
442
443TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
444 std::vector<std::string> Result = unescapeJsonCommandLine(Command: "a b c");
445 ASSERT_EQ(3ul, Result.size());
446 EXPECT_EQ("a", Result[0]);
447 EXPECT_EQ("b", Result[1]);
448 EXPECT_EQ("c", Result[2]);
449}
450
451TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
452 std::vector<std::string> Result = unescapeJsonCommandLine(Command: " a b ");
453 ASSERT_EQ(2ul, Result.size());
454 EXPECT_EQ("a", Result[0]);
455 EXPECT_EQ("b", Result[1]);
456}
457
458TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
459 std::vector<std::string> Backslash = unescapeJsonCommandLine(Command: "a\\\\\\\\");
460 ASSERT_EQ(1ul, Backslash.size());
461 EXPECT_EQ("a\\", Backslash[0]);
462 std::vector<std::string> Quote = unescapeJsonCommandLine(Command: "a\\\\\\\"");
463 ASSERT_EQ(1ul, Quote.size());
464 EXPECT_EQ("a\"", Quote[0]);
465}
466
467TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
468 std::vector<std::string> Result = unescapeJsonCommandLine(Command: "\\\" a b \\\"");
469 ASSERT_EQ(1ul, Result.size());
470 EXPECT_EQ(" a b ", Result[0]);
471}
472
473TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
474 std::vector<std::string> Result = unescapeJsonCommandLine(
475 Command: " \\\" a \\\" \\\" b \\\" ");
476 ASSERT_EQ(2ul, Result.size());
477 EXPECT_EQ(" a ", Result[0]);
478 EXPECT_EQ(" b ", Result[1]);
479}
480
481TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
482 std::vector<std::string> Result = unescapeJsonCommandLine(
483 Command: "\\\"\\\"\\\"\\\"");
484 ASSERT_EQ(1ul, Result.size());
485 EXPECT_TRUE(Result[0].empty()) << Result[0];
486}
487
488TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
489 std::vector<std::string> Result = unescapeJsonCommandLine(
490 Command: "\\\"\\\\\\\"\\\"");
491 ASSERT_EQ(1ul, Result.size());
492 EXPECT_EQ("\"", Result[0]);
493}
494
495TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
496 std::vector<std::string> Result = unescapeJsonCommandLine(
497 Command: " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
498 ASSERT_EQ(4ul, Result.size());
499 EXPECT_EQ("\"", Result[0]);
500 EXPECT_EQ("a \" b ", Result[1]);
501 EXPECT_EQ("and\\c", Result[2]);
502 EXPECT_EQ("\"", Result[3]);
503}
504
505TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
506 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
507 Command: "\\\"a\\\"\\\"b\\\"");
508 ASSERT_EQ(1ul, QuotedNoSpaces.size());
509 EXPECT_EQ("ab", QuotedNoSpaces[0]);
510
511 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
512 Command: "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
513 ASSERT_EQ(1ul, MixedNoSpaces.size());
514 EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
515}
516
517TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
518 std::vector<std::string> Unclosed = unescapeJsonCommandLine(Command: "\\\"abc");
519 ASSERT_EQ(1ul, Unclosed.size());
520 EXPECT_EQ("abc", Unclosed[0]);
521
522 std::vector<std::string> Empty = unescapeJsonCommandLine(Command: "\\\"");
523 ASSERT_EQ(1ul, Empty.size());
524 EXPECT_EQ("", Empty[0]);
525}
526
527TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) {
528 std::vector<std::string> Args = unescapeJsonCommandLine(Command: "a'\\\\b \\\"c\\\"'");
529 ASSERT_EQ(1ul, Args.size());
530 EXPECT_EQ("a\\b \"c\"", Args[0]);
531}
532
533TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
534 FixedCompilationDatabase Database(".", /*CommandLine*/ {"one", "two"});
535 StringRef FileName("source");
536 std::vector<CompileCommand> Result =
537 Database.getCompileCommands(FilePath: FileName);
538 ASSERT_EQ(1ul, Result.size());
539 EXPECT_EQ(".", Result[0].Directory);
540 EXPECT_EQ(FileName, Result[0].Filename);
541 EXPECT_THAT(Result[0].CommandLine,
542 ElementsAre(EndsWith("clang-tool"), "one", "two", "source"));
543}
544
545TEST(FixedCompilationDatabase, GetAllFiles) {
546 std::vector<std::string> CommandLine;
547 CommandLine.push_back(x: "one");
548 CommandLine.push_back(x: "two");
549 FixedCompilationDatabase Database(".", CommandLine);
550
551 EXPECT_THAT(Database.getAllFiles(), IsEmpty());
552}
553
554TEST(FixedCompilationDatabase, GetAllCompileCommands) {
555 std::vector<std::string> CommandLine;
556 CommandLine.push_back(x: "one");
557 CommandLine.push_back(x: "two");
558 FixedCompilationDatabase Database(".", CommandLine);
559
560 EXPECT_EQ(0ul, Database.getAllCompileCommands().size());
561}
562
563TEST(FixedCompilationDatabase, FromBuffer) {
564 const char *Data = R"(
565
566 -DFOO=BAR
567
568--baz
569
570 )";
571 std::string ErrorMsg;
572 auto CDB =
573 FixedCompilationDatabase::loadFromBuffer(Directory: "/cdb/dir", Data, ErrorMsg);
574
575 std::vector<CompileCommand> Result = CDB->getCompileCommands(FilePath: "/foo/bar.cc");
576 ASSERT_EQ(1ul, Result.size());
577 EXPECT_EQ("/cdb/dir", Result.front().Directory);
578 EXPECT_EQ("/foo/bar.cc", Result.front().Filename);
579 EXPECT_THAT(
580 Result.front().CommandLine,
581 ElementsAre(EndsWith("clang-tool"), "-DFOO=BAR", "--baz", "/foo/bar.cc"));
582}
583
584TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
585 int Argc = 0;
586 std::string ErrorMsg;
587 std::unique_ptr<FixedCompilationDatabase> Database =
588 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv: nullptr, ErrorMsg);
589 EXPECT_FALSE(Database);
590 EXPECT_TRUE(ErrorMsg.empty());
591 EXPECT_EQ(0, Argc);
592}
593
594TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
595 int Argc = 2;
596 const char *Argv[] = { "1", "2" };
597 std::string ErrorMsg;
598 std::unique_ptr<FixedCompilationDatabase> Database(
599 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
600 EXPECT_FALSE(Database);
601 EXPECT_TRUE(ErrorMsg.empty());
602 EXPECT_EQ(2, Argc);
603}
604
605TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
606 int Argc = 5;
607 const char *Argv[] = {
608 "1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4"
609 };
610 std::string ErrorMsg;
611 std::unique_ptr<FixedCompilationDatabase> Database(
612 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
613 ASSERT_TRUE((bool)Database);
614 ASSERT_TRUE(ErrorMsg.empty());
615 std::vector<CompileCommand> Result =
616 Database->getCompileCommands(FilePath: "source");
617 ASSERT_EQ(1ul, Result.size());
618 ASSERT_EQ(".", Result[0].Directory);
619 ASSERT_THAT(Result[0].CommandLine, ElementsAre(EndsWith("clang-tool"),
620 "-DDEF3", "-DDEF4", "source"));
621 EXPECT_EQ(2, Argc);
622}
623
624TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
625 int Argc = 3;
626 const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
627 std::string ErrorMsg;
628 std::unique_ptr<FixedCompilationDatabase> Database =
629 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
630 ASSERT_TRUE((bool)Database);
631 ASSERT_TRUE(ErrorMsg.empty());
632 std::vector<CompileCommand> Result =
633 Database->getCompileCommands(FilePath: "source");
634 ASSERT_EQ(1ul, Result.size());
635 ASSERT_EQ(".", Result[0].Directory);
636 ASSERT_THAT(Result[0].CommandLine,
637 ElementsAre(EndsWith("clang-tool"), "source"));
638 EXPECT_EQ(2, Argc);
639}
640
641TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
642 const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"};
643 int Argc = std::size(Argv);
644 std::string ErrorMsg;
645 std::unique_ptr<FixedCompilationDatabase> Database =
646 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
647 ASSERT_TRUE((bool)Database);
648 ASSERT_TRUE(ErrorMsg.empty());
649 std::vector<CompileCommand> Result = Database->getCompileCommands(FilePath: "source");
650 ASSERT_EQ(1ul, Result.size());
651 ASSERT_EQ(".", Result[0].Directory);
652 ASSERT_THAT(Result[0].CommandLine,
653 ElementsAre(EndsWith("clang-tool"), "-c", "-DDEF3", "source"));
654 EXPECT_EQ(2, Argc);
655}
656
657TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsHeader) {
658 const char *Argv[] = {"1", "2", "--", "-xc++-header",
659 "-c", "somefile.h", "-DDEF3"};
660 int Argc = std::size(Argv);
661 std::string ErrorMsg;
662 std::unique_ptr<FixedCompilationDatabase> Database =
663 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
664 ASSERT_TRUE((bool)Database);
665 ASSERT_TRUE(ErrorMsg.empty());
666 std::vector<CompileCommand> Result =
667 Database->getCompileCommands(FilePath: "source");
668 ASSERT_EQ(1ul, Result.size());
669 ASSERT_EQ(".", Result[0].Directory);
670 ASSERT_THAT(Result[0].CommandLine,
671 ElementsAre(EndsWith("clang-tool"), "-xc++-header", "-c",
672 "-DDEF3", "source"));
673 EXPECT_EQ(2, Argc);
674}
675
676TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) {
677 // Adjust the given command line arguments to ensure that any positional
678 // arguments in them are stripped.
679 const char *Argv[] = {"--", "somefile.cpp", "-fsyntax-only", "-DDEF3"};
680 int Argc = std::size(Argv);
681 std::string ErrorMessage;
682 std::unique_ptr<CompilationDatabase> Database =
683 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg&: ErrorMessage);
684 ASSERT_TRUE((bool)Database);
685 ASSERT_TRUE(ErrorMessage.empty());
686 std::vector<CompileCommand> Result = Database->getCompileCommands(FilePath: "source");
687 ASSERT_EQ(1ul, Result.size());
688 ASSERT_EQ(".", Result[0].Directory);
689 ASSERT_THAT(
690 Result[0].CommandLine,
691 ElementsAre(EndsWith("clang-tool"), "-fsyntax-only", "-DDEF3", "source"));
692}
693
694TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
695 const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
696 int Argc = std::size(Argv);
697 std::string ErrorMsg;
698 std::unique_ptr<FixedCompilationDatabase> Database =
699 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
700 ASSERT_TRUE((bool)Database);
701 ASSERT_TRUE(ErrorMsg.empty());
702 std::vector<CompileCommand> Result =
703 Database->getCompileCommands(FilePath: "source");
704 ASSERT_EQ(1ul, Result.size());
705 ASSERT_EQ(".", Result[0].Directory);
706 ASSERT_THAT(Result[0].CommandLine,
707 ElementsAre(EndsWith("clang-tool"), "source"));
708 EXPECT_EQ(2, Argc);
709}
710
711struct MemCDB : public CompilationDatabase {
712 using EntryMap = llvm::StringMap<SmallVector<CompileCommand, 1>>;
713 EntryMap Entries;
714 MemCDB(const EntryMap &E) : Entries(E) {}
715
716 std::vector<CompileCommand> getCompileCommands(StringRef F) const override {
717 auto Ret = Entries.lookup(Key: F);
718 return {Ret.begin(), Ret.end()};
719 }
720
721 std::vector<std::string> getAllFiles() const override {
722 std::vector<std::string> Result;
723 for (const auto &Entry : Entries)
724 Result.push_back(x: std::string(Entry.first()));
725 return Result;
726 }
727};
728
729class MemDBTest : public ::testing::Test {
730protected:
731 // Adds an entry to the underlying compilation database.
732 // A flag is injected: -D <File>, so the command used can be identified.
733 void add(StringRef File, StringRef Clang, StringRef Flags) {
734 SmallVector<StringRef, 8> Argv = {Clang, File, "-D", File};
735 llvm::SplitString(Source: Flags, OutFragments&: Argv);
736
737 // Trim double quotation from the argumnets if any.
738 for (auto *It = Argv.begin(); It != Argv.end(); ++It)
739 *It = It->trim(Chars: "\"");
740
741 SmallString<32> Dir;
742 llvm::sys::path::system_temp_directory(erasedOnReboot: false, result&: Dir);
743
744 Entries[path(File)].push_back(
745 Elt: {Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"});
746 }
747 void add(StringRef File, StringRef Flags = "") { add(File, Clang: "clang", Flags); }
748
749 // Turn a unix path fragment (foo/bar.h) into a native path (C:\tmp\foo\bar.h)
750 std::string path(llvm::SmallString<32> File) {
751 llvm::SmallString<32> Dir;
752 llvm::sys::path::system_temp_directory(erasedOnReboot: false, result&: Dir);
753 llvm::sys::path::native(path&: File);
754 llvm::SmallString<64> Result;
755 llvm::sys::path::append(path&: Result, a: Dir, b: File);
756 return std::string(Result.str());
757 }
758
759 MemCDB::EntryMap Entries;
760};
761
762class InterpolateTest : public MemDBTest {
763protected:
764 // Look up the command from a relative path, and return it in string form.
765 // The input file is not included in the returned command.
766 std::string getCommand(llvm::StringRef F, bool MakeNative = true) {
767 auto Results =
768 inferMissingCompileCommands(std::make_unique<MemCDB>(args&: Entries))
769 ->getCompileCommands(FilePath: MakeNative ? path(File: F) : F);
770 if (Results.empty())
771 return "none";
772 // drop the input file argument, so tests don't have to deal with path().
773 EXPECT_EQ(Results[0].CommandLine.back(), MakeNative ? path(F) : F)
774 << "Last arg should be the file";
775 Results[0].CommandLine.pop_back();
776 EXPECT_EQ(Results[0].CommandLine.back(), "--")
777 << "Second-last arg should be --";
778 Results[0].CommandLine.pop_back();
779 return llvm::join(R&: Results[0].CommandLine, Separator: " ");
780 }
781
782 // Parse the file whose command was used out of the Heuristic string.
783 std::string getProxy(llvm::StringRef F) {
784 auto Results =
785 inferMissingCompileCommands(std::make_unique<MemCDB>(args&: Entries))
786 ->getCompileCommands(FilePath: path(File: F));
787 if (Results.empty())
788 return "none";
789 StringRef Proxy = Results.front().Heuristic;
790 if (!Proxy.consume_front(Prefix: "inferred from "))
791 return "";
792 // We have a proxy file, convert back to a unix relative path.
793 // This is a bit messy, but we do need to test these strings somehow...
794 llvm::SmallString<32> TempDir;
795 llvm::sys::path::system_temp_directory(erasedOnReboot: false, result&: TempDir);
796 Proxy.consume_front(Prefix: TempDir);
797 Proxy.consume_front(Prefix: llvm::sys::path::get_separator());
798 llvm::SmallString<32> Result = Proxy;
799 llvm::sys::path::native(path&: Result, style: llvm::sys::path::Style::posix);
800 return std::string(Result.str());
801 }
802};
803
804TEST_F(InterpolateTest, Nearby) {
805 add(File: "dir/foo.cpp");
806 add(File: "dir/bar.cpp");
807 add(File: "an/other/foo.cpp");
808
809 // great: dir and name both match (prefix or full, case insensitive)
810 EXPECT_EQ(getProxy("dir/f.cpp"), "dir/foo.cpp");
811 EXPECT_EQ(getProxy("dir/FOO.cpp"), "dir/foo.cpp");
812 // no name match. prefer matching dir, break ties by alpha
813 EXPECT_EQ(getProxy("dir/a.cpp"), "dir/bar.cpp");
814 // an exact name match beats one segment of directory match
815 EXPECT_EQ(getProxy("some/other/bar.h"), "dir/bar.cpp");
816 // two segments of directory match beat a prefix name match
817 EXPECT_EQ(getProxy("an/other/b.cpp"), "an/other/foo.cpp");
818 // if nothing matches at all, we still get the closest alpha match
819 EXPECT_EQ(getProxy("below/some/obscure/path.cpp"), "an/other/foo.cpp");
820}
821
822TEST_F(InterpolateTest, Language) {
823 add(File: "dir/foo.cpp", Flags: "-std=c++17");
824 add(File: "dir/bar.c", Flags: "");
825 add(File: "dir/baz.cee", Flags: "-x c");
826 add(File: "dir/aux.cpp", Flags: "-std=c++17 -x objective-c++");
827
828 // .h is ambiguous, so we add explicit language flags
829 EXPECT_EQ(getCommand("foo.h"),
830 "clang -D dir/foo.cpp -x c++-header -std=c++17");
831 // Same thing if we have no extension. (again, we treat as header).
832 EXPECT_EQ(getCommand("foo"), "clang -D dir/foo.cpp -x c++-header -std=c++17");
833 // and invalid extensions.
834 EXPECT_EQ(getCommand("foo.cce"),
835 "clang -D dir/foo.cpp -x c++-header -std=c++17");
836 // and don't add -x if the inferred language is correct.
837 EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17");
838 // respect -x if it's already there.
839 EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header");
840 // prefer a worse match with the right extension.
841 EXPECT_EQ(getCommand("foo.c"), "clang -D dir/bar.c");
842 Entries.erase(Key: path(File: StringRef("dir/bar.c")));
843 // Now we transfer across languages, so drop -std too.
844 EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp");
845 // Prefer -x over -std when overriding language.
846 EXPECT_EQ(getCommand("aux.h"),
847 "clang -D dir/aux.cpp -x objective-c++-header -std=c++17");
848}
849
850TEST_F(InterpolateTest, Strip) {
851 add(File: "dir/foo.cpp", Flags: "-o foo.o -Wall");
852 // the -o option and the input file are removed, but -Wall is preserved.
853 EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall");
854}
855
856TEST_F(InterpolateTest, StripDoubleDash) {
857 add(File: "dir/foo.cpp", Flags: "-o foo.o -std=c++14 -Wall -- dir/foo.cpp");
858 // input file and output option are removed
859 // -Wall flag isn't
860 // -std option gets re-added as the last argument before the input file
861 // -- is removed as it's not necessary - the new input file doesn't start with
862 // a dash
863 EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall -std=c++14");
864}
865
866TEST_F(InterpolateTest, Case) {
867 add(File: "FOO/BAR/BAZ/SHOUT.cc");
868 add(File: "foo/bar/baz/quiet.cc");
869 // Case mismatches are completely ignored, so we choose the name match.
870 EXPECT_EQ(getProxy("foo/bar/baz/shout.C"), "FOO/BAR/BAZ/SHOUT.cc");
871}
872
873TEST_F(InterpolateTest, LanguagePreference) {
874 add(File: "foo/bar/baz/exact.C");
875 add(File: "foo/bar/baz/exact.c");
876 add(File: "other/random/path.cpp");
877 // Proxies for ".H" files are ".C" files, and not ".c files".
878 EXPECT_EQ(getProxy("foo/bar/baz/exact.H"), "foo/bar/baz/exact.C");
879}
880
881TEST_F(InterpolateTest, Aliasing) {
882 add(File: "foo.cpp", Flags: "-faligned-new");
883
884 // The interpolated command should keep the given flag as written, even though
885 // the flag is internally represented as an alias.
886 EXPECT_EQ(getCommand("foo.hpp"), "clang -D foo.cpp -faligned-new");
887}
888
889TEST_F(InterpolateTest, ClangCL) {
890 add(File: "foo.cpp", Clang: "clang-cl", Flags: "/W4");
891
892 // Language flags should be added with CL syntax.
893 EXPECT_EQ(getCommand("foo.h", false), "clang-cl -D foo.cpp /W4 /TP");
894}
895
896TEST_F(InterpolateTest, DriverModes) {
897 add(File: "foo.cpp", Clang: "clang-cl", Flags: "--driver-mode=gcc");
898 add(File: "bar.cpp", Clang: "clang", Flags: "--driver-mode=cl");
899
900 // --driver-mode overrides should be respected.
901 EXPECT_EQ(getCommand("foo.h"),
902 "clang-cl -D foo.cpp --driver-mode=gcc -x c++-header");
903 EXPECT_EQ(getCommand("bar.h", false),
904 "clang -D bar.cpp --driver-mode=cl /TP");
905}
906
907TEST(TransferCompileCommandTest, Smoke) {
908 CompileCommand Cmd;
909 Cmd.Filename = "foo.cc";
910 Cmd.CommandLine = {"clang", "-Wall", "foo.cc"};
911 Cmd.Directory = "dir";
912 CompileCommand Transferred = transferCompileCommand(std::move(Cmd), Filename: "foo.h");
913 EXPECT_EQ(Transferred.Filename, "foo.h");
914 EXPECT_THAT(Transferred.CommandLine,
915 ElementsAre("clang", "-Wall", "-x", "c++-header", "--", "foo.h"));
916 EXPECT_EQ(Transferred.Directory, "dir");
917}
918
919TEST(CompileCommandTest, EqualityOperator) {
920 CompileCommand CCRef("/foo/bar", "hello.c", {"a", "b"}, "hello.o");
921 CompileCommand CCTest = CCRef;
922
923 EXPECT_TRUE(CCRef == CCTest);
924 EXPECT_FALSE(CCRef != CCTest);
925
926 CCTest = CCRef;
927 CCTest.Directory = "/foo/baz";
928 EXPECT_FALSE(CCRef == CCTest);
929 EXPECT_TRUE(CCRef != CCTest);
930
931 CCTest = CCRef;
932 CCTest.Filename = "bonjour.c";
933 EXPECT_FALSE(CCRef == CCTest);
934 EXPECT_TRUE(CCRef != CCTest);
935
936 CCTest = CCRef;
937 CCTest.CommandLine.push_back(x: "c");
938 EXPECT_FALSE(CCRef == CCTest);
939 EXPECT_TRUE(CCRef != CCTest);
940
941 CCTest = CCRef;
942 CCTest.Output = "bonjour.o";
943 EXPECT_FALSE(CCRef == CCTest);
944 EXPECT_TRUE(CCRef != CCTest);
945}
946
947class TargetAndModeTest : public MemDBTest {
948public:
949 TargetAndModeTest() { llvm::InitializeAllTargetInfos(); }
950
951protected:
952 // Look up the command from a relative path, and return it in string form.
953 std::string getCommand(llvm::StringRef F) {
954 auto Results = inferTargetAndDriverMode(Base: std::make_unique<MemCDB>(args&: Entries))
955 ->getCompileCommands(FilePath: path(File: F));
956 if (Results.empty())
957 return "none";
958 return llvm::join(R&: Results[0].CommandLine, Separator: " ");
959 }
960};
961
962TEST_F(TargetAndModeTest, TargetAndMode) {
963 add(File: "foo.cpp", Clang: "clang-cl", Flags: "");
964 add(File: "bar.cpp", Clang: "clang++", Flags: "");
965
966 EXPECT_EQ(getCommand("foo.cpp"),
967 "clang-cl --driver-mode=cl foo.cpp -D foo.cpp");
968 EXPECT_EQ(getCommand("bar.cpp"),
969 "clang++ --driver-mode=g++ bar.cpp -D bar.cpp");
970}
971
972class ExpandResponseFilesTest : public MemDBTest {
973public:
974 ExpandResponseFilesTest() : FS(new llvm::vfs::InMemoryFileSystem) {}
975
976protected:
977 void addFile(StringRef File, StringRef Content) {
978 ASSERT_TRUE(
979 FS->addFile(File, 0, llvm::MemoryBuffer::getMemBufferCopy(Content)));
980 }
981
982 std::string getCommand(llvm::StringRef F) {
983 auto Results = expandResponseFiles(Base: std::make_unique<MemCDB>(args&: Entries), FS)
984 ->getCompileCommands(FilePath: path(File: F));
985 if (Results.empty())
986 return "none";
987 return llvm::join(R&: Results[0].CommandLine, Separator: " ");
988 }
989
990 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS;
991};
992
993TEST_F(ExpandResponseFilesTest, ExpandResponseFiles) {
994 addFile(File: path(File: StringRef("rsp1.rsp")), Content: "-Dflag");
995
996 add(File: "foo.cpp", Clang: "clang", Flags: "@rsp1.rsp");
997 add(File: "bar.cpp", Clang: "clang", Flags: "-Dflag");
998 EXPECT_EQ(getCommand("foo.cpp"), "clang foo.cpp -D foo.cpp -Dflag");
999 EXPECT_EQ(getCommand("bar.cpp"), "clang bar.cpp -D bar.cpp -Dflag");
1000}
1001
1002TEST_F(ExpandResponseFilesTest, ExpandResponseFilesEmptyArgument) {
1003 addFile(File: path(File: StringRef("rsp1.rsp")), Content: "-Dflag");
1004
1005 add(File: "foo.cpp", Clang: "clang", Flags: "@rsp1.rsp \"\"");
1006 EXPECT_EQ(getCommand("foo.cpp"), "clang foo.cpp -D foo.cpp -Dflag ");
1007}
1008
1009} // end namespace tooling
1010} // end namespace clang
1011

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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