Skip to content

[clangd] Fix sysroot flag handling in CommandMangler to prevent duplicates #75694

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 12, 2024

Conversation

kon72
Copy link
Contributor

@kon72 kon72 commented Dec 16, 2023

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.

@llvmbot llvmbot added the clangd label Dec 16, 2023
@llvmbot
Copy link
Member

llvmbot commented Dec 16, 2023

@llvm/pr-subscribers-clangd

Author: Kon (kon72)

Changes

In CommandMangler, it guesses sysroot path of the host system and adds that through -isysroot flag only when there is no --sysroot or -isysroot flag beforehand.
Previously, it could not find -isysroot&lt;sysroot&gt; flag in the original compile command, resulting in duplicate -isysroot flags.
This commit fixes it and ensures the explicitly specified sysroot is respected. Also adds unit tests for this fix.


Full diff: https://github.com/llvm/llvm-project/pull/75694.diff

2 Files Affected:

  • (modified) clang-tools-extra/clangd/CompileCommands.cpp (+13-10)
  • (modified) clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp (+78-3)
diff --git a/clang-tools-extra/clangd/CompileCommands.cpp b/clang-tools-extra/clangd/CompileCommands.cpp
index f43ce928463b90..a3c6f3f41118e4 100644
--- a/clang-tools-extra/clangd/CompileCommands.cpp
+++ b/clang-tools-extra/clangd/CompileCommands.cpp
@@ -328,13 +328,15 @@ 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) {
@@ -342,12 +344,13 @@ void CommandMangler::operator()(tooling::CompileCommand &Command,
   });
 
   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);
   }
@@ -358,7 +361,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] {
diff --git a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
index 28f0d85d332caa..cad135923f71c1 100644
--- a/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CompileCommandsTests.cpp
@@ -385,9 +385,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));
 }
@@ -465,6 +464,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

…cates

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.
@kon72 kon72 force-pushed the clangd-respect-isysroot branch from 1a6a38f to 5a295dd Compare December 25, 2023 16:14
@kon72 kon72 changed the title [clangd] Ensure -isysroot in the original command is respected [clangd] Fix sysroot flag handling in CommandMangler to prevent duplicates Dec 25, 2023
@kon72
Copy link
Contributor Author

kon72 commented Dec 28, 2023

Hello @HighCommander4 ,

Could you take a look at this change? Thanks!

Copy link
Collaborator

@HighCommander4 HighCommander4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM!

@kon72
Copy link
Contributor Author

kon72 commented Jan 12, 2024

Hello @HighCommander4,

Thank you for the review.
Since I don't have permission to commit the change, could you merge this on my behalf?

@HighCommander4 HighCommander4 merged commit f489fb3 into llvm:main Jan 12, 2024
@HighCommander4
Copy link
Collaborator

Thank you for the review. Since I don't have permission to commit the change, could you merge this on my behalf?

Sure, done.

justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
…cates (llvm#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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants