[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
This commit is contained in:
parent
28318d5db8
commit
b8b4804d17
@ -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<SynthAddOptions> 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<SynthAddOptions>(
|
||||
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());
|
||||
|
||||
@ -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.">;
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
||||
@ -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"])
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user