14#include "clang/Testing/CommandLineArgs.h"
15#include "clang/Tooling/ArgumentsAdjusters.h"
16#include "llvm/ADT/ArrayRef.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/ADT/ScopeExit.h"
19#include "llvm/ADT/StringExtras.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/Config/llvm-config.h"
22#include "llvm/Support/FileSystem.h"
23#include "llvm/Support/Path.h"
24#include "llvm/Support/Process.h"
25#include "llvm/Support/TargetSelect.h"
27#include "gmock/gmock.h"
28#include "gtest/gtest.h"
35using ::testing::Contains;
36using ::testing::ElementsAre;
37using ::testing::HasSubstr;
46TEST(CommandMangler, Everything) {
47 llvm::InitializeAllTargetInfos();
48 std::string Target = getAnyTargetForTesting();
50 Mangler.ClangPath =
testPath(
"fake/clang");
51 Mangler.ResourceDir =
testPath(
"fake/resources");
52 Mangler.Sysroot =
testPath(
"fake/sysroot");
53 tooling::CompileCommand Cmd;
54 Cmd.CommandLine = {Target +
"-clang++",
"--",
"foo.cc",
"bar.cc"};
55 Mangler(Cmd,
"foo.cc");
56 EXPECT_THAT(Cmd.CommandLine,
57 ElementsAre(
testPath(
"fake/" + Target +
"-clang++"),
58 "--target=" + Target,
"--driver-mode=g++",
59 "-resource-dir=" +
testPath(
"fake/resources"),
60 "-isysroot",
testPath(
"fake/sysroot"),
"--",
64TEST(CommandMangler, FilenameMismatch) {
66 Mangler.ClangPath =
testPath(
"clang");
68 tooling::CompileCommand Cmd;
69 Cmd.CommandLine = {
"clang",
"foo.cc"};
71 Mangler(Cmd,
"foo.h");
73 EXPECT_THAT(Cmd.CommandLine, ElementsAre(
testPath(
"clang"),
"-x",
74 "c++-header",
"--",
"foo.h"));
77TEST(CommandMangler, ResourceDir) {
79 Mangler.ResourceDir =
testPath(
"fake/resources");
80 tooling::CompileCommand Cmd;
81 Cmd.CommandLine = {
"clang++",
"foo.cc"};
82 Mangler(Cmd,
"foo.cc");
83 EXPECT_THAT(Cmd.CommandLine,
84 Contains(
"-resource-dir=" +
testPath(
"fake/resources")));
89 Mangler.Sysroot =
testPath(
"fake/sysroot");
91 tooling::CompileCommand Cmd;
92 Cmd.CommandLine = {
"clang++",
"foo.cc"};
93 Mangler(Cmd,
"foo.cc");
94 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
95 HasSubstr(
"-isysroot " +
testPath(
"fake/sysroot")));
98TEST(CommandMangler, ClangPath) {
100 Mangler.ClangPath =
testPath(
"fake/clang");
102 tooling::CompileCommand Cmd;
103 Cmd.CommandLine = {
"clang++",
"foo.cc"};
104 Mangler(Cmd,
"foo.cc");
105 EXPECT_EQ(
testPath(
"fake/clang++"), Cmd.CommandLine.front());
107 Cmd.CommandLine = {
"unknown-binary",
"foo.cc"};
108 Mangler(Cmd,
"foo.cc");
109 EXPECT_EQ(
testPath(
"fake/unknown-binary"), Cmd.CommandLine.front());
111 Cmd.CommandLine = {
testPath(
"path/clang++"),
"foo.cc"};
112 Mangler(Cmd,
"foo.cc");
113 EXPECT_EQ(
testPath(
"path/clang++"), Cmd.CommandLine.front());
115 Cmd.CommandLine = {
"foo/unknown-binary",
"foo.cc"};
116 Mangler(Cmd,
"foo.cc");
117 EXPECT_EQ(
"foo/unknown-binary", Cmd.CommandLine.front());
125 *result_listener << arg.message();
131TEST(CommandMangler, ClangPathResolve) {
138 llvm::SmallString<256> TempDir;
139 ASSERT_THAT(llvm::sys::fs::createUniqueDirectory(
"ClangPathResolve", TempDir),
142 ASSERT_THAT(llvm::sys::fs::real_path(TempDir.str(), TempDir), ok());
143 auto CleanDir = llvm::make_scope_exit(
144 [&] { llvm::sys::fs::remove_directories(TempDir); });
145 ASSERT_THAT(llvm::sys::fs::create_directory(TempDir +
"/bin"), ok());
146 ASSERT_THAT(llvm::sys::fs::create_directory(TempDir +
"/lib"), ok());
148 ASSERT_THAT(llvm::sys::fs::openFileForWrite(TempDir +
"/lib/bar", FD), ok());
149 ASSERT_THAT(llvm::sys::Process::SafelyCloseFileDescriptor(FD), ok());
150 ::chmod((TempDir +
"/lib/bar").str().c_str(), 0755);
152 llvm::sys::fs::create_link(TempDir +
"/lib/bar", TempDir +
"/bin/foo"),
157 Mangler.ClangPath =
testPath(
"fake/clang");
158 tooling::CompileCommand Cmd;
159 Cmd.CommandLine = {(TempDir +
"/bin/foo").str(),
"foo.cc"};
160 Mangler(Cmd,
"foo.cc");
162 EXPECT_EQ((TempDir +
"/lib/foo").str(), Cmd.CommandLine.front());
165 ASSERT_TRUE(::getenv(
"PATH"));
167 llvm::make_scope_exit([OldPath = std::string(::getenv(
"PATH"))] {
168 ::setenv(
"PATH", OldPath.c_str(), 1);
170 ::setenv(
"PATH", (TempDir +
"/bin").str().c_str(), 1);
174 Mangler.ClangPath =
testPath(
"fake/clang");
176 Cmd.CommandLine = {
"foo",
"foo.cc"};
177 Mangler(Cmd,
"foo.cc");
179 EXPECT_EQ((TempDir +
"/lib/foo").str(), Cmd.CommandLine.front());
182 Cmd.CommandLine = {
"foo",
"-no-canonical-prefixes",
"foo.cc"};
183 Mangler(Cmd,
"foo.cc");
184 EXPECT_EQ((TempDir +
"/bin/foo").str(), Cmd.CommandLine.front());
188TEST(CommandMangler, ConfigEdits) {
190 tooling::CompileCommand Cmd;
191 Cmd.CommandLine = {
"clang++",
"foo.cc"};
195 for (
auto &Arg : Argv)
197 C = llvm::toUpper(
C);
200 Argv = tooling::getInsertArgumentAdjuster(
"--hello")(Argv,
"");
202 WithContextValue WithConfig(
Config::Key, std::move(Cfg));
203 Mangler(Cmd,
"foo.cc");
209 EXPECT_THAT(Cmd.CommandLine,
210 ElementsAre(_,
"--driver-mode=g++",
"--hello",
"--",
"FOO.CC"));
213static std::string strip(llvm::StringRef Arg, llvm::StringRef Argv) {
214 llvm::SmallVector<llvm::StringRef> Parts;
215 llvm::SplitString(Argv, Parts);
216 std::vector<std::string>
Args = {Parts.begin(), Parts.end()};
223TEST(ArgStripperTest, Spellings) {
225 EXPECT_EQ(strip(
"-pedantic",
"clang -pedantic foo.cc"),
"clang foo.cc");
226 EXPECT_EQ(strip(
"-pedantic",
"clang --pedantic foo.cc"),
"clang foo.cc");
227 EXPECT_EQ(strip(
"--pedantic",
"clang -pedantic foo.cc"),
"clang foo.cc");
228 EXPECT_EQ(strip(
"--pedantic",
"clang --pedantic foo.cc"),
"clang foo.cc");
230 EXPECT_EQ(strip(
"-x",
"clang -x c++ foo.cc"),
"clang foo.cc");
231 EXPECT_EQ(strip(
"-x",
"clang --language=c++ foo.cc"),
"clang foo.cc");
232 EXPECT_EQ(strip(
"--language=",
"clang -x c++ foo.cc"),
"clang foo.cc");
233 EXPECT_EQ(strip(
"--language=",
"clang --language=c++ foo.cc"),
237TEST(ArgStripperTest, UnknownFlag) {
238 EXPECT_EQ(strip(
"-xyzzy",
"clang -xyzzy foo.cc"),
"clang foo.cc");
239 EXPECT_EQ(strip(
"-xyz*",
"clang -xyzzy foo.cc"),
"clang foo.cc");
240 EXPECT_EQ(strip(
"-xyzzy",
"clang -Xclang -xyzzy foo.cc"),
"clang foo.cc");
243TEST(ArgStripperTest, Xclang) {
245 EXPECT_EQ(strip(
"-ast-dump",
"clang -Xclang -ast-dump foo.cc"),
248 EXPECT_EQ(strip(
"-add-plugin",
"clang -Xclang -add-plugin -Xclang z foo.cc"),
252TEST(ArgStripperTest, ClangCL) {
255 EXPECT_EQ(strip(
"-I",
"clang -I /usr/inc /Interesting/file.cc"),
256 "clang /Interesting/file.cc");
258 EXPECT_EQ(strip(
"-I",
"clang-cl -I /usr/inc /Interesting/file.cc"),
261 EXPECT_EQ(strip(
"-I",
"CL.EXE -I /usr/inc /Interesting/file.cc"),
"CL.EXE");
263 EXPECT_EQ(strip(
"-I",
"cc -I /usr/inc /Interesting/file.cc --driver-mode=cl"),
264 "cc --driver-mode=cl");
267TEST(ArgStripperTest, ArgStyles) {
269 EXPECT_EQ(strip(
"-Qn",
"clang -Qn foo.cc"),
"clang foo.cc");
270 EXPECT_EQ(strip(
"-Qn",
"clang -QnZ foo.cc"),
"clang -QnZ foo.cc");
272 EXPECT_EQ(strip(
"-std=",
"clang -std= foo.cc"),
"clang foo.cc");
273 EXPECT_EQ(strip(
"-std=",
"clang -std=c++11 foo.cc"),
"clang foo.cc");
275 EXPECT_EQ(strip(
"-mllvm",
"clang -mllvm X foo.cc"),
"clang foo.cc");
276 EXPECT_EQ(strip(
"-mllvm",
"clang -mllvmX foo.cc"),
"clang -mllvmX foo.cc");
278 EXPECT_EQ(strip(
"/link",
"clang-cl /link b c d foo.cc"),
"clang-cl");
279 EXPECT_EQ(strip(
"/link",
"clang-cl /linka b c d foo.cc"),
"clang-cl");
281 EXPECT_EQ(strip(
"-Wl,",
"clang -Wl,x,y foo.cc"),
"clang foo.cc");
282 EXPECT_EQ(strip(
"-Wl,",
"clang -Wl, foo.cc"),
"clang foo.cc");
284 EXPECT_EQ(strip(
"-segaddr",
"clang -segaddr a b foo.cc"),
"clang foo.cc");
285 EXPECT_EQ(strip(
"-segaddr",
"clang -segaddra b foo.cc"),
286 "clang -segaddra b foo.cc");
288 EXPECT_EQ(strip(
"-G",
"clang -GX foo.cc"),
"clang foo.cc");
289 EXPECT_EQ(strip(
"-G",
"clang -G X foo.cc"),
"clang foo.cc");
291 EXPECT_EQ(strip(
"-plugin-arg-",
"clang -cc1 -plugin-arg-X Y foo.cc"),
292 "clang -cc1 foo.cc");
293 EXPECT_EQ(strip(
"-plugin-arg-",
"clang -cc1 -plugin-arg- Y foo.cc"),
294 "clang -cc1 foo.cc");
297TEST(ArgStripperTest, EndOfList) {
300 EXPECT_EQ(strip(
"-I",
"clang -Xclang"),
"clang -Xclang");
301 EXPECT_EQ(strip(
"-I",
"clang -Xclang -I"),
"clang");
302 EXPECT_EQ(strip(
"-I",
"clang -I -Xclang"),
"clang");
303 EXPECT_EQ(strip(
"-I",
"clang -I"),
"clang");
306TEST(ArgStripperTest, Multiple) {
310 std::vector<std::string>
Args = {
"clang",
"-o",
"foo.o",
"foo.cc",
"-c"};
312 EXPECT_THAT(
Args, ElementsAre(
"clang",
"foo.cc"));
320 std::vector<std::string>
Args = {
"clang",
"-Wfoo",
"-Wno-bar",
"-Werror",
323 EXPECT_THAT(
Args, ElementsAre(
"clang",
"foo.cc"));
329 std::vector<std::string>
Args = {
"clang",
"-Wunused",
"-Wno-unused",
332 EXPECT_THAT(
Args, ElementsAre(
"clang",
"-Wno-unused",
"foo.cc"));
336TEST(ArgStripperTest, Define) {
341 std::vector<std::string>
Args = {
"clang",
"-Dfoo",
"-Dbar=baz",
"foo.cc"};
343 EXPECT_THAT(
Args, ElementsAre(
"clang",
"foo.cc"));
349 std::vector<std::string>
Args = {
"clang",
"-Dfoo",
"-Dbar=baz",
"foo.cc"};
351 EXPECT_THAT(
Args, ElementsAre(
"clang",
"-Dfoo",
"-Dbar=baz",
"foo.cc"));
354 EXPECT_THAT(
Args, ElementsAre(
"clang",
"-Dbar=baz",
"foo.cc"));
357 EXPECT_THAT(
Args, ElementsAre(
"clang",
"foo.cc"));
361TEST(ArgStripperTest, OrderDependent) {
366 S.strip(
"-include-pch");
367 std::vector<std::string>
Args = {
"clang",
"-include-pch",
"foo.pch",
370 EXPECT_THAT(
Args, ElementsAre(
"clang",
"foo.cc"));
374 std::vector<llvm::StringRef>
Args = {
"one",
"two",
"thr ee",
375 "f\"o\"ur",
"fi\\ve",
"$"};
376 const char *
Expected = R
"(one two "thr ee" "f\"o\"ur" "fi\\ve" $)";
380TEST(CommandMangler, InputsAfterDashDash) {
383 tooling::CompileCommand Cmd;
384 Cmd.CommandLine = {
"clang",
"/Users/foo.cc"};
385 Mangler(Cmd,
"/Users/foo.cc");
386 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).take_back(2),
387 ElementsAre(
"--",
"/Users/foo.cc"));
388 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).drop_back(2),
389 Not(Contains(
"/Users/foo.cc")));
394 tooling::CompileCommand Cmd;
395 Cmd.CommandLine = {
"clang",
"--driver-mode=cl",
"bar.cc",
"/Users/foo.cc"};
396 Mangler(Cmd,
"bar.cc");
397 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).take_back(2),
398 ElementsAre(
"--",
"bar.cc"));
399 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).drop_back(2),
400 Not(Contains(
"bar.cc")));
404 tooling::CompileCommand Cmd;
405 Cmd.CommandLine = {
"clang",
"foo.cc",
"bar.cc"};
406 Mangler(Cmd,
"baz.cc");
407 EXPECT_THAT(llvm::ArrayRef(Cmd.CommandLine).take_back(2),
408 ElementsAre(
"--",
"baz.cc"));
410 llvm::ArrayRef(Cmd.CommandLine).drop_back(2),
411 testing::AllOf(Not(Contains(
"foo.cc")), Not(Contains(
"bar.cc"))));
415TEST(CommandMangler, StripsMultipleArch) {
417 tooling::CompileCommand Cmd;
418 Cmd.CommandLine = {
"clang",
"-arch",
"foo",
"-arch",
"bar",
"/Users/foo.cc"};
419 Mangler(Cmd,
"/Users/foo.cc");
420 EXPECT_EQ(llvm::count_if(Cmd.CommandLine,
421 [](llvm::StringRef Arg) { return Arg ==
"-arch"; }),
425 Cmd.CommandLine = {
"clang",
"-arch",
"foo",
"/Users/foo.cc"};
426 Mangler(Cmd,
"/Users/foo.cc");
427 EXPECT_EQ(llvm::count_if(Cmd.CommandLine,
428 [](llvm::StringRef Arg) { return Arg ==
"-arch"; }),
432TEST(CommandMangler, EmptyArgs) {
434 tooling::CompileCommand Cmd;
435 Cmd.CommandLine = {};
437 Mangler(Cmd,
"foo.cc");
440TEST(CommandMangler, PathsAsPositional) {
442 tooling::CompileCommand Cmd;
450 Mangler(Cmd,
"a.cc");
451 EXPECT_THAT(Cmd.CommandLine, Contains(
"foo"));
454TEST(CommandMangler, RespectsOriginalResourceDir) {
456 Mangler.ResourceDir =
testPath(
"fake/resources");
459 tooling::CompileCommand Cmd;
460 Cmd.CommandLine = {
"clang++",
"-resource-dir",
testPath(
"true/resources"),
462 Mangler(Cmd,
"foo.cc");
463 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
464 HasSubstr(
"-resource-dir " +
testPath(
"true/resources")));
465 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
466 Not(HasSubstr(
testPath(
"fake/resources"))));
470 tooling::CompileCommand Cmd;
471 Cmd.CommandLine = {
"clang++",
"-resource-dir=" +
testPath(
"true/resources"),
473 Mangler(Cmd,
"foo.cc");
474 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
475 HasSubstr(
"-resource-dir=" +
testPath(
"true/resources")));
476 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
477 Not(HasSubstr(
testPath(
"fake/resources"))));
481TEST(CommandMangler, RespectsOriginalSysroot) {
483 Mangler.Sysroot =
testPath(
"fake/sysroot");
486 tooling::CompileCommand Cmd;
487 Cmd.CommandLine = {
"clang++",
"-isysroot",
testPath(
"true/sysroot"),
489 Mangler(Cmd,
"foo.cc");
490 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
491 HasSubstr(
"-isysroot " +
testPath(
"true/sysroot")));
492 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
493 Not(HasSubstr(
testPath(
"fake/sysroot"))));
497 tooling::CompileCommand Cmd;
498 Cmd.CommandLine = {
"clang++",
"-isysroot" +
testPath(
"true/sysroot"),
500 Mangler(Cmd,
"foo.cc");
501 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
502 HasSubstr(
"-isysroot" +
testPath(
"true/sysroot")));
503 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
504 Not(HasSubstr(
testPath(
"fake/sysroot"))));
508 tooling::CompileCommand Cmd;
509 Cmd.CommandLine = {
"clang++",
"--sysroot",
testPath(
"true/sysroot"),
511 Mangler(Cmd,
"foo.cc");
512 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
513 HasSubstr(
"--sysroot " +
testPath(
"true/sysroot")));
514 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
515 Not(HasSubstr(
testPath(
"fake/sysroot"))));
519 tooling::CompileCommand Cmd;
520 Cmd.CommandLine = {
"clang++",
"--sysroot=" +
testPath(
"true/sysroot"),
522 Mangler(Cmd,
"foo.cc");
523 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
524 HasSubstr(
"--sysroot=" +
testPath(
"true/sysroot")));
525 EXPECT_THAT(llvm::join(Cmd.CommandLine,
" "),
526 Not(HasSubstr(
testPath(
"fake/sysroot"))));
std::vector< const char * > Expected
@ Warning
A warning message.
std::string testPath(PathRef File, llvm::sys::path::Style Style)
TEST(BackgroundQueueTest, Priority)
std::string printArgv(llvm::ArrayRef< llvm::StringRef > Args)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static CommandMangler forTests()
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
struct clang::clangd::Config::@2 CompileFlags
Controls how the compile command for the current file is determined.
std::vector< llvm::unique_function< void(std::vector< std::string > &) const > > Edits
Edits to apply to the compile command, in sequence.