Skip to content

Commit 5a295dd

Browse files
committed
[clangd] Fix sysroot flag handling in CommandMangler to prevent duplicates
CommandMangler should guess the sysroot path of the host system and add that through `-isysroot` flag only when there is no `--sysroot` or `-isysroot` flag in the original compile command to avoid duplicate sysroot. Previously, CommandMangler appropriately avoided adding a guessed sysroot flag if the original command had an argument in the form of `--sysroot=<sysroot>`, `--sysroot <sysroot>`, or `-isysroot <sysroot>`. However, when presented as `-isysroot<sysroot>` (without spaces after `-isysroot`), CommandMangler mistakenly appended the guessed sysroot flag, resulting in duplicated sysroot in the final command. This commit fixes it, ensuring the final command has no duplicate sysroot flags. Also adds unit tests for this fix.
1 parent a4d1d5f commit 5a295dd

File tree

2 files changed

+91
-13
lines changed

2 files changed

+91
-13
lines changed

clang-tools-extra/clangd/CompileCommands.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -328,26 +328,29 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,
328328

329329
tooling::addTargetAndModeForProgramName(Cmd, Cmd.front());
330330

331-
// Check whether the flag exists, either as -flag or -flag=*
332-
auto Has = [&](llvm::StringRef Flag) {
333-
for (llvm::StringRef Arg : Cmd) {
334-
if (Arg.consume_front(Flag) && (Arg.empty() || Arg[0] == '='))
335-
return true;
336-
}
337-
return false;
331+
// Check whether the flag exists in the command.
332+
auto HasExact = [&](llvm::StringRef Flag) {
333+
return llvm::any_of(Cmd, [&](llvm::StringRef Arg) { return Arg == Flag; });
334+
};
335+
336+
// Check whether the flag appears in the command as a prefix.
337+
auto HasPrefix = [&](llvm::StringRef Flag) {
338+
return llvm::any_of(
339+
Cmd, [&](llvm::StringRef Arg) { return Arg.startswith(Flag); });
338340
};
339341

340342
llvm::erase_if(Cmd, [](llvm::StringRef Elem) {
341343
return Elem.starts_with("--save-temps") || Elem.starts_with("-save-temps");
342344
});
343345

344346
std::vector<std::string> ToAppend;
345-
if (ResourceDir && !Has("-resource-dir"))
347+
if (ResourceDir && !HasExact("-resource-dir") && !HasPrefix("-resource-dir="))
346348
ToAppend.push_back(("-resource-dir=" + *ResourceDir));
347349

348350
// Don't set `-isysroot` if it is already set or if `--sysroot` is set.
349351
// `--sysroot` is a superset of the `-isysroot` argument.
350-
if (Sysroot && !Has("-isysroot") && !Has("--sysroot")) {
352+
if (Sysroot && !HasPrefix("-isysroot") && !HasExact("--sysroot") &&
353+
!HasPrefix("--sysroot=")) {
351354
ToAppend.push_back("-isysroot");
352355
ToAppend.push_back(*Sysroot);
353356
}
@@ -358,7 +361,7 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,
358361
}
359362

360363
if (!Cmd.empty()) {
361-
bool FollowSymlink = !Has("-no-canonical-prefixes");
364+
bool FollowSymlink = !HasExact("-no-canonical-prefixes");
362365
Cmd.front() =
363366
(FollowSymlink ? ResolvedDrivers : ResolvedDriversNoFollow)
364367
.get(Cmd.front(), [&, this] {

clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,8 @@ TEST(ArgStripperTest, OrderDependent) {
385385
}
386386

387387
TEST(PrintArgvTest, All) {
388-
std::vector<llvm::StringRef> Args = {
389-
"one", "two", "thr ee", "f\"o\"ur", "fi\\ve", "$"
390-
};
388+
std::vector<llvm::StringRef> Args = {"one", "two", "thr ee",
389+
"f\"o\"ur", "fi\\ve", "$"};
391390
const char *Expected = R"(one two "thr ee" "f\"o\"ur" "fi\\ve" $)";
392391
EXPECT_EQ(Expected, printArgv(Args));
393392
}
@@ -465,6 +464,82 @@ TEST(CommandMangler, PathsAsPositional) {
465464
Mangler(Cmd, "a.cc");
466465
EXPECT_THAT(Cmd.CommandLine, Contains("foo"));
467466
}
467+
468+
TEST(CommandMangler, RespectsOriginalResourceDir) {
469+
auto Mangler = CommandMangler::forTests();
470+
Mangler.ResourceDir = testPath("fake/resources");
471+
472+
{
473+
tooling::CompileCommand Cmd;
474+
Cmd.CommandLine = {"clang++", "-resource-dir", testPath("true/resources"),
475+
"foo.cc"};
476+
Mangler(Cmd, "foo.cc");
477+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
478+
HasSubstr("-resource-dir " + testPath("true/resources")));
479+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
480+
Not(HasSubstr(testPath("fake/resources"))));
481+
}
482+
483+
{
484+
tooling::CompileCommand Cmd;
485+
Cmd.CommandLine = {"clang++", "-resource-dir=" + testPath("true/resources"),
486+
"foo.cc"};
487+
Mangler(Cmd, "foo.cc");
488+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
489+
HasSubstr("-resource-dir=" + testPath("true/resources")));
490+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
491+
Not(HasSubstr(testPath("fake/resources"))));
492+
}
493+
}
494+
495+
TEST(CommandMangler, RespectsOriginalSysroot) {
496+
auto Mangler = CommandMangler::forTests();
497+
Mangler.Sysroot = testPath("fake/sysroot");
498+
499+
{
500+
tooling::CompileCommand Cmd;
501+
Cmd.CommandLine = {"clang++", "-isysroot", testPath("true/sysroot"),
502+
"foo.cc"};
503+
Mangler(Cmd, "foo.cc");
504+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
505+
HasSubstr("-isysroot " + testPath("true/sysroot")));
506+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
507+
Not(HasSubstr(testPath("fake/sysroot"))));
508+
}
509+
510+
{
511+
tooling::CompileCommand Cmd;
512+
Cmd.CommandLine = {"clang++", "-isysroot" + testPath("true/sysroot"),
513+
"foo.cc"};
514+
Mangler(Cmd, "foo.cc");
515+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
516+
HasSubstr("-isysroot" + testPath("true/sysroot")));
517+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
518+
Not(HasSubstr(testPath("fake/sysroot"))));
519+
}
520+
521+
{
522+
tooling::CompileCommand Cmd;
523+
Cmd.CommandLine = {"clang++", "--sysroot", testPath("true/sysroot"),
524+
"foo.cc"};
525+
Mangler(Cmd, "foo.cc");
526+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
527+
HasSubstr("--sysroot " + testPath("true/sysroot")));
528+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
529+
Not(HasSubstr(testPath("fake/sysroot"))));
530+
}
531+
532+
{
533+
tooling::CompileCommand Cmd;
534+
Cmd.CommandLine = {"clang++", "--sysroot=" + testPath("true/sysroot"),
535+
"foo.cc"};
536+
Mangler(Cmd, "foo.cc");
537+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
538+
HasSubstr("--sysroot=" + testPath("true/sysroot")));
539+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
540+
Not(HasSubstr(testPath("fake/sysroot"))));
541+
}
542+
}
468543
} // namespace
469544
} // namespace clangd
470545
} // namespace clang

0 commit comments

Comments
 (0)