[Lldb-commits] [lldb] 09ad100 - [lldb][NativePDB] Use decl context from a method's class (#199221)

via lldb-commits lldb-commits at lists.llvm.org
Thu May 28 11:51:30 PDT 2026


Author: Nerixyz
Date: 2026-05-28T20:51:24+02:00
New Revision: 09ad10052903ad38aa770642ae5c6a7115c92e5e

URL: https://github.com/llvm/llvm-project/commit/09ad10052903ad38aa770642ae5c6a7115c92e5e
DIFF: https://github.com/llvm/llvm-project/commit/09ad10052903ad38aa770642ae5c6a7115c92e5e.diff

LOG: [lldb][NativePDB] Use decl context from a method's class (#199221)

When we look up the parent decl context for a method, we used to look
for that by name. For "regular" types that works well.
Say, we have a `struct ns::Foo` and `ns::Foo::bar`. We'd search for
`ns::Foo` and use its context.

For special types - mainly lambdas - this doesn't work. They can't be
found by looking for the name we get from the function symbol. That name
won't be in the TPI hash table we use.

I'm not fully sure under which name they're in that table. For example,
if we get the name `Foo::fun::<lambda_1>::operator()` from the function
symbol, we'd look for `Foo::fun::<lambda_1>`. This fails. A class with
this name is actually present, but it has a different unique name. Its
unique name demangles to `` `public: void __cdecl
Foo::fun(void)'::`1'::<lambda_1> `` which doesn't match what we searched
for. That might be the issue.

If we can't find a class/struct, we create namespaces. So before, we
created namespaces for each segment and added a regular function inside.
This was wrong for methods.

However, we can tell that a function is a method by looking at its
function type. And we know the parent class from this type. With this PR
that parent class is used if it's available.

Added: 
    lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas-msvc.cpp
    lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas.cpp

Modified: 
    lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
    lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.h
    lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
    lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
index e70e69f5f697d..83b937f92eeec 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
@@ -360,6 +360,73 @@ PdbAstBuilderClang::CreateDeclInfoForUndecoratedName(llvm::StringRef name) {
   return {context, std::string(uname)};
 }
 
+clang::DeclContext *
+PdbAstBuilderClang::GetOrCreateDeclContextForCompilandSymbol(
+    PdbCompilandSymId uid) {
+  SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>(
+      m_clang.GetSymbolFile()->GetBackingSymbolFile());
+  PdbIndex &index = pdb->GetIndex();
+  CVSymbol sym = index.ReadSymbolRecord(uid);
+
+  llvm::StringRef symbol_name = getSymbolName(sym);
+
+  std::optional<PdbTypeSymId> func_id = GetFunctionType(sym);
+  if (!func_id || !symbol_name.contains("::"))
+    return CreateDeclInfoForUndecoratedName(symbol_name).first;
+
+  // Try to get the context from class type of an LF_MFUNCTION.
+  // For some types, we might not find a class type.
+  auto get_member_fn_context = [&]() -> clang::DeclContext * {
+    TypeIndex id = func_id->index;
+
+    if (func_id->is_ipi) {
+      // Type from IPI, for example from S_INLINESITE
+      std::optional<CVType> func_id_type =
+          index.ipi().tryGetType(func_id->index);
+      if (!func_id_type || func_id_type->kind() != LF_MFUNC_ID)
+        return nullptr;
+
+      MemberFuncIdRecord record;
+      llvm::Error err = TypeDeserializer::deserializeAs<MemberFuncIdRecord>(
+          *func_id_type, record);
+      if (err) {
+        llvm::consumeError(std::move(err));
+        return nullptr;
+      }
+
+      id = record.FunctionType;
+    }
+
+    std::optional<CVType> func_type = index.tpi().tryGetType(id);
+    if (!func_type || func_type->kind() != LF_MFUNCTION)
+      return nullptr;
+
+    MemberFunctionRecord mfr(TypeRecordKind::MemberFunction);
+
+    llvm::Error err =
+        TypeDeserializer::deserializeAs<MemberFunctionRecord>(*func_type, mfr);
+    if (err || mfr.ClassType.isNoneType()) {
+      llvm::consumeError(std::move(err));
+      return nullptr;
+    }
+
+    clang::QualType qt = GetOrCreateClangType(mfr.ClassType);
+    if (qt.isNull())
+      return nullptr;
+    clang::TagDecl *tag = qt->getAsTagDecl();
+    if (!tag)
+      return nullptr;
+
+    return clang::TagDecl::castToDeclContext(tag);
+  };
+
+  clang::DeclContext *context = get_member_fn_context();
+  if (!context)
+    return CreateDeclInfoForUndecoratedName(symbol_name).first;
+
+  return context;
+}
+
 clang::DeclContext *
 PdbAstBuilderClang::GetParentClangDeclContext(PdbSymUid uid) {
   // We must do this *without* calling GetOrCreate on the current uid, as
@@ -374,8 +441,7 @@ PdbAstBuilderClang::GetParentClangDeclContext(PdbSymUid uid) {
     if (scope)
       return GetOrCreateClangDeclContextForUid(*scope);
 
-    CVSymbol sym = index.ReadSymbolRecord(uid.asCompilandSym());
-    return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first;
+    return GetOrCreateDeclContextForCompilandSymbol(uid.asCompilandSym());
   }
   case PdbSymUidKind::Type: {
     // It could be a namespace, class, or global.  We don't support nested
@@ -1086,8 +1152,15 @@ PdbAstBuilderClang::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) {
   CompilerType func_ct = ToCompilerType(qt);
 
   llvm::StringRef proc_name = proc.Name;
-  proc_name.consume_front(context_name);
-  proc_name.consume_front("::");
+  if (!context_name.empty() && !(proc_name.consume_front(context_name) &&
+                                 proc_name.consume_front("::"))) {
+    // If we have some context, but the function name doesn't start with it, use
+    // the basename.
+    MSVCUndecoratedNameParser parser(proc.Name);
+    llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs(parser.GetSpecifiers());
+    if (!specs.empty())
+      proc_name = specs.back().GetBaseName();
+  }
   clang::FunctionDecl *function_decl =
       CreateFunctionDecl(func_id, proc_name, proc.FunctionType, func_ct,
                          func_type->getNumParams(), storage, false, parent);

diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.h
index fb338e5f8244e..0d214cb57f820 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.h
@@ -150,6 +150,10 @@ class PdbAstBuilderClang : public PdbAstBuilder {
   CreateDeclInfoForType(const llvm::codeview::TagRecord &record, TypeIndex ti);
   std::pair<clang::DeclContext *, std::string>
   CreateDeclInfoForUndecoratedName(llvm::StringRef uname);
+
+  clang::DeclContext *
+  GetOrCreateDeclContextForCompilandSymbol(PdbCompilandSymId uid);
+
   clang::QualType CreateSimpleType(TypeIndex ti);
 
   TypeSystemClang &m_clang;

diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
index 5860d90eca689..bd6b19eea6ff3 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -1194,3 +1194,29 @@ size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id,
   }
   return 0;
 }
+
+std::optional<PdbTypeSymId>
+lldb_private::npdb::GetFunctionType(llvm::codeview::CVSymbol symbol) {
+  switch (symbol.kind()) {
+  case SymbolKind::S_GPROC32:
+  case SymbolKind::S_LPROC32:
+  case SymbolKind::S_LPROC32_ID:
+  case SymbolKind::S_GPROC32_ID: {
+    ProcSym reg(SymbolRecordKind::ProcSym);
+    llvm::Error err = SymbolDeserializer::deserializeAs<ProcSym>(symbol, reg);
+    if (err)
+      return std::nullopt;
+    return PdbTypeSymId(reg.FunctionType, /*is_ipi=*/false);
+  }
+  case SymbolKind::S_INLINESITE: {
+    InlineSiteSym reg(SymbolRecordKind::InlineesSym);
+    llvm::Error err =
+        SymbolDeserializer::deserializeAs<InlineSiteSym>(symbol, reg);
+    if (err)
+      return std::nullopt;
+    return PdbTypeSymId(reg.Inlinee, /*is_ipi=*/true);
+  }
+  default:
+    return std::nullopt;
+  }
+}

diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
index 36e075b04f26f..bfe82db208b91 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
@@ -161,6 +161,8 @@ PdbTypeSymId GetBestPossibleDecl(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
 
 size_t GetSizeOfType(PdbTypeSymId id, llvm::pdb::TpiStream &tpi);
 
+std::optional<PdbTypeSymId> GetFunctionType(llvm::codeview::CVSymbol symbol);
+
 } // namespace npdb
 } // namespace lldb_private
 

diff  --git a/lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas-msvc.cpp b/lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas-msvc.cpp
new file mode 100644
index 0000000000000..d38cdcf22f4df
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas-msvc.cpp
@@ -0,0 +1,44 @@
+// clang-format off
+// REQUIRES: msvc
+
+// RUN: %build --compiler=msvc --nodefaultlib --std c++20 -o %t.exe -- %s
+// RUN: lldb-test symbols --dump-ast %t.exe | FileCheck %s
+
+class Foo {
+public:
+  void fun() {
+    auto f = [this]() {
+      int c = a;
+      int d = b;
+      return a + b;
+    };
+    f();
+    int local = 42;
+    auto g = [=]() mutable {
+      return local + 1;
+    };
+    g();
+  }
+
+private:
+  int a = 1;
+  int b = 2;
+};
+
+int main() {
+  Foo f;
+  f.fun();
+  return 0;
+}
+
+// CHECK:      namespace `public: void __cdecl Foo::fun(void)'::`{{.*}}' {
+// CHECK-NEXT:     class <lambda_1> {
+// CHECK:              int operator()() const;
+// CHECK:              Foo *__this;
+// CHECK-NEXT:     };
+// CHECK-NEXT:     class <lambda_2> {
+// CHECK:              int operator()();
+// CHECK:              int local;
+// CHECK-NEXT:     };
+// CHECK-NEXT: }
+

diff  --git a/lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas.cpp b/lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas.cpp
new file mode 100644
index 0000000000000..78f9856f44559
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/ast-lambdas.cpp
@@ -0,0 +1,45 @@
+// clang-format off
+// REQUIRES: lld, x86
+
+// RUN: %clang_cl --target=x86_64-windows-msvc -Od -GS- -std:c++20 -Z7 -c /Fo%t.obj -- %s
+// RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb
+// RUN: lldb-test symbols --dump-ast %t.exe | FileCheck %s
+
+class Foo {
+public:
+  void fun() {
+    auto f = [this]() {
+      int c = a;
+      int d = b;
+      return a + b;
+    };
+    f();
+    int local = 42;
+    auto g = [=]() mutable {
+      return local + 1;
+    };
+    g();
+  }
+
+private:
+  int a = 1;
+  int b = 2;
+};
+
+int main() {
+  Foo f;
+  f.fun();
+  return 0;
+}
+
+// CHECK:      namespace `public: void __cdecl Foo::fun(void)'::`1' {
+// CHECK-NEXT:     class <lambda_1> {
+// CHECK-NEXT:         int operator()() const;
+// CHECK-NEXT:         Foo *__this;
+// CHECK-NEXT:     };
+// CHECK-NEXT:     class <lambda_2> {
+// CHECK-NEXT:         int operator()();
+// CHECK-NEXT:         int local;
+// CHECK-NEXT:     };
+// CHECK-NEXT: }
+


        


More information about the lldb-commits mailing list