CommandObjectExpression.cpp revision 309124
1//===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10// C Includes
11// C++ Includes
12// Other libraries and framework includes
13#include "llvm/ADT/STLExtras.h"
14#include "llvm/ADT/StringRef.h"
15
16// Project includes
17#include "CommandObjectExpression.h"
18#include "lldb/Core/Value.h"
19#include "lldb/Core/ValueObjectVariable.h"
20#include "lldb/DataFormatters/ValueObjectPrinter.h"
21#include "Plugins/ExpressionParser/Clang/ClangExpressionVariable.h"
22#include "lldb/Expression/UserExpression.h"
23#include "lldb/Expression/DWARFExpression.h"
24#include "lldb/Expression/REPL.h"
25#include "lldb/Host/Host.h"
26#include "lldb/Host/StringConvert.h"
27#include "lldb/Core/Debugger.h"
28#include "lldb/Interpreter/CommandInterpreter.h"
29#include "lldb/Interpreter/CommandReturnObject.h"
30#include "lldb/Target/Language.h"
31#include "lldb/Symbol/ObjectFile.h"
32#include "lldb/Symbol/Variable.h"
33#include "lldb/Target/Process.h"
34#include "lldb/Target/StackFrame.h"
35#include "lldb/Target/Target.h"
36#include "lldb/Target/Thread.h"
37
38using namespace lldb;
39using namespace lldb_private;
40
41CommandObjectExpression::CommandOptions::CommandOptions () :
42    OptionGroup()
43{
44}
45
46CommandObjectExpression::CommandOptions::~CommandOptions() = default;
47
48static OptionEnumValueElement g_description_verbosity_type[] =
49{
50    { eLanguageRuntimeDescriptionDisplayVerbosityCompact,      "compact",       "Only show the description string"},
51    { eLanguageRuntimeDescriptionDisplayVerbosityFull,         "full",          "Show the full output, including persistent variable's name and type"},
52    { 0, nullptr, nullptr }
53};
54
55OptionDefinition
56CommandObjectExpression::CommandOptions::g_option_table[] =
57{
58    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads",        'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean,    "Should we run all threads if the execution doesn't complete on one thread."},
59    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean,    "Ignore breakpoint hits while running expressions"},
60    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout",            't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger,  "Timeout value (in microseconds) for running the expression."},
61    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error",    'u', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean,    "Clean up program state if the expression causes a crash, or raises a signal.  Note, unlike gdb hitting a breakpoint is controlled by another option (-i)."},
62    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "debug",              'g', OptionParser::eNoArgument      , nullptr, nullptr, 0, eArgTypeNone,       "When specified, debug the JIT code by setting a breakpoint on the first instruction and forcing breakpoints to not be ignored (-i0) and no unwinding to happen on error (-u0)."},
63    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "language",           'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage,   "Specifies the Language to use when parsing the expression.  If not set the target.language setting is used." },
64    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "apply-fixits",       'X', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLanguage,   "If true, simple fix-it hints will be automatically applied to the expression." },
65    { LLDB_OPT_SET_1, false, "description-verbosity", 'v', OptionParser::eOptionalArgument, nullptr, g_description_verbosity_type, 0, eArgTypeDescriptionVerbosity,        "How verbose should the output of this expression be, if the object description is asked for."},
66    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "top-level",          'p', OptionParser::eNoArgument      , NULL, NULL, 0, eArgTypeNone,       "Interpret the expression as top-level definitions rather than code to be immediately executed."},
67    { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "allow-jit",          'j', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean,    "Controls whether the expression can fall back to being JITted if it's not supported by the interpreter (defaults to true)."}
68};
69
70uint32_t
71CommandObjectExpression::CommandOptions::GetNumDefinitions ()
72{
73    return llvm::array_lengthof(g_option_table);
74}
75
76Error
77CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &interpreter,
78                                                         uint32_t option_idx,
79                                                         const char *option_arg)
80{
81    Error error;
82
83    const int short_option = g_option_table[option_idx].short_option;
84
85    switch (short_option)
86    {
87    case 'l':
88        language = Language::GetLanguageTypeFromString (option_arg);
89        if (language == eLanguageTypeUnknown)
90            error.SetErrorStringWithFormat ("unknown language type: '%s' for expression", option_arg);
91        break;
92
93    case 'a':
94        {
95            bool success;
96            bool result;
97            result = Args::StringToBoolean(option_arg, true, &success);
98            if (!success)
99                error.SetErrorStringWithFormat("invalid all-threads value setting: \"%s\"", option_arg);
100            else
101                try_all_threads = result;
102        }
103        break;
104
105    case 'i':
106        {
107            bool success;
108            bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
109            if (success)
110                ignore_breakpoints = tmp_value;
111            else
112                error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
113            break;
114        }
115
116    case 'j':
117        {
118            bool success;
119            bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
120            if (success)
121                allow_jit = tmp_value;
122            else
123                error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
124            break;
125        }
126
127    case 't':
128        {
129            bool success;
130            uint32_t result;
131            result = StringConvert::ToUInt32(option_arg, 0, 0, &success);
132            if (success)
133                timeout = result;
134            else
135                error.SetErrorStringWithFormat ("invalid timeout setting \"%s\"", option_arg);
136        }
137        break;
138
139    case 'u':
140        {
141            bool success;
142            bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
143            if (success)
144                unwind_on_error = tmp_value;
145            else
146                error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
147            break;
148        }
149
150    case 'v':
151        if (!option_arg)
152        {
153            m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityFull;
154            break;
155        }
156        m_verbosity = (LanguageRuntimeDescriptionDisplayVerbosity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error);
157        if (!error.Success())
158            error.SetErrorStringWithFormat ("unrecognized value for description-verbosity '%s'", option_arg);
159        break;
160
161    case 'g':
162        debug = true;
163        unwind_on_error = false;
164        ignore_breakpoints = false;
165        break;
166
167    case 'p':
168        top_level = true;
169        break;
170
171    case 'X':
172        {
173            bool success;
174            bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
175            if (success)
176                auto_apply_fixits = tmp_value ? eLazyBoolYes : eLazyBoolNo;
177            else
178                error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
179            break;
180        }
181
182    default:
183        error.SetErrorStringWithFormat("invalid short option character '%c'", short_option);
184        break;
185    }
186
187    return error;
188}
189
190void
191CommandObjectExpression::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter)
192{
193    Process *process = interpreter.GetExecutionContext().GetProcessPtr();
194    if (process != nullptr)
195    {
196        ignore_breakpoints = process->GetIgnoreBreakpointsInExpressions();
197        unwind_on_error    = process->GetUnwindOnErrorInExpressions();
198    }
199    else
200    {
201        ignore_breakpoints = true;
202        unwind_on_error = true;
203    }
204
205    show_summary = true;
206    try_all_threads = true;
207    timeout = 0;
208    debug = false;
209    language = eLanguageTypeUnknown;
210    m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityCompact;
211    auto_apply_fixits = eLazyBoolCalculate;
212    top_level = false;
213    allow_jit = true;
214}
215
216const OptionDefinition*
217CommandObjectExpression::CommandOptions::GetDefinitions ()
218{
219    return g_option_table;
220}
221
222CommandObjectExpression::CommandObjectExpression(CommandInterpreter &interpreter)
223    : CommandObjectRaw(
224          interpreter, "expression",
225          "Evaluate an expression on the current thread.  Displays any returned value with LLDB's default formatting.",
226          nullptr, eCommandProcessMustBePaused | eCommandTryTargetAPILock),
227      IOHandlerDelegate(IOHandlerDelegate::Completion::Expression),
228      m_option_group(interpreter),
229      m_format_options(eFormatDefault),
230      m_repl_option(LLDB_OPT_SET_1, false, "repl", 'r', "Drop into REPL", false, true),
231      m_command_options(),
232      m_expr_line_count(0),
233      m_expr_lines()
234{
235    SetHelpLong(
236R"(
237Timeouts:
238
239)" "    If the expression can be evaluated statically (without running code) then it will be.  \
240Otherwise, by default the expression will run on the current thread with a short timeout: \
241currently .25 seconds.  If it doesn't return in that time, the evaluation will be interrupted \
242and resumed with all threads running.  You can use the -a option to disable retrying on all \
243threads.  You can use the -t option to set a shorter timeout." R"(
244
245User defined variables:
246
247)" "    You can define your own variables for convenience or to be used in subsequent expressions.  \
248You define them the same way you would define variables in C.  If the first character of \
249your user defined variable is a $, then the variable's value will be available in future \
250expressions, otherwise it will just be available in the current expression." R"(
251
252Continuing evaluation after a breakpoint:
253
254)" "    If the \"-i false\" option is used, and execution is interrupted by a breakpoint hit, once \
255you are done with your investigation, you can either remove the expression execution frames \
256from the stack with \"thread return -x\" or if you are still interested in the expression result \
257you can issue the \"continue\" command and the expression evaluation will complete and the \
258expression result will be available using the \"thread.completed-expression\" key in the thread \
259format." R"(
260
261Examples:
262
263    expr my_struct->a = my_array[3]
264    expr -f bin -- (index * 8) + 5
265    expr unsigned int $foo = 5
266    expr char c[] = \"foo\"; c[0])"
267    );
268
269    CommandArgumentEntry arg;
270    CommandArgumentData expression_arg;
271
272    // Define the first (and only) variant of this arg.
273    expression_arg.arg_type = eArgTypeExpression;
274    expression_arg.arg_repetition = eArgRepeatPlain;
275
276    // There is only one variant this argument could be; put it into the argument entry.
277    arg.push_back (expression_arg);
278
279    // Push the data for the first argument into the m_arguments vector.
280    m_arguments.push_back (arg);
281
282    // Add the "--format" and "--gdb-format"
283    m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1);
284    m_option_group.Append (&m_command_options);
285    m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
286    m_option_group.Append (&m_repl_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3);
287    m_option_group.Finalize();
288}
289
290CommandObjectExpression::~CommandObjectExpression() = default;
291
292Options *
293CommandObjectExpression::GetOptions ()
294{
295    return &m_option_group;
296}
297
298static lldb_private::Error
299CanBeUsedForElementCountPrinting (ValueObject& valobj)
300{
301    CompilerType type(valobj.GetCompilerType());
302    CompilerType pointee;
303    if (!type.IsPointerType(&pointee))
304        return Error("as it does not refer to a pointer");
305    if (pointee.IsVoidType())
306        return Error("as it refers to a pointer to void");
307    return Error();
308}
309
310bool
311CommandObjectExpression::EvaluateExpression(const char *expr,
312                                            Stream *output_stream,
313                                            Stream *error_stream,
314                                            CommandReturnObject *result)
315{
316    // Don't use m_exe_ctx as this might be called asynchronously
317    // after the command object DoExecute has finished when doing
318    // multi-line expression that use an input reader...
319    ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
320
321    Target *target = exe_ctx.GetTargetPtr();
322
323    if (!target)
324        target = GetDummyTarget();
325
326    if (target)
327    {
328        lldb::ValueObjectSP result_valobj_sp;
329        bool keep_in_memory = true;
330        StackFrame *frame = exe_ctx.GetFramePtr();
331
332        EvaluateExpressionOptions options;
333        options.SetCoerceToId(m_varobj_options.use_objc);
334        options.SetUnwindOnError(m_command_options.unwind_on_error);
335        options.SetIgnoreBreakpoints (m_command_options.ignore_breakpoints);
336        options.SetKeepInMemory(keep_in_memory);
337        options.SetUseDynamic(m_varobj_options.use_dynamic);
338        options.SetTryAllThreads(m_command_options.try_all_threads);
339        options.SetDebug(m_command_options.debug);
340        options.SetLanguage(m_command_options.language);
341        options.SetExecutionPolicy(m_command_options.allow_jit ?
342            EvaluateExpressionOptions::default_execution_policy :
343            lldb_private::eExecutionPolicyNever);
344
345        bool auto_apply_fixits;
346        if (m_command_options.auto_apply_fixits == eLazyBoolCalculate)
347            auto_apply_fixits = target->GetEnableAutoApplyFixIts();
348        else
349            auto_apply_fixits = m_command_options.auto_apply_fixits == eLazyBoolYes ? true : false;
350
351        options.SetAutoApplyFixIts(auto_apply_fixits);
352
353        if (m_command_options.top_level)
354            options.SetExecutionPolicy(eExecutionPolicyTopLevel);
355
356        // If there is any chance we are going to stop and want to see
357        // what went wrong with our expression, we should generate debug info
358        if (!m_command_options.ignore_breakpoints ||
359            !m_command_options.unwind_on_error)
360            options.SetGenerateDebugInfo(true);
361
362        if (m_command_options.timeout > 0)
363            options.SetTimeoutUsec(m_command_options.timeout);
364        else
365            options.SetTimeoutUsec(0);
366
367        ExpressionResults success = target->EvaluateExpression(expr, frame, result_valobj_sp, options, &m_fixed_expression);
368
369        // We only tell you about the FixIt if we applied it.  The compiler errors will suggest the FixIt if it parsed.
370        if (error_stream && !m_fixed_expression.empty() && target->GetEnableNotifyAboutFixIts())
371        {
372            if (success == eExpressionCompleted)
373                error_stream->Printf ("  Fix-it applied, fixed expression was: \n    %s\n", m_fixed_expression.c_str());
374        }
375
376        if (result_valobj_sp)
377        {
378            Format format = m_format_options.GetFormat();
379
380            if (result_valobj_sp->GetError().Success())
381            {
382                if (format != eFormatVoid)
383                {
384                    if (format != eFormatDefault)
385                        result_valobj_sp->SetFormat (format);
386
387                    if (m_varobj_options.elem_count > 0)
388                    {
389                        Error error(CanBeUsedForElementCountPrinting(*result_valobj_sp));
390                        if (error.Fail())
391                        {
392                            result->AppendErrorWithFormat("expression cannot be used with --element-count %s\n", error.AsCString(""));
393                            result->SetStatus(eReturnStatusFailed);
394                            return false;
395                        }
396                    }
397
398                    DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(m_command_options.m_verbosity,format));
399                    options.SetVariableFormatDisplayLanguage(result_valobj_sp->GetPreferredDisplayLanguage());
400
401                    result_valobj_sp->Dump(*output_stream,options);
402
403                    if (result)
404                        result->SetStatus (eReturnStatusSuccessFinishResult);
405                }
406            }
407            else
408            {
409                if (result_valobj_sp->GetError().GetError() == UserExpression::kNoResult)
410                {
411                    if (format != eFormatVoid && m_interpreter.GetDebugger().GetNotifyVoid())
412                    {
413                        error_stream->PutCString("(void)\n");
414                    }
415
416                    if (result)
417                        result->SetStatus (eReturnStatusSuccessFinishResult);
418                }
419                else
420                {
421                    const char *error_cstr = result_valobj_sp->GetError().AsCString();
422                    if (error_cstr && error_cstr[0])
423                    {
424                        const size_t error_cstr_len = strlen (error_cstr);
425                        const bool ends_with_newline = error_cstr[error_cstr_len - 1] == '\n';
426                        if (strstr(error_cstr, "error:") != error_cstr)
427                            error_stream->PutCString ("error: ");
428                        error_stream->Write(error_cstr, error_cstr_len);
429                        if (!ends_with_newline)
430                            error_stream->EOL();
431                    }
432                    else
433                    {
434                        error_stream->PutCString ("error: unknown error\n");
435                    }
436
437                    if (result)
438                        result->SetStatus (eReturnStatusFailed);
439                }
440            }
441        }
442    }
443    else
444    {
445        error_stream->Printf ("error: invalid execution context for expression\n");
446        return false;
447    }
448
449    return true;
450}
451
452void
453CommandObjectExpression::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
454{
455    io_handler.SetIsDone(true);
456//    StreamSP output_stream = io_handler.GetDebugger().GetAsyncOutputStream();
457//    StreamSP error_stream = io_handler.GetDebugger().GetAsyncErrorStream();
458    StreamFileSP output_sp(io_handler.GetOutputStreamFile());
459    StreamFileSP error_sp(io_handler.GetErrorStreamFile());
460
461    EvaluateExpression (line.c_str(),
462                        output_sp.get(),
463                        error_sp.get());
464    if (output_sp)
465        output_sp->Flush();
466    if (error_sp)
467        error_sp->Flush();
468}
469
470bool
471CommandObjectExpression::IOHandlerIsInputComplete (IOHandler &io_handler,
472                                                   StringList &lines)
473{
474    // An empty lines is used to indicate the end of input
475    const size_t num_lines = lines.GetSize();
476    if (num_lines > 0 && lines[num_lines - 1].empty())
477    {
478        // Remove the last empty line from "lines" so it doesn't appear
479        // in our resulting input and return true to indicate we are done
480        // getting lines
481        lines.PopBack();
482        return true;
483    }
484    return false;
485}
486
487void
488CommandObjectExpression::GetMultilineExpression ()
489{
490    m_expr_lines.clear();
491    m_expr_line_count = 0;
492
493    Debugger &debugger = GetCommandInterpreter().GetDebugger();
494    bool color_prompt = debugger.GetUseColor();
495    const bool multiple_lines = true; // Get multiple lines
496    IOHandlerSP io_handler_sp(new IOHandlerEditline(debugger,
497                                                    IOHandler::Type::Expression,
498                                                    "lldb-expr",      // Name of input reader for history
499                                                    nullptr,          // No prompt
500                                                    nullptr,          // Continuation prompt
501                                                    multiple_lines,
502                                                    color_prompt,
503                                                    1,                // Show line numbers starting at 1
504                                                    *this));
505
506    StreamFileSP output_sp(io_handler_sp->GetOutputStreamFile());
507    if (output_sp)
508    {
509        output_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n");
510        output_sp->Flush();
511    }
512    debugger.PushIOHandler(io_handler_sp);
513}
514
515bool
516CommandObjectExpression::DoExecute(const char *command,
517                                   CommandReturnObject &result)
518{
519    m_fixed_expression.clear();
520    m_option_group.NotifyOptionParsingStarting();
521
522    const char * expr = nullptr;
523
524    if (command[0] == '\0')
525    {
526        GetMultilineExpression ();
527        return result.Succeeded();
528    }
529
530    if (command[0] == '-')
531    {
532        // We have some options and these options MUST end with --.
533        const char *end_options = nullptr;
534        const char *s = command;
535        while (s && s[0])
536        {
537            end_options = ::strstr (s, "--");
538            if (end_options)
539            {
540                end_options += 2; // Get past the "--"
541                if (::isspace (end_options[0]))
542                {
543                    expr = end_options;
544                    while (::isspace (*expr))
545                        ++expr;
546                    break;
547                }
548            }
549            s = end_options;
550        }
551
552        if (end_options)
553        {
554            Args args (llvm::StringRef(command, end_options - command));
555            if (!ParseOptions (args, result))
556                return false;
557
558            Error error (m_option_group.NotifyOptionParsingFinished());
559            if (error.Fail())
560            {
561                result.AppendError (error.AsCString());
562                result.SetStatus (eReturnStatusFailed);
563                return false;
564            }
565
566            if (m_repl_option.GetOptionValue().GetCurrentValue())
567            {
568                Target *target = m_interpreter.GetExecutionContext().GetTargetPtr();
569                if (target)
570                {
571                    // Drop into REPL
572                    m_expr_lines.clear();
573                    m_expr_line_count = 0;
574
575                    Debugger &debugger = target->GetDebugger();
576
577                    // Check if the LLDB command interpreter is sitting on top of a REPL that
578                    // launched it...
579                    if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::CommandInterpreter, IOHandler::Type::REPL))
580                    {
581                        // the LLDB command interpreter is sitting on top of a REPL that launched it,
582                        // so just say the command interpreter is done and fall back to the existing REPL
583                        m_interpreter.GetIOHandler(false)->SetIsDone(true);
584                    }
585                    else
586                    {
587                        // We are launching the REPL on top of the current LLDB command interpreter,
588                        // so just push one
589                        bool initialize = false;
590                        Error repl_error;
591                        REPLSP repl_sp (target->GetREPL(repl_error, m_command_options.language, nullptr, false));
592
593                        if (!repl_sp)
594                        {
595                            initialize = true;
596                            repl_sp = target->GetREPL(repl_error, m_command_options.language, nullptr, true);
597                            if (!repl_error.Success())
598                            {
599                                result.SetError(repl_error);
600                                return result.Succeeded();
601                            }
602                        }
603
604                        if (repl_sp)
605                        {
606                            if (initialize)
607                            {
608                                repl_sp->SetCommandOptions(m_command_options);
609                                repl_sp->SetFormatOptions(m_format_options);
610                                repl_sp->SetValueObjectDisplayOptions(m_varobj_options);
611                            }
612
613                            IOHandlerSP io_handler_sp (repl_sp->GetIOHandler());
614
615                            io_handler_sp->SetIsDone(false);
616
617                            debugger.PushIOHandler(io_handler_sp);
618                        }
619                        else
620                        {
621                            repl_error.SetErrorStringWithFormat("Couldn't create a REPL for %s", Language::GetNameForLanguageType(m_command_options.language));
622                            result.SetError(repl_error);
623                            return result.Succeeded();
624                        }
625                    }
626                }
627            }
628            // No expression following options
629            else if (expr == nullptr || expr[0] == '\0')
630            {
631                GetMultilineExpression ();
632                return result.Succeeded();
633            }
634        }
635    }
636
637    if (expr == nullptr)
638        expr = command;
639
640    if (EvaluateExpression (expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result))
641    {
642        Target *target = m_interpreter.GetExecutionContext().GetTargetPtr();
643        if (!m_fixed_expression.empty() && target->GetEnableNotifyAboutFixIts())
644        {
645            CommandHistory &history = m_interpreter.GetCommandHistory();
646            // FIXME: Can we figure out what the user actually typed (e.g. some alias for expr???)
647            // If we can it would be nice to show that.
648            std::string fixed_command("expression ");
649            if (expr == command)
650                fixed_command.append(m_fixed_expression);
651            else
652            {
653                // Add in any options that might have been in the original command:
654                fixed_command.append(command, expr - command);
655                fixed_command.append(m_fixed_expression);
656            }
657            history.AppendString(fixed_command);
658        }
659        return true;
660    }
661
662    result.SetStatus (eReturnStatusFailed);
663    return false;
664}
665