https://github.com/vbvictor created 
https://github.com/llvm/llvm-project/pull/198152

Previously, The "Switch Between Source/Header" action picked `.h` over `.hpp` 
when both files existed next to a `.cpp` file, because `.h` is listed first in 
the header-extension list. 

This patch reorders `HeaderExtensions` and `SourceExtensions` so the 
`C++`-flavored extensions come before `.h` and `.c`. `C++`-flavor of file is 
preffered since (at least in my understanding) more people using `clangd` for 
`C++` than for `C` so switching from `.cpp` should go into `.hpp`, not `.h`.

This brings an edje case that when swithing from `.c` it will go into `.hpp` 
instead of `.h`, but I think this situation is more rare than having `.cpp` and 
`.hpp` and `.h` combination since `.h` headers can be used as `extern "C"` 
wrapper of cpp library.

Ideally, we should support a mapping between Header and Source (like `c <-> 
.h`, `.cxx, .cc <-> .hpp, hxx`, but it takes 50-100 lines of additional code 
and I don't think we should bring this complexity now.

>From cfc337fde41d36326c5bdf19f5727259174a5f5c Mon Sep 17 00:00:00 2001
From: Victor Baranov <[email protected]>
Date: Sun, 17 May 2026 10:04:02 +0300
Subject: [PATCH] [clangd] Prefer .hpp files over .h with header source switch

---
 clang-tools-extra/clangd/HeaderSourceSwitch.cpp |  4 ++--
 .../unittests/HeaderSourceSwitchTests.cpp       | 17 +++++++++++++++++
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clangd/HeaderSourceSwitch.cpp 
b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
index ee4bea1401490..6c1e2ca99427e 100644
--- a/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
+++ b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
@@ -21,9 +21,9 @@ namespace clangd {
 std::optional<Path> getCorrespondingHeaderOrSource(
     PathRef OriginalFile, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) 
{
   static constexpr llvm::StringRef SourceExtensions[] = {
-      ".cpp", ".c", ".cc", ".cxx", ".c++", ".m", ".mm"};
+      ".cpp", ".cc", ".cxx", ".c++", ".c", ".m", ".mm"};
   static constexpr llvm::StringRef HeaderExtensions[] = {
-      ".h",    ".hh",  ".hpp",  ".hxx",  ".inc",
+      ".hpp",  ".hh",  ".hxx",  ".h++",  ".h",  ".inc",
       ".cppm", ".ccm", ".cxxm", ".c++m", ".ixx"};
 
   llvm::StringRef PathExt = llvm::sys::path::extension(OriginalFile);
diff --git a/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp 
b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp
index e600207de458a..782e3aacba538 100644
--- a/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp
@@ -74,6 +74,23 @@ TEST(HeaderSourceSwitchTest, FileHeuristic) {
   // string.
   PathResult = getCorrespondingHeaderOrSource(Invalid, FS.view(std::nullopt));
   EXPECT_FALSE(PathResult.has_value());
+
+  // Test when both .h and .hpp exist next to a .cpp file, prefer .hpp
+  auto FooHpp = testPath("foo.hpp");
+  FS.Files[FooHpp];
+  PathResult = getCorrespondingHeaderOrSource(FooCpp, FS.view(std::nullopt));
+  EXPECT_TRUE(PathResult.has_value());
+  ASSERT_EQ(*PathResult, FooHpp);
+
+  auto BarCc = testPath("bar2.cc");
+  auto BarH = testPath("bar2.h");
+  auto BarHh = testPath("bar2.hh");
+  FS.Files[BarCc];
+  FS.Files[BarH];
+  FS.Files[BarHh];
+  PathResult = getCorrespondingHeaderOrSource(BarCc, FS.view(std::nullopt));
+  EXPECT_TRUE(PathResult.has_value());
+  ASSERT_EQ(*PathResult, BarHh);
 }
 
 TEST(HeaderSourceSwitchTest, ModuleInterfaces) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to