[Lldb-commits] [lldb] [lldb][SymbolFileDWARF] Fall back to using parent DW_AT_LLVM_include_path for submodules (PR #142044)

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Thu May 29 14:50:13 PDT 2025


https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/142044

Inferred submodule declarations are emitted in DWARF as `DW_TAG_module`s without `DW_AT_LLVM_include_path`s. Instead the parent DIE will have the include path. This patch adds support for such setups. Without this, the `ClangModulesDeclVendor` would fail to `AddModule` the submodules (i.e., compile and load the submodules). This would cause errors such as:
```
note: error: Header search couldn't locate module 'TopLevel'
```

The test added here also tests https://github.com/llvm/llvm-project/pull/141220.

Unfortunately the embedded clang instance doesn't allow us to use the decls we find in the modules. But we'll try to fix that in a separate patch.

rdar://151022173

>From 9dd3f1b15c704d3d8ca4a09d6fd56770e2737881 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 29 May 2025 09:16:02 +0100
Subject: [PATCH] [lldb][SymbolFileDWARF] Fall back to using parent
 DW_AT_LLVM_include_path for submodules

Inferred submodule declarations are emitted in DWARF as `DW_TAG_module`s
without `DW_AT_LLVM_include_path`s. Instead the parent DIE will have the
include path. This patch adds support for such setups. Without this, the
`ClangModulesDeclVendor` would fail to `AddModule` the submodules (i.e.,
compile and load the submodules). This would cause errors such as:
```
note: error: Header search couldn't locate module 'TopLevel'
```

The test added here also tests https://github.com/llvm/llvm-project/pull/141220.

Unfortunately the embedded clang instance doesn't allow us to use the decls we find in the modules. But we'll try to fix that in a separate patch.
---
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      | 12 +++++++--
 .../API/lang/cpp/decl-from-submodule/Makefile |  4 +++
 .../TestDeclFromSubmodule.py                  | 25 +++++++++++++++++++
 .../decl-from-submodule/TopLevel/module1.h    |  2 ++
 .../decl-from-submodule/TopLevel/module2.h    |  2 ++
 .../API/lang/cpp/decl-from-submodule/main.cpp |  9 +++++++
 .../cpp/decl-from-submodule/module.modulemap  |  6 +++++
 7 files changed, 58 insertions(+), 2 deletions(-)
 create mode 100644 lldb/test/API/lang/cpp/decl-from-submodule/Makefile
 create mode 100644 lldb/test/API/lang/cpp/decl-from-submodule/TestDeclFromSubmodule.py
 create mode 100644 lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module1.h
 create mode 100644 lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module2.h
 create mode 100644 lldb/test/API/lang/cpp/decl-from-submodule/main.cpp
 create mode 100644 lldb/test/API/lang/cpp/decl-from-submodule/module.modulemap

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index d2049161fb5ad..24f905a023317 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -1188,6 +1188,8 @@ bool SymbolFileDWARF::ParseImportedModules(
       SourceModule module;
       module.path.push_back(ConstString(name));
 
+      const char *include_path = module_die.GetAttributeValueAsString(
+          DW_AT_LLVM_include_path, nullptr);
       DWARFDIE parent_die = module_die;
       while ((parent_die = parent_die.GetParent())) {
         if (parent_die.Tag() != DW_TAG_module)
@@ -1195,10 +1197,16 @@ bool SymbolFileDWARF::ParseImportedModules(
         if (const char *name =
                 parent_die.GetAttributeValueAsString(DW_AT_name, nullptr))
           module.path.push_back(ConstString(name));
+
+        // Inferred submodule declarations may not have a
+        // DW_AT_LLVM_include_path. Pick the parent (aka umbrella) module's
+        // include path instead.
+        if (!include_path)
+          include_path = parent_die.GetAttributeValueAsString(
+              DW_AT_LLVM_include_path, nullptr);
       }
       std::reverse(module.path.begin(), module.path.end());
-      if (const char *include_path = module_die.GetAttributeValueAsString(
-              DW_AT_LLVM_include_path, nullptr)) {
+      if (include_path) {
         FileSpec include_spec(include_path, dwarf_cu->GetPathStyle());
         MakeAbsoluteAndRemap(include_spec, *dwarf_cu,
                              m_objfile_sp->GetModule());
diff --git a/lldb/test/API/lang/cpp/decl-from-submodule/Makefile b/lldb/test/API/lang/cpp/decl-from-submodule/Makefile
new file mode 100644
index 0000000000000..e1e28fa0efebb
--- /dev/null
+++ b/lldb/test/API/lang/cpp/decl-from-submodule/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+CXXFLAGS_EXTRAS = $(MANDATORY_MODULE_BUILD_CFLAGS)
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/decl-from-submodule/TestDeclFromSubmodule.py b/lldb/test/API/lang/cpp/decl-from-submodule/TestDeclFromSubmodule.py
new file mode 100644
index 0000000000000..2819e59b1a1e5
--- /dev/null
+++ b/lldb/test/API/lang/cpp/decl-from-submodule/TestDeclFromSubmodule.py
@@ -0,0 +1,25 @@
+"""Test that decl lookup into submodules in C++ works as expected."""
+
+import lldb
+import shutil
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class DeclFromSubmoduleTestCase(TestBase):
+    def test_expr(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp"))
+
+        # FIXME: LLDB finds the decl for 'func' in the submodules correctly and hands it to Clang
+        # but Sema rejects using the decl during name lookup because it is not marked "Visible".
+        # However, this assertions still ensures that we at least don't fail to compile the
+        # submodule (which would cause other errors to appear before the expression error, hence
+        # we use "startstr").
+        self.expect(
+            "expr func(1, 2)",
+            error=True,
+            startstr="error: <user expression 0>:1:1: 'func' has unknown return type",
+        )
diff --git a/lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module1.h b/lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module1.h
new file mode 100644
index 0000000000000..38d9d120847c9
--- /dev/null
+++ b/lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module1.h
@@ -0,0 +1,2 @@
+// nodebug to force LLDB to find the decls in modules
+[[gnu::nodebug]] inline int func(int x) { return x; }
diff --git a/lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module2.h b/lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module2.h
new file mode 100644
index 0000000000000..440c76dd50c77
--- /dev/null
+++ b/lldb/test/API/lang/cpp/decl-from-submodule/TopLevel/module2.h
@@ -0,0 +1,2 @@
+// nodebug to force LLDB to find the decls in modules
+[[gnu::nodebug]] inline int func(int x, int y) { return x + y; }
diff --git a/lldb/test/API/lang/cpp/decl-from-submodule/main.cpp b/lldb/test/API/lang/cpp/decl-from-submodule/main.cpp
new file mode 100644
index 0000000000000..5e05b6980169a
--- /dev/null
+++ b/lldb/test/API/lang/cpp/decl-from-submodule/main.cpp
@@ -0,0 +1,9 @@
+#include "TopLevel/module1.h"
+#include "TopLevel/module2.h"
+
+int main() {
+  func(1);
+  func(2, 3);
+
+  return 0;
+}
diff --git a/lldb/test/API/lang/cpp/decl-from-submodule/module.modulemap b/lldb/test/API/lang/cpp/decl-from-submodule/module.modulemap
new file mode 100644
index 0000000000000..b00659f7efb67
--- /dev/null
+++ b/lldb/test/API/lang/cpp/decl-from-submodule/module.modulemap
@@ -0,0 +1,6 @@
+module TopLevel {
+  umbrella "TopLevel"
+  explicit module * {
+    export *
+  }
+}



More information about the lldb-commits mailing list