Skip to content

Commit f489fb3

Browse files
authored
[clangd] Fix sysroot flag handling in CommandMangler to prevent duplicates (#75694)
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 c297597 commit f489fb3

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
@@ -313,26 +313,29 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,
313313

314314
tooling::addTargetAndModeForProgramName(Cmd, Cmd.front());
315315

316-
// Check whether the flag exists, either as -flag or -flag=*
317-
auto Has = [&](llvm::StringRef Flag) {
318-
for (llvm::StringRef Arg : Cmd) {
319-
if (Arg.consume_front(Flag) && (Arg.empty() || Arg[0] == '='))
320-
return true;
321-
}
322-
return false;
316+
// Check whether the flag exists in the command.
317+
auto HasExact = [&](llvm::StringRef Flag) {
318+
return llvm::any_of(Cmd, [&](llvm::StringRef Arg) { return Arg == Flag; });
319+
};
320+
321+
// Check whether the flag appears in the command as a prefix.
322+
auto HasPrefix = [&](llvm::StringRef Flag) {
323+
return llvm::any_of(
324+
Cmd, [&](llvm::StringRef Arg) { return Arg.startswith(Flag); });
323325
};
324326

325327
llvm::erase_if(Cmd, [](llvm::StringRef Elem) {
326328
return Elem.starts_with("--save-temps") || Elem.starts_with("-save-temps");
327329
});
328330

329331
std::vector<std::string> ToAppend;
330-
if (ResourceDir && !Has("-resource-dir"))
332+
if (ResourceDir && !HasExact("-resource-dir") && !HasPrefix("-resource-dir="))
331333
ToAppend.push_back(("-resource-dir=" + *ResourceDir));
332334

333335
// Don't set `-isysroot` if it is already set or if `--sysroot` is set.
334336
// `--sysroot` is a superset of the `-isysroot` argument.
335-
if (Sysroot && !Has("-isysroot") && !Has("--sysroot")) {
337+
if (Sysroot && !HasPrefix("-isysroot") && !HasExact("--sysroot") &&
338+
!HasPrefix("--sysroot=")) {
336339
ToAppend.push_back("-isysroot");
337340
ToAppend.push_back(*Sysroot);
338341
}
@@ -343,7 +346,7 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,
343346
}
344347

345348
if (!Cmd.empty()) {
346-
bool FollowSymlink = !Has("-no-canonical-prefixes");
349+
bool FollowSymlink = !HasExact("-no-canonical-prefixes");
347350
Cmd.front() =
348351
(FollowSymlink ? ResolvedDrivers : ResolvedDriversNoFollow)
349352
.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
@@ -370,9 +370,8 @@ TEST(ArgStripperTest, OrderDependent) {
370370
}
371371

372372
TEST(PrintArgvTest, All) {
373-
std::vector<llvm::StringRef> Args = {
374-
"one", "two", "thr ee", "f\"o\"ur", "fi\\ve", "$"
375-
};
373+
std::vector<llvm::StringRef> Args = {"one", "two", "thr ee",
374+
"f\"o\"ur", "fi\\ve", "$"};
376375
const char *Expected = R"(one two "thr ee" "f\"o\"ur" "fi\\ve" $)";
377376
EXPECT_EQ(Expected, printArgv(Args));
378377
}
@@ -450,6 +449,82 @@ TEST(CommandMangler, PathsAsPositional) {
450449
Mangler(Cmd, "a.cc");
451450
EXPECT_THAT(Cmd.CommandLine, Contains("foo"));
452451
}
452+
453+
TEST(CommandMangler, RespectsOriginalResourceDir) {
454+
auto Mangler = CommandMangler::forTests();
455+
Mangler.ResourceDir = testPath("fake/resources");
456+
457+
{
458+
tooling::CompileCommand Cmd;
459+
Cmd.CommandLine = {"clang++", "-resource-dir", testPath("true/resources"),
460+
"foo.cc"};
461+
Mangler(Cmd, "foo.cc");
462+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
463+
HasSubstr("-resource-dir " + testPath("true/resources")));
464+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
465+
Not(HasSubstr(testPath("fake/resources"))));
466+
}
467+
468+
{
469+
tooling::CompileCommand Cmd;
470+
Cmd.CommandLine = {"clang++", "-resource-dir=" + testPath("true/resources"),
471+
"foo.cc"};
472+
Mangler(Cmd, "foo.cc");
473+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
474+
HasSubstr("-resource-dir=" + testPath("true/resources")));
475+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
476+
Not(HasSubstr(testPath("fake/resources"))));
477+
}
478+
}
479+
480+
TEST(CommandMangler, RespectsOriginalSysroot) {
481+
auto Mangler = CommandMangler::forTests();
482+
Mangler.Sysroot = testPath("fake/sysroot");
483+
484+
{
485+
tooling::CompileCommand Cmd;
486+
Cmd.CommandLine = {"clang++", "-isysroot", testPath("true/sysroot"),
487+
"foo.cc"};
488+
Mangler(Cmd, "foo.cc");
489+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
490+
HasSubstr("-isysroot " + testPath("true/sysroot")));
491+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
492+
Not(HasSubstr(testPath("fake/sysroot"))));
493+
}
494+
495+
{
496+
tooling::CompileCommand Cmd;
497+
Cmd.CommandLine = {"clang++", "-isysroot" + testPath("true/sysroot"),
498+
"foo.cc"};
499+
Mangler(Cmd, "foo.cc");
500+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
501+
HasSubstr("-isysroot" + testPath("true/sysroot")));
502+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
503+
Not(HasSubstr(testPath("fake/sysroot"))));
504+
}
505+
506+
{
507+
tooling::CompileCommand Cmd;
508+
Cmd.CommandLine = {"clang++", "--sysroot", testPath("true/sysroot"),
509+
"foo.cc"};
510+
Mangler(Cmd, "foo.cc");
511+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
512+
HasSubstr("--sysroot " + testPath("true/sysroot")));
513+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
514+
Not(HasSubstr(testPath("fake/sysroot"))));
515+
}
516+
517+
{
518+
tooling::CompileCommand Cmd;
519+
Cmd.CommandLine = {"clang++", "--sysroot=" + testPath("true/sysroot"),
520+
"foo.cc"};
521+
Mangler(Cmd, "foo.cc");
522+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
523+
HasSubstr("--sysroot=" + testPath("true/sysroot")));
524+
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
525+
Not(HasSubstr(testPath("fake/sysroot"))));
526+
}
527+
}
453528
} // namespace
454529
} // namespace clangd
455530
} // namespace clang

0 commit comments

Comments
 (0)