diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
index 9a53ec0592d..8f2844610b5 100644
--- a/gdb/cli/cli-option.c
+++ b/gdb/cli/cli-option.c
@@ -58,6 +58,8 @@ struct option_def_and_value
   gdb::optional<option_value> value;
 };
 
+static void save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov);
+
 /* Info passed around when handling completion.  */
 struct parse_option_completion_info
 {
@@ -349,11 +351,12 @@ parse_option (gdb::array_view<const option_def_group> options_group,
 	      {
 		complete_on_enum (completion->tracker,
 				  match->enums, *args, *args);
-		*args = after_arg;
+		if (completion->tracker.have_completions ())
+		  return {};
 
-		option_value val;
-		val.enumeration = nullptr;
-		return option_def_and_value {*match, match_ctx, val};
+		/* If we don't have completions, let the
+		   non-completion path throw on invalid enum value
+		   below, so that completion processing stops.  */
 	      }
 	  }
 
@@ -456,6 +459,11 @@ complete_options (completion_tracker &tracker,
 		    (*args - text);
 		  return true;
 		}
+
+	      /* If the caller passed in a context, then it is
+		 interested in the option argument values.  */
+	      if (ov && ov->ctx != nullptr)
+		save_option_value_in_ctx (ov);
 	    }
 	  else
 	    {
@@ -499,6 +507,36 @@ complete_options (completion_tracker &tracker,
   return false;
 }
 
+/* Save the parsed value in the option's context.  */
+
+static void
+save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov)
+{
+  switch (ov->option.type)
+    {
+    case var_boolean:
+      {
+	bool value = ov->value.has_value () ? ov->value->boolean : true;
+	*ov->option.var_address.boolean (ov->option, ov->ctx) = value;
+      }
+      break;
+    case var_uinteger:
+      *ov->option.var_address.uinteger (ov->option, ov->ctx)
+	= ov->value->uinteger;
+      break;
+    case var_zuinteger_unlimited:
+      *ov->option.var_address.integer (ov->option, ov->ctx)
+	= ov->value->integer;
+      break;
+    case var_enum:
+      *ov->option.var_address.enumeration (ov->option, ov->ctx)
+	= ov->value->enumeration;
+      break;
+    default:
+      gdb_assert_not_reached ("unhandled option type");
+    }
+}
+
 /* See cli-option.h.  */
 
 bool
@@ -534,29 +572,7 @@ process_options (const char **args,
 
       processed_any = true;
 
-      switch (ov->option.type)
-	{
-	case var_boolean:
-	  {
-	    bool value = ov->value.has_value () ? ov->value->boolean : true;
-	    *ov->option.var_address.boolean (ov->option, ov->ctx) = value;
-	  }
-	  break;
-	case var_uinteger:
-	  *ov->option.var_address.uinteger (ov->option, ov->ctx)
-	    = ov->value->uinteger;
-	  break;
-	case var_zuinteger_unlimited:
-	  *ov->option.var_address.integer (ov->option, ov->ctx)
-	    = ov->value->integer;
-	  break;
-	case var_enum:
-	  *ov->option.var_address.enumeration (ov->option, ov->ctx)
-	    = ov->value->enumeration;
-	  break;
-	default:
-	  gdb_assert_not_reached ("unhandled option type");
-	}
+      save_option_value_in_ctx (ov);
     }
 }
 
diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index f5d47aeffbc..333a86a81b9 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -60,13 +60,14 @@ get_ulongest (const char **pp, int trailer)
     }
   else
     {
-      retval = strtoulst (p, pp, 0);
-      if (p == *pp)
+      const char *end = p;
+      retval = strtoulst (p, &end, 0);
+      if (p == end)
 	{
 	  /* There is no number here.  (e.g. "cond a == b").  */
 	  error (_("Expected integer at: %s"), p);
 	}
-      p = *pp;
+      p = end;
     }
 
   if (!(isspace (*p) || *p == '\0' || *p == trailer))
diff --git a/gdb/maint-test-options.c b/gdb/maint-test-options.c
index 599155cbfe6..7e7ef6e7992 100644
--- a/gdb/maint-test-options.c
+++ b/gdb/maint-test-options.c
@@ -133,6 +133,27 @@ struct test_options_opts
   const char *enum_opt = test_options_enum_values_xxx;
   unsigned int uint_opt = 0;
   int zuint_unl_opt = 0;
+
+  /* Dump the options to FILE.  ARGS is the remainder unprocessed
+     arguments.  */
+  void dump (ui_file *file, const char *args) const
+  {
+    fprintf_unfiltered (file,
+			_("-flag %d -xx1 %d -xx2 %d -bool %d "
+			  "-enum %s -uint %s -zuint-unl %s -- %s\n"),
+			flag_opt,
+			xx1_opt,
+			xx2_opt,
+			boolean_opt,
+			enum_opt,
+			(uint_opt == UINT_MAX
+			 ? "unlimited"
+			 : pulongest (uint_opt)),
+			(zuint_unl_opt == -1
+			 ? "unlimited"
+			 : plongest (zuint_unl_opt)),
+			args);
+  }
 };
 
 /* Option definitions for the "maintenance test-options" commands.  */
@@ -226,46 +247,48 @@ maintenance_test_options_command_mode (const char *args,
   else
     args = skip_spaces (args);
 
-  printf_unfiltered (_("-flag %d -xx1 %d -xx2 %d -bool %d "
-		       "-enum %s -uint %s -zuint-unl %s -- %s\n"),
-		     opts.flag_opt,
-		     opts.xx1_opt,
-		     opts.xx2_opt,
-		     opts.boolean_opt,
-		     opts.enum_opt,
-		     (opts.uint_opt == UINT_MAX
-		      ? "unlimited"
-		      : pulongest (opts.uint_opt)),
-		     (opts.zuint_unl_opt == -1
-		      ? "unlimited"
-		      : plongest (opts.zuint_unl_opt)),
-		     args);
+  opts.dump (gdb_stdout, args);
 }
 
-/* Variables used by the "maintenance show
-   test-options-completion-result" command.  These variables are
-   stored by the completer of the "maint test-options"
-   subcommands.  */
+/* Variable used by the "maintenance show
+   test-options-completion-result" command.  This variable is stored
+   by the completer of the "maint test-options" subcommands.
 
-/* The result of gdb::option::complete_options.  */
-static int maintenance_test_options_command_completion_result;
-/* The text at the word point after gdb::option::complete_options
-   returns.  */
+   If the completer returned false, this includes the text at the word
+   point after gdb::option::complete_options returns.  If true, then
+   this includes a dump of the processed options.  */
 static std::string maintenance_test_options_command_completion_text;
 
 /* The "maintenance show test-options-completion-result" command.  */
 
 static void
-maintenance_show_test_options_completion_result
-  (struct ui_file *file, int from_tty,
-   struct cmd_list_element *c, const char *value)
+maintenance_show_test_options_completion_result (const char *args,
+						 int from_tty)
 {
-  if (maintenance_test_options_command_completion_result)
-    fprintf_filtered (file, "1\n");
+  puts_filtered (maintenance_test_options_command_completion_text.c_str ());
+}
+
+/* Save the completion result in the global variables read by the
+   "maintenance test-options require-delimiter" command.  */
+
+static void
+save_completion_result (const test_options_opts &opts, bool res,
+			const char *text)
+{
+  if (res)
+    {
+      string_file stream;
+
+      stream.puts ("1 ");
+      opts.dump (&stream, text);
+      maintenance_test_options_command_completion_text
+	= std::move (stream.string ());
+    }
   else
-    fprintf_filtered
-      (file, _("0 %s\n"),
-       maintenance_test_options_command_completion_text.c_str ());
+    {
+      maintenance_test_options_command_completion_text
+	= string_printf ("0 %s\n", text);
+    }
 }
 
 /* Implementation of completer for the "maintenance test-options
@@ -278,17 +301,19 @@ maintenance_test_options_completer_mode (completion_tracker &tracker,
 					 const char *text,
 					 gdb::option::process_options_mode mode)
 {
+  test_options_opts opts;
+
   try
     {
-      maintenance_test_options_command_completion_result
-	= gdb::option::complete_options
-	   (tracker, &text, mode,
-	    make_test_options_options_def_group (nullptr));
-      maintenance_test_options_command_completion_text = text;
+      bool res = (gdb::option::complete_options
+		  (tracker, &text, mode,
+		   make_test_options_options_def_group (&opts)));
+
+      save_completion_result (opts, res, text);
     }
   catch (const gdb_exception_error &ex)
     {
-      maintenance_test_options_command_completion_result = 1;
+      save_completion_result (opts, true, text);
       throw;
     }
 }
@@ -445,17 +470,13 @@ Options:\n\
   set_cmd_completer_handle_brkchars
     (cmd, maintenance_test_options_unknown_is_operand_command_completer);
 
-  add_setshow_zinteger_cmd ("test-options-completion-result", class_maintenance,
-			    &maintenance_test_options_command_completion_result,
-			    _("\
-Set maintenance test-options completion result."), _("\
-Show maintenance test-options completion result."), _("\
-Show the results of completing\n\
+  add_cmd ("test-options-completion-result", class_maintenance,
+	   maintenance_show_test_options_completion_result,
+	   _("\
+Show maintenance test-options completion result.\n\
+Shows the results of completing\n\
 \"maint test-options require-delimiter\",\n\
 \"maint test-options unknown-is-error\", or\n\
 \"maint test-options unknown-is-operand\"."),
-			    NULL,
-			    maintenance_show_test_options_completion_result,
-			    &maintenance_set_cmdlist,
-			    &maintenance_show_cmdlist);
+	   &maintenance_show_cmdlist);
 }
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index f88e6a87d3e..1a652b3c9dc 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -486,21 +486,27 @@ proc_with_prefix test-misc {variant} {
     }
 
     # Completing at "-" should list all options.
-    res_test_gdb_complete_multiple "1" "$cmd " "-" "" $all_options
+    res_test_gdb_complete_multiple \
+	"1 [expect_none "-"]" \
+	"$cmd " "-" "" $all_options
 
     # Now with a double dash.
     gdb_test "$cmd --" [expect_none ""]
 
     # "--" is recognized by options completer, gdb auto-appends a
     # space.
-    test_completer_recognizes 1 "$cmd --"
+    test_completer_recognizes \
+	"1 [expect_none "--"]" \
+	"$cmd --"
 
     # Now with a double dash, plus a dash as operand.
     gdb_test "$cmd -- -" [expect_none "-"]
     res_test_gdb_complete_none "0 -" "$cmd -- -"
 
     # Completing an unambiguous option just appends an empty space.
-    test_completer_recognizes 1 "$cmd -flag"
+    test_completer_recognizes \
+	"1 [expect_none "-flag"]" \
+	"$cmd -flag"
 
     # Try running an ambiguous option.
     if {$variant == "require-delimiter"} {
@@ -540,10 +546,14 @@ proc_with_prefix test-flag {variant} {
     set cmd [make_cmd $variant]
 
     # Completing a flag just appends a space.
-    test_completer_recognizes 1 "$cmd -flag"
+    test_completer_recognizes \
+	"1 [expect_none "-flag"]" \
+	"$cmd -flag"
 
     # Add a dash, and all options should be shown.
-    test_gdb_complete_multiple "$cmd  -flag " "-" "" $all_options
+    res_test_gdb_complete_multiple \
+	"1 [expect_flag "-"]" \
+	"$cmd  -flag " "-" "" $all_options
 
     # Basic smoke tests of accepted / not accepted values.
 
@@ -582,7 +592,9 @@ proc_with_prefix test-flag {variant} {
     # "on/off".
 
     if {$variant == "require-delimiter"} {
-	res_test_gdb_complete_none "1" "$cmd -flag o"
+	res_test_gdb_complete_none \
+	    "1 [expect_flag "o"]" \
+	    "$cmd -flag o"
 
 	gdb_test "$cmd -flag o" [expect_none "-flag o"]
     } else {
@@ -612,7 +624,9 @@ proc_with_prefix test-boolean {variant} {
     #   E.g., "frame apply all -past-main COMMAND".
 
     if {$variant == "require-delimiter"} {
-	res_test_gdb_complete_multiple 1 "$cmd -bool " "" "" {
+	res_test_gdb_complete_multiple \
+	    "1 [expect_none ""]" \
+	    "$cmd -bool " "" "" {
 	    "-bool"
 	    "-enum"
 	    "-flag"
@@ -628,7 +642,9 @@ proc_with_prefix test-boolean {variant} {
     }
 
     # Add another dash, and "on/off" are no longer offered:
-    res_test_gdb_complete_multiple 1 "$cmd -bool " "-" ""  $all_options
+    res_test_gdb_complete_multiple \
+	"1 [expect_bool "-"]" \
+	"$cmd -bool " "-" ""  $all_options
 
     # Basic smoke tests of accepted / not accepted values.
 
@@ -643,20 +659,25 @@ proc_with_prefix test-boolean {variant} {
     # However, the completer does recognize them if you start typing
     # the boolean value.
     foreach value {"0" "1"} {
-	test_completer_recognizes 1 "$cmd -bool $value"
+	test_completer_recognizes \
+	    "1 [expect_none ""]" \
+	    "$cmd -bool $value"
     }
     foreach value {"of" "off"} {
-	res_test_gdb_complete_unique 1 \
+	res_test_gdb_complete_unique \
+	    "1 [expect_none ""]" \
 	    "$cmd -bool $value" \
 	    "$cmd -bool off"
     }
     foreach value {"y" "ye" "yes"} {
-	res_test_gdb_complete_unique 1 \
+	res_test_gdb_complete_unique \
+	    "1 [expect_none ""]" \
 	    "$cmd -bool $value" \
 	    "$cmd -bool yes"
     }
     foreach value {"n" "no"} {
-	res_test_gdb_complete_unique 1 \
+	res_test_gdb_complete_unique \
+	    "1 [expect_none ""]" \
 	    "$cmd -bool $value" \
 	    "$cmd -bool no"
     }
@@ -668,7 +689,8 @@ proc_with_prefix test-boolean {variant} {
 	"enabl"
 	"enable"
     } {
-	res_test_gdb_complete_unique 1 \
+	res_test_gdb_complete_unique \
+	    "1 [expect_none ""]" \
 	    "$cmd -bool $value" \
 	    "$cmd -bool enable"
     }
@@ -681,13 +703,16 @@ proc_with_prefix test-boolean {variant} {
 	"disabl"
 	"disable"
     } {
-	res_test_gdb_complete_unique 1 \
+	res_test_gdb_complete_unique \
+	    "1 [expect_none ""]" \
 	    "$cmd -bool $value" \
 	    "$cmd -bool disable"
     }
 
     if {$variant == "require-delimiter"} {
-	res_test_gdb_complete_none "1" "$cmd -bool xxx"
+	res_test_gdb_complete_none \
+	    "1 [expect_none "xxx"]" \
+	    "$cmd -bool xxx"
     } else {
 	res_test_gdb_complete_none "0 xxx" "$cmd -bool xxx"
     }
@@ -763,7 +788,9 @@ proc_with_prefix test-boolean {variant} {
     # Completing after a boolean option + "o" does list "on/off",
     # though.
     if {$variant == "require-delimiter"} {
-	res_test_gdb_complete_multiple 1 "$cmd -bool " "o" "" {
+	res_test_gdb_complete_multiple \
+	    "1 [expect_none "o"]" \
+	    "$cmd -bool " "o" "" {
 	    "off"
 	    "on"
 	}
@@ -783,17 +810,22 @@ proc_with_prefix test-uinteger {variant option} {
     set cmd "[make_cmd $variant] -$option"
 
     # Test completing a uinteger option:
-    res_test_gdb_complete_multiple 1 "$cmd " "" "" {
+    res_test_gdb_complete_multiple \
+	"1 [expect_none ""]" \
+	"$cmd " "" "" {
 	"NUMBER"
 	"unlimited"
     }
 
     # NUMBER above is just a placeholder, make sure we don't complete
     # it as a valid option.
-    res_test_gdb_complete_none 1 "$cmd NU"
+    res_test_gdb_complete_none \
+	"1 [expect_none "NU"]" \
+	"$cmd NU"
 
     # "unlimited" is valid though.
-    res_test_gdb_complete_unique 1 \
+    res_test_gdb_complete_unique \
+	"1 [expect_none "u"]" \
 	"$cmd u" \
 	"$cmd unlimited"
 
@@ -815,24 +847,34 @@ proc_with_prefix test-uinteger {variant option} {
 
     # Don't offer completions until we're past the
     # -uinteger/-zuinteger-unlimited argument.
-    res_test_gdb_complete_none 1 "$cmd 1"
+    res_test_gdb_complete_none \
+	"1 [expect_none ""]" \
+	"$cmd 1"
 
     # A number of invalid values.
     foreach value {"x" "x " "1a" "1a " "1-" "1- " "unlimitedx"} {
-	res_test_gdb_complete_none 1 "$cmd $value"
+	res_test_gdb_complete_none \
+	    "1 [expect_none $value]" \
+	    "$cmd $value"
     }
 
     # Try "-1".
     if {$option == "uinteger"} {
 	# -1 is invalid uinteger.
 	foreach value {"-1" "-1 "} {
-	    res_test_gdb_complete_none 1 "$cmd $value"
+	    res_test_gdb_complete_none \
+		"1 [expect_none ""]" \
+		"$cmd $value"
 	}
     } else {
 	# -1 is valid for zuinteger-unlimited.
-	res_test_gdb_complete_none 1 "$cmd -1"
+	res_test_gdb_complete_none \
+	    "1 [expect_none ""]" \
+	    "$cmd -1"
 	if {$variant == "require-delimiter"} {
-	    res_test_gdb_complete_multiple 1 "$cmd -1 " "" "-" $all_options
+	    res_test_gdb_complete_multiple \
+		"1 [expect_integer $option "unlimited" ""]" \
+		"$cmd -1 " "" "-" $all_options
 	} else {
 	    res_test_gdb_complete_none "0 " "$cmd -1 "
 	}
@@ -846,7 +888,9 @@ proc_with_prefix test-uinteger {variant option} {
     #  - for !require-delimiter commands, completion offers nothing
     #    and returns false.
     if {$variant == "require-delimiter"} {
-	res_test_gdb_complete_multiple 1 "$cmd 1 " "" "-" $all_options
+	res_test_gdb_complete_multiple \
+	    "1 [expect_integer $option 1 ""]" \
+	    "$cmd 1 " "" "-" $all_options
     } else {
 	res_test_gdb_complete_none "0 " "$cmd 1 "
     }
@@ -854,7 +898,9 @@ proc_with_prefix test-uinteger {variant option} {
     # Test completing non-option arguments after "-uinteger 1 ".
     foreach operand {"x" "x " "1a" "1a " "1-" "1- "} {
 	if {$variant == "require-delimiter"} {
-	    res_test_gdb_complete_none 1 "$cmd 1 $operand"
+	    res_test_gdb_complete_none \
+		"1 [expect_integer $option 1 $operand]" \
+		"$cmd 1 $operand"
 	} else {
 	    res_test_gdb_complete_none "0 $operand" "$cmd 1 $operand"
 	}
@@ -864,7 +910,9 @@ proc_with_prefix test-uinteger {variant option} {
 	if {$variant == "unknown-is-operand"} {
 	    res_test_gdb_complete_none "0 $operand" "$cmd 1 $operand"
 	} else {
-	    res_test_gdb_complete_none 1 "$cmd 1 $operand"
+	    res_test_gdb_complete_none \
+		"1 [expect_integer $option 1 $operand]" \
+		"$cmd 1 $operand"
 	}
     }
 }
@@ -873,7 +921,9 @@ proc_with_prefix test-uinteger {variant option} {
 proc_with_prefix test-enum {variant} {
     set cmd [make_cmd $variant]
 
-    res_test_gdb_complete_multiple 1 "$cmd -enum " "" "" {
+    res_test_gdb_complete_multiple \
+	"1 [expect_none ""]" \
+	"$cmd -enum " "" "" {
 	"xxx"
 	"yyy"
 	"zzz"
@@ -882,7 +932,9 @@ proc_with_prefix test-enum {variant} {
     # Check that "-" where a value is expected does not show the
     # command's options.  I.e., an enum's value is not optional.
     # Check both completion and running the command.
-    res_test_gdb_complete_none 1 "$cmd -enum -"
+    res_test_gdb_complete_none \
+	"1 [expect_none "-"]" \
+	"$cmd -enum -"
     gdb_test "$cmd -enum --"\
 	"Requires an argument. Valid arguments are xxx, yyy, zzz\\."
 
