https://github.com/DominiqueFuchs created 
https://github.com/llvm/llvm-project/pull/184560

Fixes https://github.com/clangd/clangd/issues/2613
Related: https://github.com/clangd/clangd/issues/1014

`InterpolatingCompilationDatabase::transferTo()` drops `-std=c++20` when the 
source is ObjC++ and the target is a C++ header (e.g. `.mm` → `.hh`), because 
`foldType()` treats them as different languages. But `-std` flags belong to 
standard families, not language modes — C/ObjC share C standards, 
C++/ObjC++/CUDA/HIP share C++ standards (see existing 
`CompilerInvocation::isStandardCompatible()` which groups `Language::CXX` and 
`Language::ObjCXX` together, for example).

Related clangd issues above show this confusing behavior in practice, where 
it's not only leading to the expected behavior (same language standard not 
preserved within the same set of files for different types) being broken, but 
depending on tooling setup makes it impossible to enforce this standard via 
.clangd config and/or compile_comands.json.

Given a compile_commands.json file entry like
```
[
  {
    "directory": "/test",
    "command": "clang++ -x objective-c++ -std=c++20 -c Test.mm",
    "file": "Test.mm"
  }
]
```
Before:
```
$ clangd --check=Test.hh
Compile command inferred from Test.mm: clang++ --driver-mode=g++ -c -- Test.hh
All checks completed, 5 errors
```
After:
```
$ clangd --check=Test.hh
Compile command inferred from Test.mm: clang++ --driver-mode=g++ -c -std=c++20 
-- Test.hh
```

>From 616555108bf6a266e58d026d63845b2f9a651a10 Mon Sep 17 00:00:00 2001
From: Dominique Fuchs <[email protected]>
Date: Wed, 4 Mar 2026 08:05:46 +0100
Subject: [PATCH] [clang][Tooling] Preserve -std flag across same-family
 transferance

transferTo() stripped the -std flag when inferring a header's
compile command from sources of a different but compatible language mode,
not only when changing language family.

This commit replaces the foldType() equality check with a standard-family 
comparison
using the existing types::isCXX() and types::isDerivedFromC() predicate.

Fixes https://github.com/clangd/clangd/issues/2613

Signed-off-by: Dominique Fuchs <[email protected]>
---
 .../Tooling/InterpolatingCompilationDatabase.cpp | 16 +++++++++++++---
 .../Tooling/CompilationDatabaseTest.cpp          | 14 ++++++++++++++
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp 
b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
index f8306a6ad6e90..3f71165547244 100644
--- a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
+++ b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
@@ -123,6 +123,14 @@ static types::ID foldType(types::ID Lang) {
   }
 }
 
+// Whether two types use the same -std flag family.
+// C and ObjC share C standards; C++, ObjC++, CUDA, HIP share C++ standards.
+static bool typesSameStandardFamily(types::ID T1, types::ID T2) {
+  if (!types::isDerivedFromC(T1) || !types::isDerivedFromC(T2))
+    return false;
+  return types::isCXX(T1) == types::isCXX(T2);
+}
+
 // Return the language standard that's activated by the /std:clatest
 // flag in clang-CL mode.
 static LangStandard::Kind latestLangStandardC() {
@@ -253,9 +261,11 @@ struct TransferableCommand {
       }
     }
 
-    // --std flag may only be transferred if the language is the same.
-    // We may consider "translating" these, e.g. c++11 -> c11.
-    if (Std != LangStandard::lang_unspecified && foldType(TargetType) == Type) 
{
+    // --std flag may only be transferred if the language families share
+    // compatible standards. C/ObjC share C standards; C++/ObjC++/CUDA/HIP
+    // share C++ standards.
+    if (Std != LangStandard::lang_unspecified && Type &&
+        typesSameStandardFamily(foldType(TargetType), *Type)) {
       const char *Spelling =
           LangStandard::getLangStandardForKind(Std).getName();
 
diff --git a/clang/unittests/Tooling/CompilationDatabaseTest.cpp 
b/clang/unittests/Tooling/CompilationDatabaseTest.cpp
index 2d68e0994c93e..0640a1e7ba9b1 100644
--- a/clang/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/clang/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -847,6 +847,20 @@ TEST_F(InterpolateTest, Language) {
             "clang -D dir/aux.cpp -x objective-c++-header -std=c++17");
 }
 
+TEST_F(InterpolateTest, StdTransferSameFamily) {
+  // ObjC++ → C++ header: same standard family, -std should survive.
+  add("dir/foo.mm", "-std=c++20");
+  EXPECT_EQ(getCommand("dir/foo.hh"), "clang -D dir/foo.mm -std=c++20");
+
+  // Explicit -x objective-c++ on .cpp → C++ header: same family.
+  add("dir/bar.cpp", "-x objective-c++ -std=c++20");
+  EXPECT_EQ(getCommand("dir/bar.hh"), "clang -D dir/bar.cpp -std=c++20");
+
+  // ObjC → C: same standard family, -std should survive.
+  add("dir/baz.m", "-std=c17");
+  EXPECT_EQ(getCommand("dir/baz.c"), "clang -D dir/baz.m -std=c17");
+}
+
 TEST_F(InterpolateTest, Strip) {
   add("dir/foo.cpp", "-o foo.o -Wall");
   // the -o option and the input file are removed, but -Wall is preserved.

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

Reply via email to