Skip to content

Commit

Permalink
[clangd] Fix sysroot flag handling in CommandMangler to prevent dupli…
Browse files Browse the repository at this point in the history
…cates (#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.
  • Loading branch information
kon72 committed Jan 12, 2024
1 parent c297597 commit f489fb3
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 13 deletions.
23 changes: 13 additions & 10 deletions clang-tools-extra/clangd/CompileCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,26 +313,29 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,

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

// Check whether the flag exists, either as -flag or -flag=*
auto Has = [&](llvm::StringRef Flag) {
for (llvm::StringRef Arg : Cmd) {
if (Arg.consume_front(Flag) && (Arg.empty() || Arg[0] == '='))
return true;
}
return false;
// Check whether the flag exists in the command.
auto HasExact = [&](llvm::StringRef Flag) {
return llvm::any_of(Cmd, [&](llvm::StringRef Arg) { return Arg == Flag; });
};

// Check whether the flag appears in the command as a prefix.
auto HasPrefix = [&](llvm::StringRef Flag) {
return llvm::any_of(
Cmd, [&](llvm::StringRef Arg) { return Arg.startswith(Flag); });
};

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

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

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

if (!Cmd.empty()) {
bool FollowSymlink = !Has("-no-canonical-prefixes");
bool FollowSymlink = !HasExact("-no-canonical-prefixes");
Cmd.front() =
(FollowSymlink ? ResolvedDrivers : ResolvedDriversNoFollow)
.get(Cmd.front(), [&, this] {
Expand Down
81 changes: 78 additions & 3 deletions clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,8 @@ TEST(ArgStripperTest, OrderDependent) {
}

TEST(PrintArgvTest, All) {
std::vector<llvm::StringRef> Args = {
"one", "two", "thr ee", "f\"o\"ur", "fi\\ve", "$"
};
std::vector<llvm::StringRef> Args = {"one", "two", "thr ee",
"f\"o\"ur", "fi\\ve", "$"};
const char *Expected = R"(one two "thr ee" "f\"o\"ur" "fi\\ve" $)";
EXPECT_EQ(Expected, printArgv(Args));
}
Expand Down Expand Up @@ -450,6 +449,82 @@ TEST(CommandMangler, PathsAsPositional) {
Mangler(Cmd, "a.cc");
EXPECT_THAT(Cmd.CommandLine, Contains("foo"));
}

TEST(CommandMangler, RespectsOriginalResourceDir) {
auto Mangler = CommandMangler::forTests();
Mangler.ResourceDir = testPath("fake/resources");

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-resource-dir", testPath("true/resources"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-resource-dir " + testPath("true/resources")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/resources"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-resource-dir=" + testPath("true/resources"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-resource-dir=" + testPath("true/resources")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/resources"))));
}
}

TEST(CommandMangler, RespectsOriginalSysroot) {
auto Mangler = CommandMangler::forTests();
Mangler.Sysroot = testPath("fake/sysroot");

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-isysroot", testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-isysroot " + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "-isysroot" + testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("-isysroot" + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "--sysroot", testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("--sysroot " + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}

{
tooling::CompileCommand Cmd;
Cmd.CommandLine = {"clang++", "--sysroot=" + testPath("true/sysroot"),
"foo.cc"};
Mangler(Cmd, "foo.cc");
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
HasSubstr("--sysroot=" + testPath("true/sysroot")));
EXPECT_THAT(llvm::join(Cmd.CommandLine, " "),
Not(HasSubstr(testPath("fake/sysroot"))));
}
}
} // namespace
} // namespace clangd
} // namespace clang

0 comments on commit f489fb3

Please sign in to comment.