[Lldb-commits] [lldb] [LLDB] Add formatters for MSVC STL std::vector (PR #147538)

via lldb-commits lldb-commits at lists.llvm.org
Tue Jul 15 10:17:47 PDT 2025


https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/147538

>From d8a898b9a3f6b16d551baa7253344402d75ca4d8 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Sat, 21 Jun 2025 17:35:23 +0200
Subject: [PATCH 1/3] [LLDB] Add formatters for MSVC STL std::vector

---
 .../Plugins/Language/CPlusPlus/CMakeLists.txt |   1 +
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  |  30 +-
 .../Plugins/Language/CPlusPlus/MsvcStl.h      |   5 +
 .../Language/CPlusPlus/MsvcStlVector.cpp      | 297 ++++++++++++++++++
 .../vbool/TestDataFormatterStdVBool.py        |  24 +-
 .../data-formatter-stl/generic/vbool/main.cpp |  39 ++-
 .../vector/TestDataFormatterStdVector.py      |  11 +
 7 files changed, 399 insertions(+), 8 deletions(-)
 create mode 100644 lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp

diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index ea86b6b4327be..8ee6e2a246c55 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -36,6 +36,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   MsvcStl.cpp
   MsvcStlSmartPointer.cpp
   MsvcStlTuple.cpp
+  MsvcStlVector.cpp
   MSVCUndecoratedNameParser.cpp
 
   LINK_COMPONENTS
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index bf4139119a76b..8724e829835c1 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1404,7 +1404,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   stl_deref_flags.SetFrontEndWantsDereference();
 
   cpp_category_sp->AddTypeSynthetic(
-      "^std::(__debug::)?vector<.+>(( )?&)?$", eFormatterMatchRegex,
+      "^std::__debug::vector<.+>(( )?&)?$", eFormatterMatchRegex,
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_synth_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider")));
@@ -1465,10 +1465,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       "libstdc++ std::bitset summary provider",
       "^std::(__debug::)?bitset<.+>(( )?&)?$", stl_summary_flags, true);
 
-  AddCXXSummary(
-      cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
-      "libstdc++ std::vector summary provider",
-      "^std::(__debug::)?vector<.+>(( )?&)?$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::ContainerSizeSummaryProvider,
+                "libstdc++ std::__debug::vector summary provider",
+                "^std::__debug::vector<.+>(( )?&)?$", stl_summary_flags, true);
 
   AddCXXSummary(
       cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1615,6 +1615,20 @@ GenericTupleSyntheticFrontEndCreator(CXXSyntheticChildren *children,
   return LibStdcppTupleSyntheticFrontEndCreator(children, valobj_sp);
 }
 
+static SyntheticChildrenFrontEnd *
+GenericVectorSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+                                      lldb::ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+
+  // checks for vector<T> and vector<bool>
+  if (auto *msvc = MsvcStlVectorSyntheticFrontEndCreator(valobj_sp))
+    return msvc;
+
+  return new ScriptedSyntheticChildren::FrontEnd(
+      "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider", *valobj_sp);
+}
+
 /// Load formatters that are formatting types from more than one STL
 static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   if (!cpp_category_sp)
@@ -1686,6 +1700,12 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
                 "MSVC STL/libstdc++ std::tuple summary provider",
                 "^std::tuple<.*>(( )?&)?$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+                "MSVC/libstdc++ std::vector summary provider",
+                "^std::vector<.+>(( )?&)?$", stl_summary_flags, true);
+  AddCXXSynthetic(cpp_category_sp, GenericVectorSyntheticFrontEndCreator,
+                  "MSVC/libstdc++ std::vector synthetic provider",
+                  "^std::vector<.+>(( )?&)?$", stl_synth_flags, true);
 }
 
 static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index bad47701904bb..81397851b6010 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -51,6 +51,11 @@ SyntheticChildrenFrontEnd *
 MsvcStlTupleSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                      lldb::ValueObjectSP valobj_sp);
 
+// MSVC STL std::vector<>
+bool IsMsvcStlVector(ValueObject &valobj);
+lldb_private::SyntheticChildrenFrontEnd *
+MsvcStlVectorSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
+
 } // namespace formatters
 } // namespace lldb_private
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
new file mode 100644
index 0000000000000..83374c9a67a3c
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
@@ -0,0 +1,297 @@
+//===-- MsvcStlVector.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MsvcStl.h"
+
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+
+using namespace lldb;
+
+namespace lldb_private {
+namespace formatters {
+
+class MsvcStlVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  MsvcStlVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+  llvm::Expected<uint32_t> CalculateNumChildren() override;
+
+  lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
+
+  lldb::ChildCacheState Update() override;
+
+  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
+
+private:
+  ValueObject *m_start = nullptr;
+  ValueObject *m_finish = nullptr;
+  CompilerType m_element_type;
+  uint32_t m_element_size = 0;
+};
+
+class MsvcStlVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  MsvcStlVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+  llvm::Expected<uint32_t> CalculateNumChildren() override;
+
+  lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
+
+  lldb::ChildCacheState Update() override;
+
+  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
+
+private:
+  CompilerType m_bool_type;
+  ExecutionContextRef m_exe_ctx_ref;
+  uint64_t m_count = 0;
+  uint64_t m_element_bit_size = 0;
+  lldb::addr_t m_base_data_address = 0;
+  std::map<size_t, lldb::ValueObjectSP> m_children;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::
+    MsvcStlVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+    : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() {
+  if (valobj_sp)
+    Update();
+}
+
+llvm::Expected<uint32_t> lldb_private::formatters::
+    MsvcStlVectorSyntheticFrontEnd::CalculateNumChildren() {
+  if (!m_start || !m_finish)
+    return llvm::createStringError(
+        "Failed to determine start/end of vector data.");
+
+  uint64_t start_val = m_start->GetValueAsUnsigned(0);
+  uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
+
+  // A default-initialized empty vector.
+  if (start_val == 0 && finish_val == 0)
+    return 0;
+
+  if (start_val == 0)
+    return llvm::createStringError("Invalid value for start of vector.");
+
+  if (finish_val == 0)
+    return llvm::createStringError("Invalid value for end of vector.");
+
+  if (start_val > finish_val)
+    return llvm::createStringError(
+        "Start of vector data begins after end pointer.");
+
+  size_t num_children = (finish_val - start_val);
+  if (num_children % m_element_size)
+    return llvm::createStringError("Size not multiple of element size.");
+
+  return num_children / m_element_size;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::GetChildAtIndex(
+    uint32_t idx) {
+  if (!m_start || !m_finish)
+    return lldb::ValueObjectSP();
+
+  uint64_t offset = idx * m_element_size;
+  offset = offset + m_start->GetValueAsUnsigned(0);
+  StreamString name;
+  name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+  return CreateValueObjectFromAddress(name.GetString(), offset,
+                                      m_backend.GetExecutionContextRef(),
+                                      m_element_type);
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::Update() {
+  m_start = m_finish = nullptr;
+  ValueObjectSP data_sp(m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"}));
+
+  if (!data_sp)
+    return lldb::ChildCacheState::eRefetch;
+
+  m_start = data_sp->GetChildMemberWithName("_Myfirst").get();
+  m_finish = data_sp->GetChildMemberWithName("_Mylast").get();
+  if (!m_start || !m_finish)
+    return lldb::ChildCacheState::eRefetch;
+
+  m_element_type = m_start->GetCompilerType().GetPointeeType();
+  llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr);
+  if (size_or_err)
+    m_element_size = *size_or_err;
+  else
+    LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(),
+                    "{0}");
+
+  return lldb::ChildCacheState::eRefetch;
+}
+
+llvm::Expected<size_t> lldb_private::formatters::
+    MsvcStlVectorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
+  if (!m_start || !m_finish)
+    return llvm::createStringError("Type has no child named '%s'",
+                                   name.AsCString());
+  auto optional_idx = ExtractIndexFromString(name.GetCString());
+  if (!optional_idx) {
+    return llvm::createStringError("Type has no child named '%s'",
+                                   name.AsCString());
+  }
+  return *optional_idx;
+}
+
+lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::
+    MsvcStlVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+    : SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(),
+      m_children() {
+  if (valobj_sp) {
+    Update();
+    m_bool_type =
+        valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool);
+  }
+}
+
+llvm::Expected<uint32_t> lldb_private::formatters::
+    MsvcStlVectorBoolSyntheticFrontEnd::CalculateNumChildren() {
+  return m_count;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::GetChildAtIndex(
+    uint32_t idx) {
+  auto iter = m_children.find(idx), end = m_children.end();
+  if (iter != end)
+    return iter->second;
+  if (idx >= m_count)
+    return {};
+  if (m_base_data_address == 0 || m_count == 0)
+    return {};
+  if (!m_bool_type)
+    return {};
+
+  // The vector<bool> is represented as a sequence of `int`s.
+  // The size of an `int` is in `m_element_bit_size` (most often 32b).
+  // To access the element at index `i`:
+  // (bool)((data_address[i / bit_size] >> (i % bit_size)) & 1)
+
+  // int *byte_location = &data_address[i / bit_size]
+  size_t byte_idx = (idx / m_element_bit_size) * (m_element_bit_size / 8);
+  lldb::addr_t byte_location = m_base_data_address + byte_idx;
+
+  ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
+  if (!process_sp)
+    return {};
+  Status err;
+  Scalar scalar;
+  size_t bytes_read = process_sp->ReadScalarIntegerFromMemory(
+      byte_location, m_element_bit_size / 8, false, scalar, err);
+  if (err.Fail() || bytes_read == 0 || !scalar.IsValid())
+    return {};
+
+  size_t bit_index = idx % m_element_bit_size;
+  bool bit_set = scalar.GetAPSInt()[bit_index];
+  std::optional<uint64_t> size =
+      llvm::expectedToOptional(m_bool_type.GetByteSize(nullptr));
+  if (!size)
+    return {};
+  WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
+  if (bit_set && buffer_sp && buffer_sp->GetBytes()) {
+    // regardless of endianness, anything non-zero is true
+    *(buffer_sp->GetBytes()) = 1;
+  }
+  StreamString name;
+  name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+  ValueObjectSP retval_sp(CreateValueObjectFromData(
+      name.GetString(),
+      DataExtractor(buffer_sp, process_sp->GetByteOrder(),
+                    process_sp->GetAddressByteSize()),
+      m_exe_ctx_ref, m_bool_type));
+  if (retval_sp)
+    m_children[idx] = retval_sp;
+  return retval_sp;
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::Update() {
+  m_children.clear();
+  ValueObjectSP valobj_sp = m_backend.GetSP();
+  if (!valobj_sp)
+    return lldb::ChildCacheState::eRefetch;
+  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+
+  ValueObjectSP size_sp = valobj_sp->GetChildMemberWithName("_Mysize");
+  if (!size_sp)
+    return lldb::ChildCacheState::eRefetch;
+  m_count = size_sp->GetValueAsUnsigned(0);
+  if (!m_count)
+    return lldb::ChildCacheState::eReuse;
+
+  ValueObjectSP begin_sp(valobj_sp->GetChildAtNamePath(
+      {"_Myvec", "_Mypair", "_Myval2", "_Myfirst"}));
+  if (!begin_sp) {
+    m_count = 0;
+    return lldb::ChildCacheState::eRefetch;
+  }
+
+  // FIXME: the STL exposes _EEN_VBITS as a constant - it should be used instead
+  CompilerType begin_ty = begin_sp->GetCompilerType().GetPointeeType();
+  if (!begin_ty.IsValid())
+    return lldb::ChildCacheState::eRefetch;
+  llvm::Expected<uint64_t> bit_size = begin_ty.GetBitSize(nullptr);
+  if (!bit_size)
+    return lldb::ChildCacheState::eRefetch;
+  m_element_bit_size = *bit_size;
+
+  m_base_data_address = begin_sp->GetValueAsUnsigned(0);
+  if (!m_base_data_address) {
+    m_count = 0;
+    return lldb::ChildCacheState::eRefetch;
+  }
+  return lldb::ChildCacheState::eRefetch;
+}
+
+llvm::Expected<size_t>
+lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::
+    GetIndexOfChildWithName(ConstString name) {
+  if (!m_count || !m_base_data_address)
+    return llvm::createStringError("Type has no child named '%s'",
+                                   name.AsCString());
+  auto optional_idx = ExtractIndexFromString(name.AsCString());
+  if (!optional_idx) {
+    return llvm::createStringError("Type has no child named '%s'",
+                                   name.AsCString());
+  }
+  uint32_t idx = *optional_idx;
+  if (idx >= CalculateNumChildrenIgnoringErrors())
+    return llvm::createStringError("Type has no child named '%s'",
+                                   name.AsCString());
+  return idx;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlVectorSyntheticFrontEndCreator(
+    lldb::ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+
+  valobj_sp = valobj_sp->GetNonSyntheticValue();
+  if (!valobj_sp)
+    return nullptr;
+
+  // vector<T>
+  if (valobj_sp->GetChildMemberWithName("_Mypair") != nullptr)
+    return new MsvcStlVectorSyntheticFrontEnd(valobj_sp);
+  // vector<bool>
+  if (valobj_sp->GetChildMemberWithName("_Myvec") != nullptr)
+    return new MsvcStlVectorBoolSyntheticFrontEnd(valobj_sp);
+
+  return nullptr;
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py
index 56c86d1edde25..dd142d2be193b 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py
@@ -47,7 +47,7 @@ def cleanup():
         self.expect(
             "frame variable -A vBool",
             substrs=[
-                "size=49",
+                "size=73",
                 "[0] = false",
                 "[1] = true",
                 "[18] = false",
@@ -55,13 +55,20 @@ def cleanup():
                 "[36] = false",
                 "[47] = true",
                 "[48] = true",
+                "[49] = true",
+                "[50] = false",
+                "[56] = false",
+                "[65] = true",
+                "[70] = false",
+                "[71] = true",
+                "[72] = true",
             ],
         )
 
         self.expect(
             "expr -A -- vBool",
             substrs=[
-                "size=49",
+                "size=73",
                 "[0] = false",
                 "[1] = true",
                 "[18] = false",
@@ -69,6 +76,13 @@ def cleanup():
                 "[36] = false",
                 "[47] = true",
                 "[48] = true",
+                "[49] = true",
+                "[50] = false",
+                "[56] = false",
+                "[65] = true",
+                "[70] = false",
+                "[71] = true",
+                "[72] = true",
             ],
         )
 
@@ -88,3 +102,9 @@ def test_libstdcxx_debug(self):
             dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": "-D_GLIBCXX_DEBUG"}
         )
         self.do_test()
+
+    @add_test_categories(["msvcstl"])
+    def test_libstdcxx(self):
+        # No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
+        self.build()
+        self.do_test()
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp
index 22fc6c89ca8a2..2c54166ace7cc 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/main.cpp
@@ -1,10 +1,10 @@
 #include <cstdio>
-#include <string>
 #include <vector>
 
 int main() {
   std::vector<bool> vBool;
 
+  // 0..=7
   vBool.push_back(false);
   vBool.push_back(true);
   vBool.push_back(false);
@@ -14,6 +14,7 @@ int main() {
   vBool.push_back(false);
   vBool.push_back(true);
 
+  // 8..=15
   vBool.push_back(false);
   vBool.push_back(true);
   vBool.push_back(false);
@@ -23,6 +24,7 @@ int main() {
   vBool.push_back(false);
   vBool.push_back(true);
 
+  // 16..=23
   vBool.push_back(false);
   vBool.push_back(true);
   vBool.push_back(false);
@@ -32,6 +34,7 @@ int main() {
   vBool.push_back(false);
   vBool.push_back(true);
 
+  // 24..=31
   vBool.push_back(false);
   vBool.push_back(true);
   vBool.push_back(false);
@@ -41,6 +44,7 @@ int main() {
   vBool.push_back(false);
   vBool.push_back(true);
 
+  // 32..=39
   vBool.push_back(false);
   vBool.push_back(true);
   vBool.push_back(false);
@@ -50,6 +54,7 @@ int main() {
   vBool.push_back(false);
   vBool.push_back(true);
 
+  // 40..=47
   vBool.push_back(false);
   vBool.push_back(true);
   vBool.push_back(false);
@@ -58,6 +63,38 @@ int main() {
   vBool.push_back(true);
   vBool.push_back(false);
   vBool.push_back(true);
+
+  // 48..=55
+  vBool.push_back(true);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+
+  // 56..=63
+  vBool.push_back(false);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+
+  // 64..=71
+  vBool.push_back(false);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+  vBool.push_back(true);
+  vBool.push_back(true);
+  vBool.push_back(false);
+  vBool.push_back(true);
+
+  // 72
   vBool.push_back(true);
 
   std::puts("// Set break point at this line.");
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py
index ba8b10450f4fc..d4da60f86a315 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vector/TestDataFormatterStdVector.py
@@ -184,6 +184,12 @@ def test_libcxx(self):
         self.build(dictionary={"USE_LIBCPP": 1})
         self.do_test()
 
+    @add_test_categories(["msvcstl"])
+    def test_msvcstl(self):
+        # No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
+        self.build()
+        self.do_test()
+
     def do_test_ref_and_ptr(self):
         """Test that that file and class static variables display correctly."""
         (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
@@ -215,3 +221,8 @@ def test_ref_and_ptr_libstdcxx_debug(self):
     def test_ref_and_ptr_libcxx(self):
         self.build(dictionary={"USE_LIBCPP": 1})
         self.do_test_ref_and_ptr()
+
+    @add_test_categories(["msvcstl"])
+    def test_ref_and_ptr_msvcstl(self):
+        self.build()
+        self.do_test_ref_and_ptr()

>From 610ef299426a2f11e9cde0a2065c62d54979893c Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 15 Jul 2025 15:15:18 +0200
Subject: [PATCH 2/3] fix: reset members first and set at the end of update()

---
 .../Language/CPlusPlus/MsvcStlVector.cpp      | 31 +++++++++++--------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
index 83374c9a67a3c..b1ae057548aa2 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
@@ -221,40 +221,45 @@ lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::GetChildAtIndex(
 
 lldb::ChildCacheState
 lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::Update() {
+  m_exe_ctx_ref.Clear();
+  m_count = 0;
+  m_element_bit_size = 0;
+  m_base_data_address = 0;
   m_children.clear();
+
   ValueObjectSP valobj_sp = m_backend.GetSP();
   if (!valobj_sp)
     return lldb::ChildCacheState::eRefetch;
-  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
+  auto exe_ctx_ref = valobj_sp->GetExecutionContextRef();
 
   ValueObjectSP size_sp = valobj_sp->GetChildMemberWithName("_Mysize");
   if (!size_sp)
     return lldb::ChildCacheState::eRefetch;
-  m_count = size_sp->GetValueAsUnsigned(0);
-  if (!m_count)
+  uint64_t count = size_sp->GetValueAsUnsigned(0);
+  if (count == 0)
     return lldb::ChildCacheState::eReuse;
 
   ValueObjectSP begin_sp(valobj_sp->GetChildAtNamePath(
       {"_Myvec", "_Mypair", "_Myval2", "_Myfirst"}));
-  if (!begin_sp) {
-    m_count = 0;
+  if (!begin_sp)
     return lldb::ChildCacheState::eRefetch;
-  }
 
   // FIXME: the STL exposes _EEN_VBITS as a constant - it should be used instead
   CompilerType begin_ty = begin_sp->GetCompilerType().GetPointeeType();
   if (!begin_ty.IsValid())
     return lldb::ChildCacheState::eRefetch;
-  llvm::Expected<uint64_t> bit_size = begin_ty.GetBitSize(nullptr);
-  if (!bit_size)
+  llvm::Expected<uint64_t> element_bit_size = begin_ty.GetBitSize(nullptr);
+  if (!element_bit_size)
     return lldb::ChildCacheState::eRefetch;
-  m_element_bit_size = *bit_size;
 
-  m_base_data_address = begin_sp->GetValueAsUnsigned(0);
-  if (!m_base_data_address) {
-    m_count = 0;
+  uint64_t base_data_address = begin_sp->GetValueAsUnsigned(0);
+  if (!base_data_address)
     return lldb::ChildCacheState::eRefetch;
-  }
+
+  m_exe_ctx_ref = exe_ctx_ref;
+  m_count = count;
+  m_element_bit_size = *element_bit_size;
+  m_base_data_address = base_data_address;
   return lldb::ChildCacheState::eRefetch;
 }
 

>From 75851ee6b31316f36aa0936166e981b050217126 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 15 Jul 2025 19:17:33 +0200
Subject: [PATCH 3/3] fix: add comment regarding PDB missing templates

---
 lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
index b1ae057548aa2..cfc98d27f56d6 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
@@ -291,6 +291,9 @@ lldb_private::formatters::MsvcStlVectorSyntheticFrontEndCreator(
   if (!valobj_sp)
     return nullptr;
 
+  // We can't check the template parameter here, because PDB doesn't include
+  // this information.
+
   // vector<T>
   if (valobj_sp->GetChildMemberWithName("_Mypair") != nullptr)
     return new MsvcStlVectorSyntheticFrontEnd(valobj_sp);



More information about the lldb-commits mailing list