xref: /llvm-project/lldb/source/Commands/CommandObjectDWIMPrint.cpp (revision 1250a1db1a378736afda389c94d2846d7a254576)
1185d4964SDave Lee //===-- CommandObjectDWIMPrint.cpp ------------------------------*- C++ -*-===//
2185d4964SDave Lee //
3185d4964SDave Lee // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4185d4964SDave Lee // See https://llvm.org/LICENSE.txt for license information.
5185d4964SDave Lee // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6185d4964SDave Lee //
7185d4964SDave Lee //===----------------------------------------------------------------------===//
8185d4964SDave Lee 
9185d4964SDave Lee #include "CommandObjectDWIMPrint.h"
10185d4964SDave Lee 
11d160873cSDave Lee #include "lldb/DataFormatters/DumpValueObjectOptions.h"
12db814552SDave Lee #include "lldb/Expression/ExpressionVariable.h"
13061a8390SDave Lee #include "lldb/Expression/UserExpression.h"
14185d4964SDave Lee #include "lldb/Interpreter/CommandInterpreter.h"
15185d4964SDave Lee #include "lldb/Interpreter/CommandObject.h"
16185d4964SDave Lee #include "lldb/Interpreter/CommandReturnObject.h"
17d160873cSDave Lee #include "lldb/Interpreter/OptionGroupFormat.h"
18d160873cSDave Lee #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
19185d4964SDave Lee #include "lldb/Target/StackFrame.h"
20185d4964SDave Lee #include "lldb/Utility/ConstString.h"
21b852fb1eSJonas Devlieghere #include "lldb/ValueObject/ValueObject.h"
22d160873cSDave Lee #include "lldb/lldb-defines.h"
23185d4964SDave Lee #include "lldb/lldb-enumerations.h"
24185d4964SDave Lee #include "lldb/lldb-forward.h"
25d160873cSDave Lee #include "llvm/ADT/StringRef.h"
26185d4964SDave Lee 
275f45a87bSAugusto Noronha #include <regex>
285f45a87bSAugusto Noronha 
29185d4964SDave Lee using namespace llvm;
30185d4964SDave Lee using namespace lldb;
31185d4964SDave Lee using namespace lldb_private;
32185d4964SDave Lee 
33185d4964SDave Lee CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter &interpreter)
34cebb87e7SDave Lee     : CommandObjectRaw(interpreter, "dwim-print",
35cebb87e7SDave Lee                        "Print a variable or expression.",
36185d4964SDave Lee                        "dwim-print [<variable-name> | <expression>]",
37cebb87e7SDave Lee                        eCommandProcessMustBePaused | eCommandTryTargetAPILock) {
388794712eSDave Lee 
392d704f4bSjimingham   AddSimpleArgumentList(eArgTypeVarName);
408794712eSDave Lee 
41d160873cSDave Lee   m_option_group.Append(&m_format_options,
42d160873cSDave Lee                         OptionGroupFormat::OPTION_GROUP_FORMAT |
43d160873cSDave Lee                             OptionGroupFormat::OPTION_GROUP_GDB_FMT,
44d160873cSDave Lee                         LLDB_OPT_SET_1);
45920b46e1SDave Lee   StringRef exclude_expr_options[] = {"debug", "top-level"};
46920b46e1SDave Lee   m_option_group.Append(&m_expr_options, exclude_expr_options);
47d160873cSDave Lee   m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
48d160873cSDave Lee   m_option_group.Finalize();
49cebb87e7SDave Lee }
50185d4964SDave Lee 
51d160873cSDave Lee Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; }
52185d4964SDave Lee 
5392d8a28cSPete Lawrence void CommandObjectDWIMPrint::DoExecute(StringRef command,
54d160873cSDave Lee                                        CommandReturnObject &result) {
55d160873cSDave Lee   m_option_group.NotifyOptionParsingStarting(&m_exe_ctx);
56d160873cSDave Lee   OptionsWithRaw args{command};
57d160873cSDave Lee   StringRef expr = args.GetRawPart();
58d160873cSDave Lee 
594d18d97bSDave Lee   if (expr.empty()) {
604d18d97bSDave Lee     result.AppendErrorWithFormatv("'{0}' takes a variable or expression",
614d18d97bSDave Lee                                   m_cmd_name);
6292d8a28cSPete Lawrence     return;
634d18d97bSDave Lee   }
644d18d97bSDave Lee 
65d160873cSDave Lee   if (args.HasArgs()) {
66d160873cSDave Lee     if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,
67d160873cSDave Lee                                m_exe_ctx))
6892d8a28cSPete Lawrence       return;
69185d4964SDave Lee   }
70920b46e1SDave Lee 
7138549638SDave Lee   // If the user has not specified, default to disabling persistent results.
7238549638SDave Lee   if (m_expr_options.suppress_persistent_result == eLazyBoolCalculate)
7338549638SDave Lee     m_expr_options.suppress_persistent_result = eLazyBoolYes;
74db814552SDave Lee   bool suppress_result = m_expr_options.ShouldSuppressResult(m_varobj_options);
7538549638SDave Lee 
76185d4964SDave Lee   auto verbosity = GetDebugger().GetDWIMPrintVerbosity();
77185d4964SDave Lee 
7863c77bf7SDave Lee   Target *target_ptr = m_exe_ctx.GetTargetPtr();
7963c77bf7SDave Lee   // Fallback to the dummy target, which can allow for expression evaluation.
8063c77bf7SDave Lee   Target &target = target_ptr ? *target_ptr : GetDummyTarget();
8163c77bf7SDave Lee 
82db814552SDave Lee   EvaluateExpressionOptions eval_options =
8363c77bf7SDave Lee       m_expr_options.GetEvaluateExpressionOptions(target, m_varobj_options);
84db814552SDave Lee   // This command manually removes the result variable, make sure expression
85db814552SDave Lee   // evaluation doesn't do it first.
86db814552SDave Lee   eval_options.SetSuppressPersistentResult(false);
8763c77bf7SDave Lee 
88d160873cSDave Lee   DumpValueObjectOptions dump_options = m_varobj_options.GetAsDumpOptions(
89920b46e1SDave Lee       m_expr_options.m_verbosity, m_format_options.GetFormat());
90db814552SDave Lee   dump_options.SetHideRootName(suppress_result);
91db814552SDave Lee 
925f45a87bSAugusto Noronha   bool is_po = m_varobj_options.use_objc;
935f45a87bSAugusto Noronha 
94db814552SDave Lee   StackFrame *frame = m_exe_ctx.GetFramePtr();
95d160873cSDave Lee 
9635fa46a5SAdrian Prantl   // Either the language was explicitly specified, or we check the frame.
975f45a87bSAugusto Noronha   lldb::LanguageType language = m_expr_options.language;
985f45a87bSAugusto Noronha   if (language == lldb::eLanguageTypeUnknown && frame)
99975eca0eSAdrian Prantl     language = frame->GuessLanguage().AsLanguageType();
1005f45a87bSAugusto Noronha 
1015f45a87bSAugusto Noronha   // Add a hint if object description was requested, but no description
1025f45a87bSAugusto Noronha   // function was implemented.
1035f45a87bSAugusto Noronha   auto maybe_add_hint = [&](llvm::StringRef output) {
1045f45a87bSAugusto Noronha     // Identify the default output of object description for Swift and
1055f45a87bSAugusto Noronha     // Objective-C
1065f45a87bSAugusto Noronha     // "<Name: 0x...>. The regex is:
1075f45a87bSAugusto Noronha     // - Start with "<".
1085f45a87bSAugusto Noronha     // - Followed by 1 or more non-whitespace characters.
1095f45a87bSAugusto Noronha     // - Followed by ": 0x".
1105f45a87bSAugusto Noronha     // - Followed by 5 or more hex digits.
1115f45a87bSAugusto Noronha     // - Followed by ">".
1125f45a87bSAugusto Noronha     // - End with zero or more whitespace characters.
1135f45a87bSAugusto Noronha     const std::regex swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");
1145f45a87bSAugusto Noronha 
1155f45a87bSAugusto Noronha     if (GetDebugger().GetShowDontUsePoHint() && target_ptr &&
1165f45a87bSAugusto Noronha         (language == lldb::eLanguageTypeSwift ||
1175f45a87bSAugusto Noronha          language == lldb::eLanguageTypeObjC) &&
1185f45a87bSAugusto Noronha         std::regex_match(output.data(), swift_class_regex)) {
1195f45a87bSAugusto Noronha 
1205f45a87bSAugusto Noronha       static bool note_shown = false;
1215f45a87bSAugusto Noronha       if (note_shown)
1225f45a87bSAugusto Noronha         return;
1235f45a87bSAugusto Noronha 
12479178ca6SJonas Devlieghere       result.AppendNote(
12579178ca6SJonas Devlieghere           "object description requested, but type doesn't implement "
1265f45a87bSAugusto Noronha           "a custom object description. Consider using \"p\" instead of "
12779178ca6SJonas Devlieghere           "\"po\" (this note will only be shown once per debug session).\n");
1285f45a87bSAugusto Noronha       note_shown = true;
1295f45a87bSAugusto Noronha     }
1305f45a87bSAugusto Noronha   };
1315f45a87bSAugusto Noronha 
13265d444b9SFelipe de Azevedo Piovezan   // Dump `valobj` according to whether `po` was requested or not.
13365d444b9SFelipe de Azevedo Piovezan   auto dump_val_object = [&](ValueObject &valobj) {
13465d444b9SFelipe de Azevedo Piovezan     if (is_po) {
13565d444b9SFelipe de Azevedo Piovezan       StreamString temp_result_stream;
136d1bc75c0SAdrian Prantl       if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) {
137d1bc75c0SAdrian Prantl         result.AppendError(toString(std::move(error)));
138d1bc75c0SAdrian Prantl         return;
139d1bc75c0SAdrian Prantl       }
14065d444b9SFelipe de Azevedo Piovezan       llvm::StringRef output = temp_result_stream.GetString();
14165d444b9SFelipe de Azevedo Piovezan       maybe_add_hint(output);
14265d444b9SFelipe de Azevedo Piovezan       result.GetOutputStream() << output;
14365d444b9SFelipe de Azevedo Piovezan     } else {
1449473e162SAdrian Prantl       llvm::Error error =
1459473e162SAdrian Prantl         valobj.Dump(result.GetOutputStream(), dump_options);
1469473e162SAdrian Prantl       if (error) {
147d1bc75c0SAdrian Prantl         result.AppendError(toString(std::move(error)));
1489473e162SAdrian Prantl         return;
14965d444b9SFelipe de Azevedo Piovezan       }
1509473e162SAdrian Prantl     }
1519473e162SAdrian Prantl     result.SetStatus(eReturnStatusSuccessFinishResult);
15265d444b9SFelipe de Azevedo Piovezan   };
15365d444b9SFelipe de Azevedo Piovezan 
154*1250a1dbSDave Lee   // First, try `expr` as a _limited_ frame variable expression path: only the
155*1250a1dbSDave Lee   // dot operator (`.`) is permitted for this case.
156*1250a1dbSDave Lee   //
157*1250a1dbSDave Lee   // This is limited to support only unambiguous expression paths. Of note,
158*1250a1dbSDave Lee   // expression paths are not attempted if the expression contain either the
159*1250a1dbSDave Lee   // arrow operator (`->`) or the subscript operator (`[]`). This is because
160*1250a1dbSDave Lee   // both operators can be overloaded in C++, and could result in ambiguity in
161*1250a1dbSDave Lee   // how the expression is handled. Additionally, `*` and `&` are not supported.
162*1250a1dbSDave Lee   const bool try_variable_path =
163*1250a1dbSDave Lee       expr.find_first_of("*&->[]") == StringRef::npos;
164*1250a1dbSDave Lee   if (frame && try_variable_path) {
165*1250a1dbSDave Lee     VariableSP var_sp;
166*1250a1dbSDave Lee     Status status;
167*1250a1dbSDave Lee     auto valobj_sp = frame->GetValueForVariableExpressionPath(
168*1250a1dbSDave Lee         expr, eval_options.GetUseDynamic(),
169*1250a1dbSDave Lee         StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp,
170*1250a1dbSDave Lee         status);
171*1250a1dbSDave Lee     if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
172db814552SDave Lee       if (!suppress_result) {
173581ac50dSAugusto Noronha         if (auto persisted_valobj = valobj_sp->Persist())
174581ac50dSAugusto Noronha           valobj_sp = persisted_valobj;
175581ac50dSAugusto Noronha       }
17663c77bf7SDave Lee 
177d160873cSDave Lee       if (verbosity == eDWIMPrintVerbosityFull) {
178d160873cSDave Lee         StringRef flags;
179d160873cSDave Lee         if (args.HasArgs())
180d160873cSDave Lee           flags = args.GetArgString();
18179178ca6SJonas Devlieghere         result.AppendNoteWithFormatv("ran `frame variable {0}{1}`", flags,
18279178ca6SJonas Devlieghere                                      expr);
183d160873cSDave Lee       }
18463c77bf7SDave Lee 
18565d444b9SFelipe de Azevedo Piovezan       dump_val_object(*valobj_sp);
18692d8a28cSPete Lawrence       return;
187185d4964SDave Lee     }
188185d4964SDave Lee   }
189185d4964SDave Lee 
1904da2b542SDave Lee   // Second, try `expr` as a persistent variable.
1914da2b542SDave Lee   if (expr.starts_with("$"))
1924da2b542SDave Lee     if (auto *state = target.GetPersistentExpressionStateForLanguage(language))
1934da2b542SDave Lee       if (auto var_sp = state->GetVariable(expr))
1944da2b542SDave Lee         if (auto valobj_sp = var_sp->GetValueObject()) {
19565d444b9SFelipe de Azevedo Piovezan           dump_val_object(*valobj_sp);
1964da2b542SDave Lee           return;
1974da2b542SDave Lee         }
1984da2b542SDave Lee 
1994da2b542SDave Lee   // Third, and lastly, try `expr` as a source expression to evaluate.
200185d4964SDave Lee   {
201cebb87e7SDave Lee     auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
202185d4964SDave Lee     ValueObjectSP valobj_sp;
2038e2bd05cSPete Lawrence     std::string fixed_expression;
2048e2bd05cSPete Lawrence 
2058e2bd05cSPete Lawrence     ExpressionResults expr_result = target.EvaluateExpression(
2068e2bd05cSPete Lawrence         expr, exe_scope, valobj_sp, eval_options, &fixed_expression);
2078e2bd05cSPete Lawrence 
208089227feSAdrian Prantl     // Record the position of the expression in the command.
209089227feSAdrian Prantl     std::optional<uint16_t> indent;
210089227feSAdrian Prantl     if (fixed_expression.empty()) {
2119eddc8b9SAdrian Prantl       size_t pos = m_original_command.rfind(expr);
212089227feSAdrian Prantl       if (pos != llvm::StringRef::npos)
213089227feSAdrian Prantl         indent = pos;
214089227feSAdrian Prantl     }
215089227feSAdrian Prantl     // Previously the indent was set up for diagnosing command line
216089227feSAdrian Prantl     // parsing errors. Now point it to the expression.
217089227feSAdrian Prantl     result.SetDiagnosticIndent(indent);
218089227feSAdrian Prantl 
2198e2bd05cSPete Lawrence     // Only mention Fix-Its if the expression evaluator applied them.
2208e2bd05cSPete Lawrence     // Compiler errors refer to the final expression after applying Fix-It(s).
2218e2bd05cSPete Lawrence     if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {
2228e2bd05cSPete Lawrence       Stream &error_stream = result.GetErrorStream();
2238e2bd05cSPete Lawrence       error_stream << "  Evaluated this expression after applying Fix-It(s):\n";
2248e2bd05cSPete Lawrence       error_stream << "    " << fixed_expression << "\n";
2258e2bd05cSPete Lawrence     }
2268e2bd05cSPete Lawrence 
2279473e162SAdrian Prantl     // If the expression failed, return an error.
2289473e162SAdrian Prantl     if (expr_result != eExpressionCompleted) {
2299473e162SAdrian Prantl       if (valobj_sp)
2308789c966SAdrian Prantl         result.SetError(valobj_sp->GetError().Clone());
2319473e162SAdrian Prantl       else
2329473e162SAdrian Prantl         result.AppendErrorWithFormatv(
2339473e162SAdrian Prantl             "unknown error evaluating expression `{0}`", expr);
2349473e162SAdrian Prantl       return;
2359473e162SAdrian Prantl     }
2369473e162SAdrian Prantl 
237d160873cSDave Lee     if (verbosity != eDWIMPrintVerbosityNone) {
238d160873cSDave Lee       StringRef flags;
239d160873cSDave Lee       if (args.HasArgs())
240d160873cSDave Lee         flags = args.GetArgStringWithDelimiter();
24179178ca6SJonas Devlieghere       result.AppendNoteWithFormatv("ran `expression {0}{1}`", flags, expr);
242d160873cSDave Lee     }
24363c77bf7SDave Lee 
24465d444b9SFelipe de Azevedo Piovezan     if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)
24565d444b9SFelipe de Azevedo Piovezan       dump_val_object(*valobj_sp);
2469473e162SAdrian Prantl     else
2472bd21b26SDave Lee       result.SetStatus(eReturnStatusSuccessFinishNoResult);
248db814552SDave Lee 
249db814552SDave Lee     if (suppress_result)
250db814552SDave Lee       if (auto result_var_sp =
251db814552SDave Lee               target.GetPersistentVariable(valobj_sp->GetName())) {
252db814552SDave Lee         auto language = valobj_sp->GetPreferredDisplayLanguage();
253db814552SDave Lee         if (auto *persistent_state =
254db814552SDave Lee                 target.GetPersistentExpressionStateForLanguage(language))
255db814552SDave Lee           persistent_state->RemovePersistentVariable(result_var_sp);
256db814552SDave Lee       }
257185d4964SDave Lee   }
258185d4964SDave Lee }
259