[clang-tools-extra] [clangd] Support `-specs` arguments when querying the driver. (PR #70285)

Chris Carlon via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 26 18:32:37 PDT 2023


https://github.com/cjc25 updated https://github.com/llvm/llvm-project/pull/70285

>From f171053854f3926641ecc450e35a625b5850a4be Mon Sep 17 00:00:00 2001
From: Chris Carlon <cjc25 at cjc25.com>
Date: Tue, 24 Oct 2023 22:21:59 -0400
Subject: [PATCH 1/2] [clangd] Support `-specs` arguments when querying the
 driver.

Similarly to commit 3935a29, forward spec file arguments to the driver if they
appear in the compile database. Spec files can affect the include search path.

fixes clangd/clangd#1410
---
 .../clangd/SystemIncludeExtractor.cpp         | 30 ++++++++++++++++---
 .../clangd/test/system-include-extractor.test |  9 ++++--
 2 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/clangd/SystemIncludeExtractor.cpp b/clang-tools-extra/clangd/SystemIncludeExtractor.cpp
index a86f152c3bf364e..ea98c7d948a2f6d 100644
--- a/clang-tools-extra/clangd/SystemIncludeExtractor.cpp
+++ b/clang-tools-extra/clangd/SystemIncludeExtractor.cpp
@@ -89,13 +89,14 @@ struct DriverArgs {
   std::string ISysroot;
   std::string Target;
   std::string Stdlib;
+  llvm::SmallVector<std::string> Specs;
 
   bool operator==(const DriverArgs &RHS) const {
     return std::tie(Driver, StandardIncludes, StandardCXXIncludes, Lang,
-                    Sysroot, ISysroot, Target, Stdlib) ==
+                    Sysroot, ISysroot, Target, Stdlib, Specs) ==
            std::tie(RHS.Driver, RHS.StandardIncludes, RHS.StandardCXXIncludes,
-                    RHS.Lang, RHS.Sysroot, RHS.ISysroot, RHS.Target,
-                    RHS.Stdlib);
+                    RHS.Lang, RHS.Sysroot, RHS.ISysroot, RHS.Target, RHS.Stdlib,
+                    RHS.Specs);
   }
 
   DriverArgs(const tooling::CompileCommand &Cmd, llvm::StringRef File) {
@@ -145,6 +146,17 @@ struct DriverArgs {
           Stdlib = Cmd.CommandLine[I + 1];
       } else if (Arg.consume_front("-stdlib=")) {
         Stdlib = Arg.str();
+      } else if (Arg.startswith("-specs=")) {
+        // clang requires a single token like `-specs=file` or `--specs=file`,
+        // but gcc will accept two tokens like `--specs file`. Since the
+        // compilation database is presumably correct, we just forward the flags
+        // as-is.
+        Specs.push_back(Arg.str());
+      } else if (Arg.startswith("--specs=")) {
+        Specs.push_back(Arg.str());
+      } else if (Arg == "--specs" && I + 1 < E) {
+        Specs.push_back(Arg.str());
+        Specs.push_back(Cmd.CommandLine[I + 1]);
       }
     }
 
@@ -186,6 +198,11 @@ struct DriverArgs {
       Args.append({"-target", Target});
     if (!Stdlib.empty())
       Args.append({"--stdlib", Stdlib});
+
+    for (llvm::StringRef Spec : Specs) {
+      Args.push_back(Spec);
+    }
+
     return Args;
   }
 
@@ -210,7 +227,7 @@ template <> struct DenseMapInfo<DriverArgs> {
     return Driver;
   }
   static unsigned getHashValue(const DriverArgs &Val) {
-    return llvm::hash_value(std::tuple{
+    unsigned FixedFieldsHash = llvm::hash_value(std::tuple{
         Val.Driver,
         Val.StandardIncludes,
         Val.StandardCXXIncludes,
@@ -220,6 +237,11 @@ template <> struct DenseMapInfo<DriverArgs> {
         Val.Target,
         Val.Stdlib,
     });
+
+    unsigned SpecsHash =
+        llvm::hash_combine_range(Val.Specs.begin(), Val.Specs.end());
+
+    return llvm::hash_combine(FixedFieldsHash, SpecsHash);
   }
   static bool isEqual(const DriverArgs &LHS, const DriverArgs &RHS) {
     return LHS == RHS;
diff --git a/clang-tools-extra/clangd/test/system-include-extractor.test b/clang-tools-extra/clangd/test/system-include-extractor.test
index cbb3018b2fa7349..4ff6f946ffa73d4 100644
--- a/clang-tools-extra/clangd/test/system-include-extractor.test
+++ b/clang-tools-extra/clangd/test/system-include-extractor.test
@@ -19,6 +19,11 @@
 # RUN: echo '[ -z "${args##*"-isysroot /isysroot"*}" ] || exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ -z "${args##*"-target arm-linux-gnueabihf"*}" ] || exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ -z "${args##*"--stdlib libc++"*}" ] || exit' >> %t.dir/bin/my_driver.sh
+# RUN: echo '[ -z "${args##*"-specs=test.spec"*}" ] || exit' >> %t.dir/bin/my_driver.sh
+# RUN: echo '[ -z "${args##*"--specs=test2.spec"*}" ] || exit' >> %t.dir/bin/my_driver.sh
+# RUN: echo '[ -z "${args##*"--specs test3.spec"*}" ] || exit' >> %t.dir/bin/my_driver.sh
+# Check that clangd drops other flags like -lc++, which don't affect includes
+# RUN: echo '[ -n "${args##*"-lc++"*}" ] || exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo line to ignore >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'printf "Target: arm-linux-gnueabihf\r\n" >&2' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/bin/my_driver.sh
@@ -38,7 +43,7 @@
 
 # Generate a compile_commands.json that will query the mock driver we've
 # created. Which should add a.h and b.h into include search path.
-# RUN: echo '[{"directory": "%/t.dir", "command": "my_driver.sh the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
+# RUN: echo '[{"directory": "%/t.dir", "command": "my_driver.sh the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++ -lc++ -specs=test. --specs=test2.spec --specs test3.spec", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
 
 # RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1
 # On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..."
@@ -76,7 +81,7 @@
 {"jsonrpc":"2.0","method":"exit"}
 
 # Generate a different compile_commands.json which does not point to the mock driver
-# RUN: echo '[{"directory": "%/t.dir", "command": "gcc the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
+# RUN: echo '[{"directory": "%/t.dir", "command": "gcc the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++ -lc++ -specs=test. --specs=test2.spec --specs test3.spec", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
 
 # Generate a clangd config file which points to the mock driver instead
 # RUN: echo 'CompileFlags:' > %t.dir/.clangd

>From a29fb6071c7139aec55f11d4fe08301bd3e2b522 Mon Sep 17 00:00:00 2001
From: Chris Carlon <cjc25 at cjc25.com>
Date: Thu, 26 Oct 2023 21:32:17 -0400
Subject: [PATCH 2/2] fixup! [clangd] Support `-specs` arguments when querying
 the driver.

---
 clang-tools-extra/clangd/test/system-include-extractor.test | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/clangd/test/system-include-extractor.test b/clang-tools-extra/clangd/test/system-include-extractor.test
index 4ff6f946ffa73d4..1d4aeeb0067c564 100644
--- a/clang-tools-extra/clangd/test/system-include-extractor.test
+++ b/clang-tools-extra/clangd/test/system-include-extractor.test
@@ -21,7 +21,6 @@
 # RUN: echo '[ -z "${args##*"--stdlib libc++"*}" ] || exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ -z "${args##*"-specs=test.spec"*}" ] || exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo '[ -z "${args##*"--specs=test2.spec"*}" ] || exit' >> %t.dir/bin/my_driver.sh
-# RUN: echo '[ -z "${args##*"--specs test3.spec"*}" ] || exit' >> %t.dir/bin/my_driver.sh
 # Check that clangd drops other flags like -lc++, which don't affect includes
 # RUN: echo '[ -n "${args##*"-lc++"*}" ] || exit' >> %t.dir/bin/my_driver.sh
 # RUN: echo 'echo line to ignore >&2' >> %t.dir/bin/my_driver.sh
@@ -43,7 +42,7 @@
 
 # Generate a compile_commands.json that will query the mock driver we've
 # created. Which should add a.h and b.h into include search path.
-# RUN: echo '[{"directory": "%/t.dir", "command": "my_driver.sh the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++ -lc++ -specs=test. --specs=test2.spec --specs test3.spec", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
+# RUN: echo '[{"directory": "%/t.dir", "command": "my_driver.sh the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++ -lc++ -specs=test. --specs=test2.spec", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
 
 # RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1
 # On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..."
@@ -81,7 +80,7 @@
 {"jsonrpc":"2.0","method":"exit"}
 
 # Generate a different compile_commands.json which does not point to the mock driver
-# RUN: echo '[{"directory": "%/t.dir", "command": "gcc the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++ -lc++ -specs=test. --specs=test2.spec --specs test3.spec", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
+# RUN: echo '[{"directory": "%/t.dir", "command": "gcc the-file.cpp --target=arm-linux-gnueabihf -nostdinc --sysroot /my/sysroot/path -isysroot/isysroot -stdlib=libc++ -lc++ -specs=test. --specs=test2.spec", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json
 
 # Generate a clangd config file which points to the mock driver instead
 # RUN: echo 'CompileFlags:' > %t.dir/.clangd



More information about the cfe-commits mailing list