From b8b4804d1785784933f8945334d9c8bcd2443417 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 25 Mar 2026 20:11:23 +0000 Subject: [PATCH] [lldb][CommandObjectType] Add --wants-dereference option to `type synthetic add` (#188512) This patch exposes the `TypeSynthetic::SetFrontEndWantsDereference` via the `type synthetic add` command. The motivation for this is moving the various STL data-formatters to Python. Those currently set this flag programmatically so that pointers and references get formatted using the pointee synthetic provider. Patch that makes use of this new option is: https://github.com/llvm/llvm-project/pull/187677 Claude helped with writing the test code. Reviewed and cleaned it up myself --- lldb/source/Commands/CommandObjectType.cpp | 22 ++++++-- lldb/source/Commands/Options.td | 6 +++ .../type-synth-wants-deref/Makefile | 3 ++ .../TestTypeSynthRequiresDeref.py | 52 +++++++++++++++++++ .../type-synth-wants-deref/main.cpp | 13 +++++ .../type-synth-wants-deref/provider.py | 36 +++++++++++++ 6 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/TestTypeSynthRequiresDeref.py create mode 100644 lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/main.cpp create mode 100644 lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/provider.py diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 60a695c04ad9..5c90006d9add 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -67,14 +67,16 @@ public: bool m_skip_pointers; bool m_skip_references; bool m_cascade; + bool m_wants_deref; FormatterMatchType m_match_type; StringList m_target_types; std::string m_category; - SynthAddOptions(bool sptr, bool sref, bool casc, + SynthAddOptions(bool sptr, bool sref, bool casc, bool wants_deref, FormatterMatchType match_type, std::string catg) : m_skip_pointers(sptr), m_skip_references(sref), m_cascade(casc), - m_match_type(match_type), m_category(catg) {} + m_wants_deref(wants_deref), m_match_type(match_type), m_category(catg) { + } typedef std::shared_ptr SharedPointer; }; @@ -322,6 +324,13 @@ private: error = Status::FromErrorStringWithFormat( "invalid value for cascade: %s", option_arg.str().c_str()); break; + case 'D': + m_wants_deref = OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error = Status::FromErrorStringWithFormat( + "invalid value for wants-dereference: %s", + option_arg.str().c_str()); + break; case 'P': handwrite_python = true; break; @@ -361,6 +370,7 @@ private: void OptionParsingStarting(ExecutionContext *execution_context) override { m_cascade = true; + m_wants_deref = true; m_class_name = ""; m_skip_pointers = false; m_skip_references = false; @@ -379,6 +389,7 @@ private: bool m_cascade; bool m_skip_references; bool m_skip_pointers; + bool m_wants_deref; std::string m_class_name; bool m_input_python; std::string m_category; @@ -454,7 +465,8 @@ protected: SyntheticChildren::Flags() .SetCascades(options->m_cascade) .SetSkipPointers(options->m_skip_pointers) - .SetSkipReferences(options->m_skip_references), + .SetSkipReferences(options->m_skip_references) + .SetFrontEndWantsDereference(options->m_wants_deref), class_name_str.c_str()); lldb::TypeCategoryImplSP category; @@ -2131,7 +2143,8 @@ bool CommandObjectTypeSynthAdd::Execute_HandwritePython( Args &command, CommandReturnObject &result) { auto options = std::make_unique( m_options.m_skip_pointers, m_options.m_skip_references, - m_options.m_cascade, m_options.m_match_type, m_options.m_category); + m_options.m_cascade, m_options.m_wants_deref, m_options.m_match_type, + m_options.m_category); for (auto &entry : command.entries()) { if (entry.ref().empty()) { @@ -2173,6 +2186,7 @@ bool CommandObjectTypeSynthAdd::Execute_PythonClass( ScriptedSyntheticChildren *impl = new ScriptedSyntheticChildren( SyntheticChildren::Flags() .SetCascades(m_options.m_cascade) + .SetFrontEndWantsDereference(m_options.m_wants_deref) .SetSkipPointers(m_options.m_skip_pointers) .SetSkipReferences(m_options.m_skip_references), m_options.m_class_name.c_str()); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index a4d72010d2c4..78bde6619965 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -2267,6 +2267,12 @@ let Command = "type synth add" in { def type_synth_add_cascade : Option<"cascade", "C">, Arg<"Boolean">, Desc<"If true, cascade through typedef chains.">; + def type_synth_add_wants_deref + : Option<"requires-dereference", "D">, + Arg<"Boolean">, + Desc<"If true, when this synthetic provider matches a pointer or " + "reference type, it will receive the dereferenced value object " + "instead of the raw pointer or reference.">; def type_synth_add_skip_pointers : Option<"skip-pointers", "p">, Desc<"Don't use this format for pointers-to-type objects.">; diff --git a/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/Makefile b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/Makefile new file mode 100644 index 000000000000..99998b20bcb0 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/TestTypeSynthRequiresDeref.py b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/TestTypeSynthRequiresDeref.py new file mode 100644 index 000000000000..ca5643284128 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/TestTypeSynthRequiresDeref.py @@ -0,0 +1,52 @@ +""" +Test the --requires-dereference flag of 'type synth add'. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TypeSynthRequiresDerefTestCase(TestBase): + def setUp(self): + TestBase.setUp(self) + self.addTearDownHook(lambda: self.runCmd("type synth clear", check=False)) + + def _setup_synthetic(self, requires_deref: bool): + self.runCmd("command script import provider.py") + self.runCmd( + f"type synth add -l provider.WrapperSynthProvider --requires-dereference {requires_deref} Wrapper" + ) + + def test_requires_deref_on_pointer(self): + """With --requires-dereference true on pointer.""" + self.build() + lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) + self._setup_synthetic(True) + + self.expect_var_path("wp", children=[ValueCheck(name="sum", value="30")]) + + def test_requires_deref_on_reference(self): + """With --requires-dereference true on reference.""" + self.build() + lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) + self._setup_synthetic(True) + + self.expect_var_path("wr", children=[ValueCheck(name="sum", value="30")]) + + def test_no_requires_deref_on_pointer(self): + """With --requires-dereference false on pointer.""" + self.build() + lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) + self._setup_synthetic(False) + + self.expect("frame variable wp", matching=False, substrs=["sum"]) + + def test_no_requires_deref_on_reference(self): + """With --requires-dereference false on reference.""" + self.build() + lldbutil.run_to_source_breakpoint(self, "return 0", lldb.SBFileSpec("main.cpp")) + self._setup_synthetic(False) + + self.expect("frame variable wr", matching=False, substrs=["sum"]) diff --git a/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/main.cpp b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/main.cpp new file mode 100644 index 000000000000..40903b9ca465 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/main.cpp @@ -0,0 +1,13 @@ +struct Wrapper { + int x; + int y; +}; + +int main() { + Wrapper w{10, 20}; + Wrapper *wp = &w; + Wrapper &wr = w; + (void)wp; + (void)wr; + return 0; +} diff --git a/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/provider.py b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/provider.py new file mode 100644 index 000000000000..e4cc5bbc94a2 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/type-synth-wants-deref/provider.py @@ -0,0 +1,36 @@ +class WrapperSynthProvider: + def __init__(self, valobj, internal_dict): + self.valobj = valobj + self.sum_value = None + + def update(self): + self.sum_value = None + ty = self.valobj.GetType() + + # Artificially bail out if LLDB passed us a reference or a pointer. + if ty.IsPointerType() or ty.IsReferenceType(): + return False + + x = self.valobj.GetChildMemberWithName("x") + y = self.valobj.GetChildMemberWithName("y") + if x.IsValid() and y.IsValid(): + sum_val = x.GetValueAsUnsigned(0) + y.GetValueAsUnsigned(0) + self.sum_value = self.valobj.CreateValueFromExpression("sum", str(sum_val)) + + return False + + def num_children(self): + return 1 + + def get_child_at_index(self, index): + if index == 0 and self.sum_value: + return self.sum_value + return None + + def get_child_index(self, name): + if name == "sum": + return 0 + return -1 + + def has_children(self): + return True