Author: nerix
Date: 2025-08-04T08:56:04+01:00
New Revision: d95dadff8f094e793b79eec57737ec397fad7724

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

LOG: [LLDB][NativePDB] Allow type lookup in namespaces (#149876)

Previously, `type lookup` for types in namespaces didn't work with the
native PDB plugin, because `FindTypes` would only look for types whose
base name was equal to their full name. PDB/CodeView does not store the
base names in the TPI stream, but the types have their full name (e.g.
`std::thread` instead of `thread`). So `findRecordsByName` would only
return types in the top level namespace.

This PR changes the lookup to go through all types and check their base
name. As that could be a bit expensive, the names are first cached
(similar to the function lookup in the DIA PDB plugin). Potential types
are checked with `TypeQuery::ContextMatches`.

To be able to handle anonymous namespaces, I changed
`TypeQuery::ContextMatches`. The [`TypeQuery`
constructor](https://github.com/llvm/llvm-project/blob/9ad7edef4276207ca4cefa6b39d11145f4145a72/lldb/source/Symbol/Type.cpp#L76-L79)
inserts all name components as `CompilerContextKind::AnyDeclContext`. To
skip over anonymous namespaces, `ContextMatches` checked if a component
was empty and exactly of kind `Namespace`. For our query, the last check
was always false, so we never skipped anonymous namespaces. DWARF
doesn't have this problem, as it [constructs the context
outside](https://github.com/llvm/llvm-project/blob/abe93d9d7e891a2a6596ddb0c6324280137c89dc/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp#L154-L160)
and has proper information about namespaces. I'm not fully sure if my
change is correct and that it doesn't break other users of `TypeQuery`.

This enables `type lookup <type>` to work on types in namespaces.
However, expressions don't work with this yet, because `FindNamespace`
is unimplemented for native PDB.

Added: 
    lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test

Modified: 
    lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
    lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
    lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
    lldb/source/Symbol/Type.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h 
b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
index 98b965c361a77..36e075b04f26f 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
@@ -72,6 +72,16 @@ struct CVTagRecord {
     return cvunion.Name;
   }
 
+  CompilerContextKind contextKind() const {
+    if (m_kind == Struct || m_kind == Class)
+      return CompilerContextKind::ClassOrStruct;
+    if (m_kind == Enum)
+      return CompilerContextKind::Enum;
+
+    assert(m_kind == Union);
+    return CompilerContextKind::Union;
+  }
+
 private:
   CVTagRecord(llvm::codeview::ClassRecord &&c);
   CVTagRecord(llvm::codeview::UnionRecord &&u);

diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp 
b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 20d8c1acf9c42..7af53e16ce9d5 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -1720,18 +1720,22 @@ void SymbolFileNativePDB::FindTypes(const 
lldb_private::TypeQuery &query,
 
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
 
-  std::vector<TypeIndex> matches =
-      m_index->tpi().findRecordsByName(query.GetTypeBasename().GetStringRef());
-
-  for (TypeIndex type_idx : matches) {
-    TypeSP type_sp = GetOrCreateType(type_idx);
-    if (!type_sp)
+  // We can't query for the full name because the type might reside
+  // in an anonymous namespace. Search for the basename in our map and check 
the
+  // matching types afterwards.
+  std::vector<uint32_t> matches;
+  m_type_base_names.GetValues(query.GetTypeBasename(), matches);
+
+  for (uint32_t match_idx : matches) {
+    std::vector context = GetContextForType(TypeIndex(match_idx));
+    if (context.empty())
       continue;
 
-    // We resolved a type. Get the fully qualified name to ensure it matches.
-    ConstString name = type_sp->GetQualifiedName();
-    TypeQuery type_match(name.GetStringRef(), TypeQueryOptions::e_exact_match);
-    if (query.ContextMatches(type_match.GetContextRef())) {
+    if (query.ContextMatches(context)) {
+      TypeSP type_sp = GetOrCreateType(TypeIndex(match_idx));
+      if (!type_sp)
+        continue;
+
       results.InsertUnique(type_sp);
       if (results.Done(query))
         return;
@@ -2201,11 +2205,15 @@ void SymbolFileNativePDB::BuildParentMap() {
     CVTagRecord tag = CVTagRecord::create(type);
 
     RecordIndices &indices = record_indices[tag.asTag().getUniqueName()];
-    if (tag.asTag().isForwardRef())
+    if (tag.asTag().isForwardRef()) {
       indices.forward = *ti;
-    else
+    } else {
       indices.full = *ti;
 
+      auto base_name = MSVCUndecoratedNameParser::DropScope(tag.name());
+      m_type_base_names.Append(ConstString(base_name), ti->getIndex());
+    }
+
     if (indices.full != TypeIndex::None() &&
         indices.forward != TypeIndex::None()) {
       forward_to_full[indices.forward] = indices.full;
@@ -2261,6 +2269,10 @@ void SymbolFileNativePDB::BuildParentMap() {
       llvm::consumeError(std::move(error));
   }
 
+  // After calling Append(), the type-name map needs to be sorted again to be
+  // able to look up a type by its name.
+  m_type_base_names.Sort();
+
   // Now that we know the forward -> full mapping of all type indices, we can
   // re-write all the indices.  At the end of this process, we want a mapping
   // consisting of fwd -> full and full -> full for all child -> parent 
indices.
@@ -2353,3 +2365,52 @@ 
SymbolFileNativePDB::GetParentType(llvm::codeview::TypeIndex ti) {
     return std::nullopt;
   return parent_iter->second;
 }
+
+std::vector<CompilerContext>
+SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
+  CVType type = m_index->tpi().getType(ti);
+  if (!IsTagRecord(type))
+    return {};
+
+  CVTagRecord tag = CVTagRecord::create(type);
+
+  std::optional<Type::ParsedName> parsed_name =
+      Type::GetTypeScopeAndBasename(tag.name());
+  if (!parsed_name)
+    return {{tag.contextKind(), ConstString(tag.name())}};
+
+  std::vector<CompilerContext> ctx;
+  // assume everything is a namespace at first
+  for (llvm::StringRef scope : parsed_name->scope) {
+    ctx.emplace_back(CompilerContextKind::Namespace, ConstString(scope));
+  }
+  // we know the kind of our own type
+  ctx.emplace_back(tag.contextKind(), ConstString(parsed_name->basename));
+
+  // try to find the kind of parents
+  for (auto &el : llvm::reverse(llvm::drop_end(ctx))) {
+    std::optional<TypeIndex> parent = GetParentType(ti);
+    if (!parent)
+      break;
+
+    ti = *parent;
+    type = m_index->tpi().getType(ti);
+    switch (type.kind()) {
+    case LF_CLASS:
+    case LF_STRUCTURE:
+    case LF_INTERFACE:
+      el.kind = CompilerContextKind::ClassOrStruct;
+      continue;
+    case LF_UNION:
+      el.kind = CompilerContextKind::Union;
+      continue;
+    case LF_ENUM:
+      el.kind = CompilerContextKind::Enum;
+      continue;
+    default:
+      break;
+    }
+    break;
+  }
+  return ctx;
+}

diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h 
b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index 9891313f11d0b..eda375d4cebe7 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -258,6 +258,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
 
   void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr);
 
+  std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
+
   llvm::BumpPtrAllocator m_allocator;
 
   lldb::addr_t m_obj_load_address = 0;
@@ -278,6 +280,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
   llvm::DenseMap<lldb::user_id_t, std::shared_ptr<InlineSite>> m_inline_sites;
   llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
       m_parent_types;
+
+  lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
 };
 
 } // namespace npdb

diff  --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp
index b7adae41b5190..952b2bdee1886 100644
--- a/lldb/source/Symbol/Type.cpp
+++ b/lldb/source/Symbol/Type.cpp
@@ -817,10 +817,12 @@ Type::GetTypeScopeAndBasename(llvm::StringRef name) {
     case ':':
       if (prev_is_colon && template_depth == 0) {
         llvm::StringRef scope_name = name.slice(name_begin, pos.index() - 1);
-        // The itanium demangler uses this string to represent anonymous
+        // The demanglers use these strings to represent anonymous
         // namespaces. Convert it to a more language-agnostic form (which is
         // also used in DWARF).
-        if (scope_name == "(anonymous namespace)")
+        if (scope_name == "(anonymous namespace)" ||
+            scope_name == "`anonymous namespace'" ||
+            scope_name == "`anonymous-namespace'")
           scope_name = "";
         result.scope.push_back(scope_name);
         name_begin = pos.index() + 1;

diff  --git a/lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test 
b/lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test
new file mode 100644
index 0000000000000..f6c1ccf22fa18
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test
@@ -0,0 +1,135 @@
+# REQUIRES: target-windows
+
+# Test namespace lookup.
+# RUN: split-file %s %t
+# RUN: %build --nodefaultlib -o %t.exe -- %t/main.cpp
+# RUN: %lldb -f %t.exe -s \
+# RUN:     %t/commands.input 2>&1 | FileCheck %s
+
+#--- main.cpp
+
+struct S {
+  char a[1];
+};
+
+namespace Outer {
+
+  struct S {
+    char a[2];
+  };
+
+  namespace Inner1 {
+    struct S {
+      char a[3];
+    };
+
+    namespace Inner2 {
+      struct S {
+        char a[4];
+      };
+    } // namespace Inner2
+  } // namespace Inner1
+
+  namespace Inner2 {
+    struct S {
+      char a[5];
+    };
+  } // namespace Inner2
+
+  namespace {
+    struct A {
+      char a[6];
+    };
+  } // namespace
+
+} // namespace Outer
+
+namespace {
+  struct A {
+    char a[7];
+  };
+} // namespace
+
+int main(int argc, char **argv) {
+  S s;
+  Outer::S os;
+  Outer::Inner1::S oi1s;
+  Outer::Inner1::Inner2::S oi1i2s;
+  Outer::Inner2::S oi2s;
+  A a1;
+  Outer::A a2;
+  return sizeof(s) + sizeof(os) + sizeof(oi1s) + sizeof(oi1i2s) + sizeof(oi2s) 
+ sizeof(a1) + sizeof(a2);
+}
+
+#--- commands.input
+
+b main
+r
+
+type lookup S
+type lookup ::S
+type lookup Outer::S
+type lookup Outer::Inner1::S
+type lookup Inner1::S
+type lookup Outer::Inner1::Inner2::S
+type lookup Inner2::S
+type lookup Outer::Inner2::S
+type lookup Outer::A
+type lookup A
+type lookup ::A
+expr sizeof(S)
+expr sizeof(A)
+
+quit
+
+# CHECK:      (lldb) type lookup S  
+# CHECK:      struct S {
+# CHECK:      struct S {
+# CHECK:      struct S {
+# CHECK:      struct S {
+# CHECK:      struct S {
+# CHECK:      }
+# CHECK-NEXT: (lldb) type lookup ::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT:     char a[1];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT:     char a[2];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::Inner1::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT:     char a[3];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Inner1::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT:     char a[3];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::Inner1::Inner2::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT:     char a[4];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Inner2::S         
+# CHECK-NEXT: struct S {
+# CHECK:      struct S {
+# CHECK:      }
+# CHECK-NEXT: (lldb) type lookup Outer::Inner2::S        
+# CHECK-NEXT: struct S {
+# CHECK-NEXT:     char a[5];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::A        
+# CHECK-NEXT: struct A {
+# CHECK-NEXT:     char a[6];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup A       
+# CHECK-NEXT: struct A {
+# CHECK:      struct A {
+# CHECK:      }
+# CHECK-NEXT: (lldb) type lookup ::A
+# CHECK-NEXT: struct A {
+# CHECK-NEXT:     char a[7];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) expr sizeof(S) 
+# CHECK-NEXT: (__size_t) $0 = 1
+# CHECK-NEXT: (lldb) expr sizeof(A)
+# CHECK-NEXT: (__size_t) $1 = 7


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to