diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 60a695c04ad95..5c90006d9addf 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -67,14 +67,16 @@ class SynthAddOptions { 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 @@ class CommandObjectTypeSynthAdd : public CommandObjectParsed, 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 @@ class CommandObjectTypeSynthAdd : public CommandObjectParsed, 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 @@ class CommandObjectTypeSynthAdd : public CommandObjectParsed, 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 @@ class CommandObjectTypeSynthAdd : public CommandObjectParsed, 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 a4d72010d2c4c..78bde66199659 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 0000000000000..99998b20bcb05 --- /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 0000000000000..ca56432841284 --- /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 0000000000000..40903b9ca4654 --- /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 0000000000000..e4cc5bbc94a22 --- /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