1 //===-- CommandObjectDWIMPrint.cpp ------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "CommandObjectDWIMPrint.h"
11 #include "lldb/Core/ValueObject.h"
12 #include "lldb/DataFormatters/DumpValueObjectOptions.h"
13 #include "lldb/Expression/ExpressionVariable.h"
14 #include "lldb/Expression/UserExpression.h"
15 #include "lldb/Interpreter/CommandInterpreter.h"
16 #include "lldb/Interpreter/CommandObject.h"
17 #include "lldb/Interpreter/CommandReturnObject.h"
18 #include "lldb/Interpreter/OptionGroupFormat.h"
19 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
20 #include "lldb/Target/StackFrame.h"
21 #include "lldb/Utility/ConstString.h"
22 #include "lldb/lldb-defines.h"
23 #include "lldb/lldb-enumerations.h"
24 #include "lldb/lldb-forward.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/Support/FormatVariadic.h"
32 using namespace lldb_private
;
34 CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter
&interpreter
)
35 : CommandObjectRaw(interpreter
, "dwim-print",
36 "Print a variable or expression.",
37 "dwim-print [<variable-name> | <expression>]",
38 eCommandProcessMustBePaused
| eCommandTryTargetAPILock
) {
40 CommandArgumentData
var_name_arg(eArgTypeVarName
, eArgRepeatPlain
);
41 m_arguments
.push_back({var_name_arg
});
43 m_option_group
.Append(&m_format_options
,
44 OptionGroupFormat::OPTION_GROUP_FORMAT
|
45 OptionGroupFormat::OPTION_GROUP_GDB_FMT
,
47 StringRef exclude_expr_options
[] = {"debug", "top-level"};
48 m_option_group
.Append(&m_expr_options
, exclude_expr_options
);
49 m_option_group
.Append(&m_varobj_options
, LLDB_OPT_SET_ALL
, LLDB_OPT_SET_1
);
50 m_option_group
.Finalize();
53 Options
*CommandObjectDWIMPrint::GetOptions() { return &m_option_group
; }
55 void CommandObjectDWIMPrint::HandleArgumentCompletion(
56 CompletionRequest
&request
, OptionElementVector
&opt_element_vector
) {
57 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
58 GetCommandInterpreter(), lldb::eVariablePathCompletion
, request
, nullptr);
61 void CommandObjectDWIMPrint::DoExecute(StringRef command
,
62 CommandReturnObject
&result
) {
63 m_option_group
.NotifyOptionParsingStarting(&m_exe_ctx
);
64 OptionsWithRaw args
{command
};
65 StringRef expr
= args
.GetRawPart();
68 result
.AppendErrorWithFormatv("'{0}' takes a variable or expression",
74 if (!ParseOptionsAndNotify(args
.GetArgs(), result
, m_option_group
,
79 // If the user has not specified, default to disabling persistent results.
80 if (m_expr_options
.suppress_persistent_result
== eLazyBoolCalculate
)
81 m_expr_options
.suppress_persistent_result
= eLazyBoolYes
;
82 bool suppress_result
= m_expr_options
.ShouldSuppressResult(m_varobj_options
);
84 auto verbosity
= GetDebugger().GetDWIMPrintVerbosity();
86 Target
*target_ptr
= m_exe_ctx
.GetTargetPtr();
87 // Fallback to the dummy target, which can allow for expression evaluation.
88 Target
&target
= target_ptr
? *target_ptr
: GetDummyTarget();
90 EvaluateExpressionOptions eval_options
=
91 m_expr_options
.GetEvaluateExpressionOptions(target
, m_varobj_options
);
92 // This command manually removes the result variable, make sure expression
93 // evaluation doesn't do it first.
94 eval_options
.SetSuppressPersistentResult(false);
96 DumpValueObjectOptions dump_options
= m_varobj_options
.GetAsDumpOptions(
97 m_expr_options
.m_verbosity
, m_format_options
.GetFormat());
98 dump_options
.SetHideRootName(suppress_result
);
100 bool is_po
= m_varobj_options
.use_objc
;
102 StackFrame
*frame
= m_exe_ctx
.GetFramePtr();
104 // Either Swift was explicitly specified, or the frame is Swift.
105 lldb::LanguageType language
= m_expr_options
.language
;
106 if (language
== lldb::eLanguageTypeUnknown
&& frame
)
107 language
= frame
->GuessLanguage();
109 // Add a hint if object description was requested, but no description
110 // function was implemented.
111 auto maybe_add_hint
= [&](llvm::StringRef output
) {
112 // Identify the default output of object description for Swift and
114 // "<Name: 0x...>. The regex is:
116 // - Followed by 1 or more non-whitespace characters.
117 // - Followed by ": 0x".
118 // - Followed by 5 or more hex digits.
119 // - Followed by ">".
120 // - End with zero or more whitespace characters.
121 const std::regex
swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");
123 if (GetDebugger().GetShowDontUsePoHint() && target_ptr
&&
124 (language
== lldb::eLanguageTypeSwift
||
125 language
== lldb::eLanguageTypeObjC
) &&
126 std::regex_match(output
.data(), swift_class_regex
)) {
128 static bool note_shown
= false;
132 result
.GetOutputStream()
133 << "note: object description requested, but type doesn't implement "
134 "a custom object description. Consider using \"p\" instead of "
135 "\"po\" (this note will only be shown once per debug session).\n";
140 // First, try `expr` as the name of a frame variable.
142 auto valobj_sp
= frame
->FindVariable(ConstString(expr
));
143 if (valobj_sp
&& valobj_sp
->GetError().Success()) {
144 if (!suppress_result
) {
145 if (auto persisted_valobj
= valobj_sp
->Persist())
146 valobj_sp
= persisted_valobj
;
149 if (verbosity
== eDWIMPrintVerbosityFull
) {
152 flags
= args
.GetArgString();
153 result
.AppendMessageWithFormatv("note: ran `frame variable {0}{1}`",
158 StreamString temp_result_stream
;
159 valobj_sp
->Dump(temp_result_stream
, dump_options
);
160 llvm::StringRef output
= temp_result_stream
.GetString();
161 maybe_add_hint(output
);
162 result
.GetOutputStream() << output
;
164 valobj_sp
->Dump(result
.GetOutputStream(), dump_options
);
166 result
.SetStatus(eReturnStatusSuccessFinishResult
);
171 // Second, also lastly, try `expr` as a source expression to evaluate.
173 auto *exe_scope
= m_exe_ctx
.GetBestExecutionContextScope();
174 ValueObjectSP valobj_sp
;
175 std::string fixed_expression
;
177 ExpressionResults expr_result
= target
.EvaluateExpression(
178 expr
, exe_scope
, valobj_sp
, eval_options
, &fixed_expression
);
180 // Only mention Fix-Its if the expression evaluator applied them.
181 // Compiler errors refer to the final expression after applying Fix-It(s).
182 if (!fixed_expression
.empty() && target
.GetEnableNotifyAboutFixIts()) {
183 Stream
&error_stream
= result
.GetErrorStream();
184 error_stream
<< " Evaluated this expression after applying Fix-It(s):\n";
185 error_stream
<< " " << fixed_expression
<< "\n";
188 if (expr_result
== eExpressionCompleted
) {
189 if (verbosity
!= eDWIMPrintVerbosityNone
) {
192 flags
= args
.GetArgStringWithDelimiter();
193 result
.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags
,
197 if (valobj_sp
->GetError().GetError() != UserExpression::kNoResult
) {
199 StreamString temp_result_stream
;
200 valobj_sp
->Dump(temp_result_stream
, dump_options
);
201 llvm::StringRef output
= temp_result_stream
.GetString();
202 maybe_add_hint(output
);
203 result
.GetOutputStream() << output
;
205 valobj_sp
->Dump(result
.GetOutputStream(), dump_options
);
210 if (auto result_var_sp
=
211 target
.GetPersistentVariable(valobj_sp
->GetName())) {
212 auto language
= valobj_sp
->GetPreferredDisplayLanguage();
213 if (auto *persistent_state
=
214 target
.GetPersistentExpressionStateForLanguage(language
))
215 persistent_state
->RemovePersistentVariable(result_var_sp
);
218 result
.SetStatus(eReturnStatusSuccessFinishResult
);
221 result
.SetError(valobj_sp
->GetError());
223 result
.AppendErrorWithFormatv(
224 "unknown error evaluating expression `{0}`", expr
);