diff --git a/lldb/include/lldb/Interpreter/OptionGroupVariable.h b/lldb/include/lldb/Interpreter/OptionGroupVariable.h --- a/lldb/include/lldb/Interpreter/OptionGroupVariable.h +++ b/lldb/include/lldb/Interpreter/OptionGroupVariable.h @@ -35,7 +35,7 @@ // true) show_locals : 1, // Frame option only (include_frame_options == true) show_globals : 1, // Frame option only (include_frame_options == true) - use_regex : 1, show_scope : 1, show_decl : 1; + use_regex : 1, show_scope : 1, show_decl : 1, apply_fixits : 1; OptionValueString summary; // the name of a named summary OptionValueString summary_string; // a summary string diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -578,9 +578,11 @@ { Status error; uint32_t expr_path_options = - StackFrame::eExpressionPathOptionCheckPtrVsMember | StackFrame::eExpressionPathOptionsAllowDirectIVarAccess | StackFrame::eExpressionPathOptionsInspectAnonymousUnions; + if (!m_option_variable.apply_fixits) + expr_path_options |= + StackFrame::eExpressionPathOptionCheckPtrVsMember; lldb::VariableSP var_sp; valobj_sp = frame->GetValueForVariableExpressionPath( entry.ref(), m_varobj_options.use_dynamic, expr_path_options, @@ -602,10 +604,16 @@ options.SetVariableFormatDisplayLanguage( valobj_sp->GetPreferredDisplayLanguage()); - Stream &output_stream = result.GetOutputStream(); + // Use the returned value's expression path, because it might + // differ from the input expression (when fixits are used). + StreamString expr_path_stream; + valobj_sp->GetExpressionPath( + expr_path_stream, + ValueObject::eGetExpressionPathFormatHonorPointers); + auto root_name = expr_path_stream.GetString(); options.SetRootValueObjectName( - valobj_sp->GetParent() ? entry.c_str() : nullptr); - valobj_sp->Dump(output_stream, options); + valobj_sp->GetParent() ? root_name.data() : nullptr); + valobj_sp->Dump(result.GetOutputStream(), options); } else { if (auto error_cstr = error.AsCString(nullptr)) result.AppendError(error_cstr); diff --git a/lldb/source/Interpreter/OptionGroupVariable.cpp b/lldb/source/Interpreter/OptionGroupVariable.cpp --- a/lldb/source/Interpreter/OptionGroupVariable.cpp +++ b/lldb/source/Interpreter/OptionGroupVariable.cpp @@ -11,14 +11,14 @@ #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Target/Target.h" #include "lldb/Utility/Status.h" using namespace lldb; using namespace lldb_private; -// if you add any options here, remember to update the counters in -// OptionGroupVariable::GetNumDefinitions() +// clang-format off static constexpr OptionDefinition g_variable_options[] = { {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-args", 'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, @@ -42,6 +42,9 @@ {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "scope", 's', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show variable scope (argument, local, global, static)."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "apply-fixits", 'X', + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, + "simple fix-it corrections will be applied to the variable path."}, {LLDB_OPT_SET_1, false, "summary", 'y', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, "Specify the summary that the variable output should use."}, @@ -49,6 +52,7 @@ OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, "Specify a summary string to use to format the variable output."}, }; +// clang-format on static Status ValidateNamedSummary(const char *str, void *) { if (!str || !str[0]) @@ -70,7 +74,8 @@ : include_frame_options(show_frame_options), show_args(false), show_recognized_args(false), show_locals(false), show_globals(false), use_regex(false), show_scope(false), show_decl(false), - summary(ValidateNamedSummary), summary_string(ValidateSummaryString) {} + apply_fixits(false), summary(ValidateNamedSummary), + summary_string(ValidateSummaryString) {} Status OptionGroupVariable::SetOptionValue(uint32_t option_idx, @@ -102,6 +107,15 @@ case 't': show_recognized_args = false; break; + case 'X': { + bool success = false; + apply_fixits = OptionArgParser::ToBoolean(option_arg, false, &success); + if (!success) + error.SetErrorStringWithFormat( + "Invalid boolean '%s' passed for -X option", + option_arg.str().c_str()); + break; + } case 'y': error = summary.SetCurrentValue(option_arg); break; @@ -124,6 +138,7 @@ show_decl = false; use_regex = false; show_scope = false; + apply_fixits = false; summary.Clear(); summary_string.Clear(); } diff --git a/lldb/test/API/commands/frame/var/fixits/Makefile b/lldb/test/API/commands/frame/var/fixits/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/frame/var/fixits/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := main.c +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var/fixits/TestFrameVarApplyFixits.py b/lldb/test/API/commands/frame/var/fixits/TestFrameVarApplyFixits.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/frame/var/fixits/TestFrameVarApplyFixits.py @@ -0,0 +1,24 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test_frame_variable_apply_fixits(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + self.expect( + "frame variable s->num", + error=True, + startstr='error: "s" is not a pointer and -> was used to attempt to access "num". Did you mean "s.num"?', + ) + self.expect("frame variable --apply-fixits on s->num", startstr="(int) s.num = 30") + + self.expect( + "frame variable p.num", + error=True, + startstr='error: "p" is a pointer and . was used to attempt to access "num". Did you mean "p->num"?', + ) + self.expect("frame variable --apply-fixits on p.num", startstr="(int) p->num = 30") diff --git a/lldb/test/API/commands/frame/var/fixits/main.c b/lldb/test/API/commands/frame/var/fixits/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/frame/var/fixits/main.c @@ -0,0 +1,10 @@ +struct Structure { + int num; +}; + +int main() { + struct Structure s = {30}; + struct Structure *p = &s; + // break here + return 0; +}