[v2,13/24] Make "print" and "compile print" support -OPT options

Message ID 20190530195333.20448-14-palves@redhat.com
State Superseded
Headers show
Series
  • gdb::option framework, "print -OPT", other cmd options
Related show

Commit Message

Pedro Alves May 30, 2019, 7:53 p.m.
This patch adds support for "print -option optval --", etc.
Likewise for "compile print".

We'll get:

~~~~~~
(gdb) help print
Print value of expression EXP.
Usage: print [[OPTION]... --] [/FMT] [EXP]

Options:
  -address [on|off]
    Set printing of addresses.

  -array [on|off]
    Set pretty formatting of arrays.

  -array-indexes [on|off]
    Set printing of array indexes.

  -elements NUMBER|unlimited
    Set limit on string chars or array elements to print.
    "unlimited" causes there to be no limit.

  -max-depth NUMBER|unlimited
    Set maximum print depth for nested structures, unions and arrays.
    When structures, unions, or arrays are nested beyond this depth then they
    will be replaced with either '{...}' or '(...)' depending on the language.
    Use "unlimited" to print the complete structure.

-null-stop [on|off]
    Set printing of char arrays to stop at first null char.

  -object [on|off]
    Set printing of C++ virtual function tables.

  -pretty [on|off]
    Set pretty formatting of structures.

  -repeats NUMBER|unlimited
    Set threshold for repeated print elements.
    "unlimited" causes all elements to be individually printed.

  -static-members [on|off]
    Set printing of C++ static members.

  -symbol [on|off]
    Set printing of symbol names when printing pointers.

  -union [on|off]
    Set printing of unions interior to structures.

  -vtbl [on|off]
    Set printing of C++ virtual function tables.

Note: because this command accepts arbitrary expressions, if you
specify any command option, you must use a double dash ("--")
to mark the end of option processing.  E.g.: "print -o -- myobj".
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I want to highlight the comment above about "--".

At first, I thought we could make the print command parse the options,
and if the option wasn't recognized, fallback to parsing as an
expression.  Then, if the user wanted to disambiguate, he'd use the
"--" option delimiter.  For example, if you had a variable called
"object" and you wanted to print its negative, you'd have to do:

  (gdb) print -- -object

After getting that working, I saw that gdb.pascal/floats.exp
regressed, in these tests:

 gdb_test "print -r" " = -1\\.2(499.*|5|500.*)"
 gdb_test "print -(r)" " = -1.2(499.*|5|500.*)"
 gdb_test "print -(r + s)" " = -3\\.4(499.*|5|500.*)"

It's the first one that I found most concerning.  It regressed because
"-r" is the abbreviation of "-raw".  I realized then that the behavior
change was a bit risker than I'd like, considering scripts, wrappers
around gdb, etc., and even user expectation.  So instead, I made the
print command _require_ the "--" options delimiter if you want to
specify any option.  So:

  (gdb) print -r

is parsed as an expression, and

  (gdb) print -r --

is parsed as an option.

I noticed that that's also what lldb's expr (the equivalent of print)
does to handle the same problem.

Going back the options themselves, note that:

 - you can shorten option names, as long as unambiguous.
 - For boolean options, 0/1 stand for off/on.
 - For boolean options, "true" is implied.

So these are all equivalent:

 (gdb) print -object on -static-members off -pretty on -- foo
 (gdb) print -object -static-members off -pretty -- foo
 (gdb) print -object -static-members 0 -pretty -- foo
 (gdb) print -o -st 0 -p -- foo

TAB completion is fully supported:

  (gdb) p -[TAB]
  -address         -elements        -pretty          -symbol
  -array           -null-stop       -repeats         -union
  -array-indexes   -object          -static-members  -vtbl

Note that the code is organized such that some of the options and the
"set/show" commands code is shared.  In particular, the "print"
options and the corresponding "set print" commands are defined with
the same structures.  The commands are installed with the
gdb::option::add_setshow_cmds_for_options function.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* compile/compile.c: Include "cli/cli-option.h".
	(compile_print_value): Scope data pointer is now a
	value_print_options pointer; adjust.
	(compile_print_command): Process options.  Scope data pointer is
	now a value_print_options pointer; adjust.
	(_initialize_compile): Update "compile print"'s help to include
	supported options.  Install a completer for "compile print".
	* cp-valprint.c (show_vtblprint, show_objectprint)
	(show_static_field_print): Delete.
	(_initialize_cp_valprint): Don't install "set print
	static-members", "set print vtbl", "set print object" here.
	* printcmd.c: Include "cli/cli-option.h" and
	"common/gdb_optional.h".
	(print_command_parse_format): Rework to fill in a
	value_print_options instead of a format_data.
	(print_value): Change parameter type from format_data pointer to
	value_print_options reference.  Adjust.
	(print_command_1): Process options.  Adjust to pass down a
	value_print_options.
	(print_command_completer): New.
	(_initialize_printcmd): Install print_command_completer as
	handle_brkchars completer for the "print" command.  Update
	"print"'s help to include supported options.
	* valprint.c: Include "cli/cli-option.h".
	(show_vtblprint, show_objectprint, show_static_field_print): Moved
	here from cp-valprint.c.
	(boolean_option_def, uinteger_option_def)
	(value_print_option_defs, make_value_print_options_def_group):
	New.  Use gdb::option::add_setshow_cmds_for_options to install
	"set print elements", "set print null-stop", "set print repeats",
	"set print pretty", "set print union", "set print array", "set
	print address", "set print symbol", "set print array-indexes".
	* valprint.h: Include <string> and "cli/cli-option.h".
	(make_value_print_options_def_group): Declare.
	(print_value): Change parameter type from format_data pointer to
	value_print_options reference.
	(print_command_completer): Declare.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp: Build executable.
	(test-print): New procedure.
	(top level): Call it, once for "print" and another for "compile
	print".
---
 gdb/compile/compile.c              |  46 +++++--
 gdb/cp-valprint.c                  |  57 --------
 gdb/printcmd.c                     |  94 +++++++++-----
 gdb/testsuite/gdb.base/options.exp | 122 +++++++++++++++++
 gdb/valprint.c                     | 259 +++++++++++++++++++++++++------------
 gdb/valprint.h                     |  20 ++-
 6 files changed, 416 insertions(+), 182 deletions(-)

-- 
2.14.5

Comments

Tom Tromey June 3, 2019, 7:20 p.m. | #1
>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:


Pedro> This patch adds support for "print -option optval --", etc.
Pedro> Likewise for "compile print".

Pedro> We'll get:

Pedro> (gdb) help print
Pedro> Print value of expression EXP.
Pedro> Usage: print [[OPTION]... --] [/FMT] [EXP]

Pedro> Options:
Pedro>   -address [on|off]
Pedro>     Set printing of addresses.

Pedro>   -array [on|off]
Pedro>     Set pretty formatting of arrays.
[...]
Pedro> -null-stop [on|off]
Pedro>     Set printing of char arrays to stop at first null char.

I assume -null-stop is out-dented due to a cut-and-paste error with the
email, but I wanted to double-check that there isn't a bug here.


Pedro> I want to highlight the comment above about "--".
[...]

I think this approach makes sense, and it addresses the one thing that
made me hesitate about this idea.

Tom
Pedro Alves June 4, 2019, 9:59 p.m. | #2
On 6/3/19 8:20 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

> 

> Pedro> This patch adds support for "print -option optval --", etc.

> Pedro> Likewise for "compile print".

> 

> Pedro> We'll get:

> 

> Pedro> (gdb) help print

> Pedro> Print value of expression EXP.

> Pedro> Usage: print [[OPTION]... --] [/FMT] [EXP]

> 

> Pedro> Options:

> Pedro>   -address [on|off]

> Pedro>     Set printing of addresses.

> 

> Pedro>   -array [on|off]

> Pedro>     Set pretty formatting of arrays.

> [...]

> Pedro> -null-stop [on|off]

> Pedro>     Set printing of char arrays to stop at first null char.

> 

> I assume -null-stop is out-dented due to a cut-and-paste error with the

> email, but I wanted to double-check that there isn't a bug here.


Indeed, there's no bug here.  Noticed this in the v1 submission but forgot
to fix it for v2...  Sorry about that.

> 

> 

> Pedro> I want to highlight the comment above about "--".

> [...]

> 

> I think this approach makes sense, and it addresses the one thing that

> made me hesitate about this idea.


Awesome, that's great to hear.

Thanks,
Pedro Alves

Patch

diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index 72920642d16..6693809cf4f 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -23,6 +23,7 @@ 
 #include "command.h"
 #include "cli/cli-script.h"
 #include "cli/cli-utils.h"
+#include "cli/cli-option.h"
 #include "completer.h"
 #include "gdbcmd.h"
 #include "compile.h"
@@ -328,9 +329,9 @@  compile_code_command (const char *arg, int from_tty)
 void
 compile_print_value (struct value *val, void *data_voidp)
 {
-  const struct format_data *fmtp = (const struct format_data *) data_voidp;
+  const value_print_options *print_opts = (value_print_options *) data_voidp;
 
-  print_value (val, fmtp);
+  print_value (val, *print_opts);
 }
 
 /* Handle the input from the 'compile print' command.  The "compile
@@ -342,22 +343,30 @@  static void
 compile_print_command (const char *arg, int from_tty)
 {
   enum compile_i_scope_types scope = COMPILE_I_PRINT_ADDRESS_SCOPE;
-  struct format_data fmt;
+  value_print_options print_opts;
 
   scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
-  /* Passing &FMT as SCOPE_DATA is safe as do_module_cleanup will not
-     touch the stale pointer if compile_object_run has already quit.  */
-  print_command_parse_format (&arg, "compile print", &fmt);
+  get_user_print_options (&print_opts);
+  /* Override global settings with explicit options, if any.  */
+  auto group = make_value_print_options_def_group (&print_opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group);
+
+  print_command_parse_format (&arg, "compile print", &print_opts);
+
+  /* Passing &PRINT_OPTS as SCOPE_DATA is safe as do_module_cleanup
+     will not touch the stale pointer if compile_object_run has
+     already quit.  */
 
   if (arg && *arg)
-    eval_compile_command (NULL, arg, scope, &fmt);
+    eval_compile_command (NULL, arg, scope, &print_opts);
   else
     {
       counted_command_line l = get_command_line (compile_control, "");
 
       l->control_u.compile.scope = scope;
-      l->control_u.compile.scope_data = &fmt;
+      l->control_u.compile.scope_data = &print_opts;
       execute_control_command_untraced (l.get ());
     }
 }
@@ -946,11 +955,19 @@  Usage: compile file [-r|-raw] [FILENAME]\n\
 	       &compile_command_list);
   set_cmd_completer (c, filename_completer);
 
-  add_cmd ("print", class_obscure, compile_print_command,
-	   _("\
+  const auto compile_print_opts = make_value_print_options_def_group (nullptr);
+
+  static const std::string compile_print_help
+    = gdb::option::build_help (N_("\
 Evaluate EXPR by using the compiler and print result.\n\
 \n\
-Usage: compile print[/FMT] [EXPR]\n\
+Usage: compile print [[OPTION]... --] [/FMT] [EXPR]\n\
+\n\
+Options:\n\
+%OPTIONS%\
+Note: because this command accepts arbitrary expressions, if you\n\
+specify any command option, you must use a double dash (\"--\")\n\
+to mark the end of option processing.  E.g.: \"compile print -o -- myobj\".\n\
 \n\
 The expression may be specified on the same line as the command, e.g.:\n\
 \n\
@@ -963,7 +980,12 @@  indicate the end of the expression.\n\
 \n\
 EXPR may be preceded with /FMT, where FMT is a format letter\n\
 but no count or size letter (see \"x\" command)."),
-	   &compile_command_list);
+			       compile_print_opts);
+
+  c = add_cmd ("print", class_obscure, compile_print_command,
+	       compile_print_help.c_str (),
+	       &compile_command_list);
+  set_cmd_completer_handle_brkchars (c, print_command_completer);
 
   add_setshow_boolean_cmd ("compile", class_maintenance, &compile_debug, _("\
 Set compile command debugging."), _("\
diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index d8d5c06fd1e..5781d7ab561 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -37,39 +37,6 @@ 
 #include "typeprint.h"
 #include "common/byte-vector.h"
 
-/* Controls printing of vtbl's.  */
-static void
-show_vtblprint (struct ui_file *file, int from_tty,
-		struct cmd_list_element *c, const char *value)
-{
-  fprintf_filtered (file, _("\
-Printing of C++ virtual function tables is %s.\n"),
-		    value);
-}
-
-/* Controls looking up an object's derived type using what we find in
-   its vtables.  */
-static void
-show_objectprint (struct ui_file *file, int from_tty,
-		  struct cmd_list_element *c,
-		  const char *value)
-{
-  fprintf_filtered (file, _("\
-Printing of object's derived type based on vtable info is %s.\n"),
-		    value);
-}
-
-static void
-show_static_field_print (struct ui_file *file, int from_tty,
-			 struct cmd_list_element *c,
-			 const char *value)
-{
-  fprintf_filtered (file,
-		    _("Printing of C++ static members is %s.\n"),
-		    value);
-}
-
-
 static struct obstack dont_print_vb_obstack;
 static struct obstack dont_print_statmem_obstack;
 static struct obstack dont_print_stat_array_obstack;
@@ -821,30 +788,6 @@  cp_print_class_member (const gdb_byte *valaddr, struct type *type,
 void
 _initialize_cp_valprint (void)
 {
-  add_setshow_boolean_cmd ("static-members", class_support,
-			   &user_print_options.static_field_print, _("\
-Set printing of C++ static members."), _("\
-Show printing of C++ static members."), NULL,
-			   NULL,
-			   show_static_field_print,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("vtbl", class_support,
-			   &user_print_options.vtblprint, _("\
-Set printing of C++ virtual function tables."), _("\
-Show printing of C++ virtual function tables."), NULL,
-			   NULL,
-			   show_vtblprint,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("object", class_support,
-			   &user_print_options.objectprint, _("\
-Set printing of object's derived type based on vtable info."), _("\
-Show printing of object's derived type based on vtable info."), NULL,
-			   NULL,
-			   show_objectprint,
-			   &setprintlist, &showprintlist);
-
   obstack_begin (&dont_print_stat_array_obstack,
 		 32 * sizeof (struct type *));
   obstack_begin (&dont_print_statmem_obstack,
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 9e84594fe68..0509360581e 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -45,11 +45,13 @@ 
 #include "charset.h"
 #include "arch-utils.h"
 #include "cli/cli-utils.h"
+#include "cli/cli-option.h"
 #include "cli/cli-script.h"
 #include "cli/cli-style.h"
 #include "common/format.h"
 #include "source.h"
 #include "common/byte-vector.h"
+#include "common/gdb_optional.h"
 
 /* Last specified output format.  */
 
@@ -1117,40 +1119,41 @@  validate_format (struct format_data fmt, const char *cmdname)
 	   fmt.format, cmdname);
 }
 
-/* Parse print command format string into *FMTP and update *EXPP.
+/* Parse print command format string into *OPTS and update *EXPP.
    CMDNAME should name the current command.  */
 
 void
 print_command_parse_format (const char **expp, const char *cmdname,
-			    struct format_data *fmtp)
+			    value_print_options *opts)
 {
   const char *exp = *expp;
 
   if (exp && *exp == '/')
     {
+      format_data fmt;
+
       exp++;
-      *fmtp = decode_format (&exp, last_format, 0);
-      validate_format (*fmtp, cmdname);
-      last_format = fmtp->format;
+      fmt = decode_format (&exp, last_format, 0);
+      validate_format (fmt, cmdname);
+      last_format = fmt.format;
+
+      opts->format = fmt.format;
+      opts->raw = fmt.raw;
     }
   else
     {
-      fmtp->count = 1;
-      fmtp->format = 0;
-      fmtp->size = 0;
-      fmtp->raw = 0;
+      opts->format = 0;
+      opts->raw = 0;
     }
 
   *expp = exp;
 }
 
-/* Print VAL to console according to *FMTP, including recording it to
-   the history.  */
+/* See valprint.h.  */
 
 void
-print_value (struct value *val, const struct format_data *fmtp)
+print_value (value *val, const value_print_options &opts)
 {
-  struct value_print_options opts;
   int histindex = record_latest_value (val);
 
   annotate_value_history_begin (histindex, value_type (val));
@@ -1159,28 +1162,31 @@  print_value (struct value *val, const struct format_data *fmtp)
 
   annotate_value_history_value ();
 
-  get_formatted_print_options (&opts, fmtp->format);
-  opts.raw = fmtp->raw;
-
-  print_formatted (val, fmtp->size, &opts, gdb_stdout);
+  print_formatted (val, 0, &opts, gdb_stdout);
   printf_filtered ("\n");
 
   annotate_value_history_end ();
 }
 
-/* Evaluate string EXP as an expression in the current language and
-   print the resulting value.  EXP may contain a format specifier as the
-   first argument ("/x myvar" for example, to print myvar in hex).  */
+/* Implementation of the "print" and "call" commands.  */
 
 static void
-print_command_1 (const char *exp, int voidprint)
+print_command_1 (const char *args, int voidprint)
 {
   struct value *val;
-  struct format_data fmt;
+  value_print_options print_opts;
+
+  get_user_print_options (&print_opts);
+  /* Override global settings with explicit options, if any.  */
+  auto group = make_value_print_options_def_group (&print_opts);
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group);
+
+  print_command_parse_format (&args, "print", &print_opts);
 
-  print_command_parse_format (&exp, "print", &fmt);
+  const char *exp = args;
 
-  if (exp && *exp)
+  if (exp != nullptr && *exp)
     {
       expression_up expr = parse_expression (exp);
       val = evaluate_expression (expr.get ());
@@ -1190,7 +1196,23 @@  print_command_1 (const char *exp, int voidprint)
 
   if (voidprint || (val && value_type (val) &&
 		    TYPE_CODE (value_type (val)) != TYPE_CODE_VOID))
-    print_value (val, &fmt);
+    print_value (val, print_opts);
+}
+
+/* See valprint.h.  */
+
+void
+print_command_completer (struct cmd_list_element *ignore,
+			 completion_tracker &tracker,
+			 const char *text, const char * /*word*/)
+{
+  const auto group = make_value_print_options_def_group (nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group))
+    return;
+
+  const char *word = advance_to_expression_complete_word_point (tracker, text);
+  expression_completer (ignore, tracker, text, word);
 }
 
 static void
@@ -2761,7 +2783,7 @@  Usage: call EXP\n\
 The argument is the function name and arguments, in the notation of the\n\
 current working language.  The result is printed and saved in the value\n\
 history, if it is not void."));
-  set_cmd_completer (c, expression_completer);
+  set_cmd_completer_handle_brkchars (c, print_command_completer);
 
   add_cmd ("variable", class_vars, set_command, _("\
 Evaluate expression EXP and assign result to variable VAR\n\
@@ -2775,9 +2797,18 @@  This may usually be abbreviated to simply \"set\"."),
 	   &setlist);
   add_alias_cmd ("var", "variable", class_vars, 0, &setlist);
 
-  c = add_com ("print", class_vars, print_command, _("\
+  const auto print_opts = make_value_print_options_def_group (nullptr);
+
+  static const std::string print_help = gdb::option::build_help (N_("\
 Print value of expression EXP.\n\
-Usage: print[/FMT] EXP\n\
+Usage: print [[OPTION]... --] [/FMT] [EXP]\n\
+\n\
+Options:\n\
+%OPTIONS%\
+Note: because this command accepts arbitrary expressions, if you\n\
+specify any command option, you must use a double dash (\"--\")\n\
+to mark the end of option processing.  E.g.: \"print -o -- myobj\".\n\
+\n\
 Variables accessible are those of the lexical environment of the selected\n\
 stack frame, plus all those whose scope is global or an entire file.\n\
 \n\
@@ -2797,8 +2828,11 @@  where FOO is stored, etc.  FOO must be an expression whose value\n\
 resides in memory.\n\
 \n\
 EXP may be preceded with /FMT, where FMT is a format letter\n\
-but no count or size letter (see \"x\" command)."));
-  set_cmd_completer (c, expression_completer);
+but no count or size letter (see \"x\" command)."),
+					      print_opts);
+
+  c = add_com ("print", class_vars, print_command, print_help.c_str ());
+  set_cmd_completer_handle_brkchars (c, print_command_completer);
   add_com_alias ("p", "print", class_vars, 1);
   add_com_alias ("inspect", "print", class_vars, 1);
 
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 1891176dc1d..3b4e7ee189f 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -19,9 +19,18 @@ 
 
 # The test uses the "maintenance test-options" subcommands to exercise
 # TAB-completion and option processing.
+#
+# It also tests option integration in various commands, including
+# "print" and "compile print".
 
 load_lib completion-support.exp
 
+standard_testfile .c
+
+if {[build_executable "failed to prepare" $testfile $srcfile debug]} {
+    return -1
+}
+
 clean_restart
 
 if { ![readline_is_used] } {
@@ -117,6 +126,111 @@  set all_options {
     "-zuinteger-unlimited"
 }
 
+# Basic option-machinery + "print" command integration tests.
+proc_with_prefix test-print {{prefix ""}} {
+    clean_restart
+
+    # Completing "print" with no argument completes on symbols only,
+    # no options are offered.  Since we haven't loaded any symbols,
+    # the match list should be empty.
+    test_gdb_complete_none "${prefix}print "
+
+    # OTOH, completing at "-" should list all options.
+    test_gdb_complete_multiple "${prefix}print " "-" "" {
+	"-address"
+	"-array"
+	"-array-indexes"
+	"-elements"
+	"-max-depth"
+	"-null-stop"
+	"-object"
+	"-pretty"
+	"-repeats"
+	"-static-members"
+	"-symbol"
+	"-union"
+	"-vtbl"
+    }
+
+    global binfile
+    clean_restart $binfile
+
+    if ![runto_main] {
+	fail "cannot run to main"
+	return
+    }
+
+    # Mix options and format.
+    gdb_test "${prefix}print -pretty -- /x 1" " = 0x1"
+
+    # Smoke test that options actually work.
+    gdb_test "${prefix}print -pretty -- g_s" \
+	[multi_line  \
+	     " = {" \
+	     "  a = 1," \
+	     "  b = 2," \
+	     "  c = 3" \
+	     "}"]
+
+    test_gdb_complete_unique \
+	"${prefix}print xxx" \
+	"${prefix}print xxx1"
+    test_gdb_complete_unique \
+	"${prefix}print -- xxx" \
+	"${prefix}print -- xxx1"
+
+    # Error messages when testing with "compile" are different from
+    # the error messages gdb's internal parser throws.  This procedure
+    # hides the difference.  EXPECTED_RE is only considered when not
+    # testing with "compile".
+    proc test_invalid_expression {cmd expected_re} {
+	upvar prefix prefix
+
+	if {$prefix != "compile "} {
+	    gdb_test $cmd $expected_re
+	} else {
+	    # Error messages depend on compiler version, so we just
+	    # look for the last line indicating a failure.
+	    gdb_test $cmd "Compilation failed\\."
+	}
+    }
+
+    # Check that '-XXX' without a "--" is handled as an
+    # expression.
+    gdb_test "${prefix}print -1" " = -1"
+    test_invalid_expression \
+	"${prefix}print --1" \
+	"Left operand of assignment is not an lvalue\\."
+    test_invalid_expression \
+	"${prefix}print -object" \
+	"No symbol \"object\".*"
+
+    # Test printing with options and no expression.
+    set test "${prefix}print -object --"
+    if {$prefix != "compile "} {
+	# Regular "print" repeats the last history value.
+	gdb_test $test " = -1"
+    } else {
+	# "compile print" starts a multiline expression.
+	gdb_test_multiple $test $test {
+	    -re ">$" {
+		gdb_test "-1\nend" " = -1" \
+		    $test
+	    }
+	}
+    }
+
+    # Check that everything after "-- " is treated as an
+    # expression, not confused with an option.
+    test_invalid_expression \
+	"${prefix}print -- -address" \
+	"No symbol.*"
+    gdb_test "${prefix}print -- -1" " = -1"
+    test_invalid_expression \
+	"${prefix}print -- --1" \
+	"Left operand of assignment is not an lvalue\\."
+}
+
 # Miscellaneous tests.
 proc_with_prefix test-misc {variant} {
     global all_options
@@ -552,3 +666,11 @@  foreach_with_prefix cmd {
     }
     test-enum $cmd
 }
+
+# Run the print integration tests.
+test-print ""
+
+# Same for "compile print".
+if ![skip_compile_feature_tests] {
+    test-print "compile "
+}
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 4c3d67a9ff8..e3197e69192 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -36,6 +36,7 @@ 
 #include <ctype.h>
 #include <algorithm>
 #include "common/byte-vector.h"
+#include "cli/cli-option.h"
 
 /* Maximum number of wchars returned from wchar_iterate.  */
 #define MAX_WCHARS 4
@@ -3069,7 +3070,181 @@  show_print_raw (const char *args, int from_tty)
   cmd_show_list (showprintrawlist, from_tty, "");
 }
 
+/* Controls printing of vtbl's.  */
+static void
+show_vtblprint (struct ui_file *file, int from_tty,
+		struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("\
+Printing of C++ virtual function tables is %s.\n"),
+		    value);
+}
+
+/* Controls looking up an object's derived type using what we find in
+   its vtables.  */
+static void
+show_objectprint (struct ui_file *file, int from_tty,
+		  struct cmd_list_element *c,
+		  const char *value)
+{
+  fprintf_filtered (file, _("\
+Printing of object's derived type based on vtable info is %s.\n"),
+		    value);
+}
+
+static void
+show_static_field_print (struct ui_file *file, int from_tty,
+			 struct cmd_list_element *c,
+			 const char *value)
+{
+  fprintf_filtered (file,
+		    _("Printing of C++ static members is %s.\n"),
+		    value);
+}
+
 
+
+/* A couple typedefs to make writing the options a bit more
+   convenient.  */
+using boolean_option_def
+  = gdb::option::boolean_option_def<value_print_options>;
+using uinteger_option_def
+  = gdb::option::uinteger_option_def<value_print_options>;
+using zuinteger_unlimited_option_def
+  = gdb::option::zuinteger_unlimited_option_def<value_print_options>;
+
+/* Definions of options for the "print" and "compile print"
+   commands.  */
+static const gdb::option::option_def value_print_option_defs[] = {
+
+  boolean_option_def {
+    "address",
+    [] (value_print_options *opt) { return &opt->addressprint; },
+    show_addressprint, /* show_cmd_cb */
+    N_("Set printing of addresses."),
+    N_("Show printing of addresses."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "array",
+    [] (value_print_options *opt) { return &opt->prettyformat_arrays; },
+    show_prettyformat_arrays, /* show_cmd_cb */
+    N_("Set pretty formatting of arrays."),
+    N_("Show pretty formatting of arrays."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "array-indexes",
+    [] (value_print_options *opt) { return &opt->print_array_indexes; },
+    show_print_array_indexes, /* show_cmd_cb */
+    N_("Set printing of array indexes."),
+    N_("Show printing of array indexes"),
+    NULL, /* help_doc */
+  },
+
+  uinteger_option_def {
+    "elements",
+    [] (value_print_options *opt) { return &opt->print_max; },
+    show_print_max, /* show_cmd_cb */
+    N_("Set limit on string chars or array elements to print."),
+    N_("Show limit on string chars or array elements to print."),
+    N_("\"unlimited\" causes there to be no limit."),
+  },
+
+  zuinteger_unlimited_option_def {
+    "max-depth",
+    [] (value_print_options *opt) { return &opt->max_depth; },
+    show_print_max_depth, /* show_cmd_cb */
+    N_("Set maximum print depth for nested structures, unions and arrays."),
+    N_("Show maximum print depth for nested structures, unions, and arrays."),
+    N_("When structures, unions, or arrays are nested beyond this depth then they\n\
+will be replaced with either '{...}' or '(...)' depending on the language.\n\
+Use \"unlimited\" to print the complete structure.")
+  },
+
+  boolean_option_def {
+    "null-stop",
+    [] (value_print_options *opt) { return &opt->stop_print_at_null; },
+    show_stop_print_at_null, /* show_cmd_cb */
+    N_("Set printing of char arrays to stop at first null char."),
+    N_("Show printing of char arrays to stop at first null char."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "object",
+    [] (value_print_options *opt) { return &opt->objectprint; },
+    show_objectprint, /* show_cmd_cb */
+    _("Set printing of C++ virtual function tables."),
+    _("Show printing of C++ virtual function tables."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "pretty",
+    [] (value_print_options *opt) { return &opt->prettyformat_structs; },
+    show_prettyformat_structs, /* show_cmd_cb */
+    N_("Set pretty formatting of structures."),
+    N_("Show pretty formatting of structures."),
+    NULL, /* help_doc */
+  },
+
+  uinteger_option_def {
+    "repeats",
+    [] (value_print_options *opt) { return &opt->repeat_count_threshold; },
+    show_repeat_count_threshold, /* show_cmd_cb */
+    N_("Set threshold for repeated print elements."),
+    N_("Show threshold for repeated print elements."),
+    N_("\"unlimited\" causes all elements to be individually printed."),
+  },
+
+  boolean_option_def {
+    "static-members",
+    [] (value_print_options *opt) { return &opt->static_field_print; },
+    show_static_field_print, /* show_cmd_cb */
+    N_("Set printing of C++ static members."),
+    N_("Show printing of C++ static members."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "symbol",
+    [] (value_print_options *opt) { return &opt->symbol_print; },
+    show_symbol_print, /* show_cmd_cb */
+    N_("Set printing of symbol names when printing pointers."),
+    N_("Show printing of symbol names when printing pointers."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "union",
+    [] (value_print_options *opt) { return &opt->unionprint; },
+    show_unionprint, /* show_cmd_cb */
+    N_("Set printing of unions interior to structures."),
+    N_("Show printing of unions interior to structures."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "vtbl",
+    [] (value_print_options *opt) { return &opt->vtblprint; },
+    show_vtblprint, /* show_cmd_cb */
+    N_("Set printing of C++ virtual function tables."),
+    N_("Show printing of C++ virtual function tables."),
+    NULL, /* help_doc */
+  },
+};
+
+/* See valprint.h.  */
+
+gdb::option::option_def_group
+make_value_print_options_def_group (value_print_options *opts)
+{
+  return {{value_print_option_defs}, opts};
+}
+
 void
 _initialize_valprint (void)
 {
@@ -3094,71 +3269,9 @@  Generic command for setting what things to print in \"raw\" mode."),
 		  _("Generic command for showing \"print raw\" settings."),
 		  &showprintrawlist, "show print raw ", 0, &showprintlist);
 
-  add_setshow_uinteger_cmd ("elements", no_class,
-			    &user_print_options.print_max, _("\
-Set limit on string chars or array elements to print."), _("\
-Show limit on string chars or array elements to print."), _("\
-\"set print elements unlimited\" causes there to be no limit."),
-			    NULL,
-			    show_print_max,
-			    &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("null-stop", no_class,
-			   &user_print_options.stop_print_at_null, _("\
-Set printing of char arrays to stop at first null char."), _("\
-Show printing of char arrays to stop at first null char."), NULL,
-			   NULL,
-			   show_stop_print_at_null,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_uinteger_cmd ("repeats", no_class,
-			    &user_print_options.repeat_count_threshold, _("\
-Set threshold for repeated print elements."), _("\
-Show threshold for repeated print elements."), _("\
-\"set print repeats unlimited\" causes all elements to be individually printed."),
-			    NULL,
-			    show_repeat_count_threshold,
-			    &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("pretty", class_support,
-			   &user_print_options.prettyformat_structs, _("\
-Set pretty formatting of structures."), _("\
-Show pretty formatting of structures."), NULL,
-			   NULL,
-			   show_prettyformat_structs,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("union", class_support,
-			   &user_print_options.unionprint, _("\
-Set printing of unions interior to structures."), _("\
-Show printing of unions interior to structures."), NULL,
-			   NULL,
-			   show_unionprint,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("array", class_support,
-			   &user_print_options.prettyformat_arrays, _("\
-Set pretty formatting of arrays."), _("\
-Show pretty formatting of arrays."), NULL,
-			   NULL,
-			   show_prettyformat_arrays,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("address", class_support,
-			   &user_print_options.addressprint, _("\
-Set printing of addresses."), _("\
-Show printing of addresses."), NULL,
-			   NULL,
-			   show_addressprint,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("symbol", class_support,
-			   &user_print_options.symbol_print, _("\
-Set printing of symbol names when printing pointers."), _("\
-Show printing of symbol names when printing pointers."),
-			   NULL, NULL,
-			   show_symbol_print,
-			   &setprintlist, &showprintlist);
+  gdb::option::add_setshow_cmds_for_options
+    (class_support, &user_print_options, value_print_option_defs,
+     &setprintlist, &showprintlist);
 
   add_setshow_zuinteger_cmd ("input-radix", class_support, &input_radix_1,
 			     _("\
@@ -3192,20 +3305,4 @@  Without an argument, sets both radices back to the default value of 10."),
 Show the default input and output number radices.\n\
 Use 'show input-radix' or 'show output-radix' to independently show each."),
 	   &showlist);
-
-  add_setshow_boolean_cmd ("array-indexes", class_support,
-                           &user_print_options.print_array_indexes, _("\
-Set printing of array indexes."), _("\
-Show printing of array indexes"), NULL, NULL, show_print_array_indexes,
-                           &setprintlist, &showprintlist);
-
-  add_setshow_zuinteger_unlimited_cmd ("max-depth", class_support,
-                            &user_print_options.max_depth, _("\
-Set maximum print depth for nested structures, unions and arrays."), _("\
-Show maximum print depth for nested structures, unions, and arrays."), _("\
-When structures, unions, or arrays are nested beyond this depth then they\n\
-will be replaced with either '{...}' or '(...)' depending on the language.\n\
-Use 'set print max-depth unlimited' to print the complete structure."),
-				       NULL, show_print_max_depth,
-				       &setprintlist, &showprintlist);
 }
diff --git a/gdb/valprint.h b/gdb/valprint.h
index 0bd3f1966c4..987c534eaf4 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -20,6 +20,8 @@ 
 #ifndef VALPRINT_H
 #define VALPRINT_H
 
+#include "cli/cli-option.h"
+
 /* This is used to pass formatting options to various value-printing
    functions.  */
 struct value_print_options
@@ -100,6 +102,11 @@  struct value_print_options
   int finish_print;
 };
 
+/* Create an option_def_group for the value_print options, with OPTS
+   as context.  */
+extern gdb::option::option_def_group make_value_print_options_def_group
+  (value_print_options *opts);
+
 /* The global print options set by the user.  In general this should
    not be directly accessed, except by set/show commands.  Ordinary
    code should call get_user_print_options instead.  */
@@ -233,8 +240,17 @@  struct format_data
   };
 
 extern void print_command_parse_format (const char **expp, const char *cmdname,
-					struct format_data *fmtp);
-extern void print_value (struct value *val, const struct format_data *fmtp);
+					value_print_options *opts);
+
+/* Print VAL to console according to OPTS, including recording it to
+   the history.  */
+extern void print_value (value *val, const value_print_options &opts);
+
+/* Completer for the "print", "call", and "compile print"
+   commands.  */
+extern void print_command_completer (struct cmd_list_element *ignore,
+				     completion_tracker &tracker,
+				     const char *text, const char *word);
 
 /* Given an address ADDR return all the elements needed to print the
    address in a symbolic form.  NAME can be mangled or not depending