This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH] PR gdb/17960 - Move completion_tracker_t into the completion API.
- From: Doug Evans <xdje42 at gmail dot com>
- To: Keith Seitz <keiths at redhat dot com>
- Cc: gdb-patches at sourceware dot org
- Date: Sun, 22 Feb 2015 16:26:12 -0800
- Subject: Re: [PATCH] PR gdb/17960 - Move completion_tracker_t into the completion API.
- Authentication-results: sourceware.org; auth=none
- References: <20150216204053 dot 7430 dot 38377 dot stgit at valrhona dot uglyboxes dot com>
Keith Seitz <keiths@redhat.com> writes:
> The bug itself is easily demonstrated:
>
> $ gdb -nx -q gdb
> (gdb) b gdb.c:ma<TAB>
> ./../src/gdb/completer.c:837: internal-error: maybe_add_completion:
> Assertion `tracker != NULL' failed.
> A problem internal to GDB has been detected,
> further debugging may prove unreliable.
> Quit this debugging session? (y or n)
Yeah.
Just didn't think of this case. Bleah.
High level thought.
A good next step after this patch would be to replace the "tracker"
argument we now pass around with a more general completion state
struct pointer.
Then we could, I think, get rid of the "return_val" global in symtab.c.
I haven't worked through the details so there may be some gotchas.
This doesn't have to be done as part of this patch, but OTOH now's
a good time to do this.
Further comments are prefixed with ====.
>
> This occurs because the code path in this case is:
> complete_line
> - complete_line_internal
> -- location_completer
> --- make_file_symbol_completion_list
> (...)
> ---- completion_list_add_name
> ----- maybe_add_completion
> ----> gdb_assert (tracker != NULL);
>
> The tracker in maybe_add_completion is a static global in symtab.c called
> completion_tracker. It is initialized by
> default_make_symbol_completion_list_break_on_1 (which is a defaulted
> language vector method). In this case, this function is never called
> and completion_tracker is left NULL/uninitialized.
>
> This patch refactors the completion API, adding a completion_tracker_t
> to the list of function arguments for all completion functions, removing
> the (file) global completion_tracker in symtab.c that is the cause of this bug.
>
> gdb/ChangeLog
>
> PR gdb/17960
> * cli/cli-decode.c (find_cmd): Use maybe_add_completion to
> implement completion tracking.
> * command.h (completer_ftype, completer_ftype_void,
> set_cmd_completer, set_cmd_completer_handle_brkchars,
> lookup_cmd, lookup_cmd_1, complete_on_cmdlist,
> complete_on_enum): Move to completer.h.
> * completer.c (location_completer): Use TRY_CATCH to catch
> max completion exception from make_file_symbol_completion_list,
> make_symbol_completion_list, and make_source_files_completion_list.
> (complete_line_internal): Empty the tracker when a unique
> command completion is found.
> (complete_line): Instantiate a new tracker earlier and pass this
> to complete_line_internal.
> If the list of completions is bigger than those tracked,
> completion tracking was not implemented for the requested completion;
> re-filter the list for maximum completions. Otherwise simply
> copy the result.
> * completer.h (max_completions, new_completion_tracker,
> make_cleanup_free_completion_tracker, enum maybe_add_completion_enum,
> maybe_add_completion, throw_max_completions_reached_error): Move
> declarations to earlier in file.
> (completer_ftype, completer_ftype_void, set_cmd_completer,
> set_cmd_completer_handle_brkchars, lookup_cmd, lookup_cmd_1,
> complete_on_cmdlist, complete_on_enum): Move declarations here
> from command.h.
> * symtab.c (COMPLETION_LIST_ADD_SYMBOL,
> MCOMPLETION_LIST_ADD_SYMBOL): Add `tracker' as first argument.
> All users updated.
> (struct add_name_data): Add `tracker' member.
> (default_make_symbol_completion_list_break_on_1): Add tracker
> to call data.
> Do not instantiate a completion tracker here -- use the one
> passed instead.
> (add_filename_to_list): Implement completion tracking.
> (struct add_partial_filename_data): Add `tracker' member.
> (make_source_files_completion_list): Add tracker to call data.
> * symtab.h: Include completer.h.
> * ada-lang.c, breakpoint.c, cli/cli-cmds.c, cli/cli-decode.h,
> corefile.c, cp-abi.c, f-lang.c, infrun.c, interps.c, language.h,
> python/py-cmd.c, top.c, symtab.c: Updated for completion API change.
====
I've been told that regardless of the tediousness,
one line per file:
* ada-lang.c: Updated for completion API change.
* ... and so on.
You could instead just say "All callers updated."
at the relevant points.
>
> gdb/testsuite/ChangeLog
>
> PR gdb/17960
> * gdb.base/completion.exp: Add location completer tests.
> ---
> gdb/ada-lang.c | 3 -
> gdb/breakpoint.c | 6 +
> gdb/cli/cli-cmds.c | 6 +
> gdb/cli/cli-decode.c | 58 ++++++++---
> gdb/cli/cli-decode.h | 1
> gdb/command.h | 31 ------
> gdb/completer.c | 177 +++++++++++++++++++++++----------
> gdb/completer.h | 168 +++++++++++++++++++------------
> gdb/corefile.c | 3 -
> gdb/cp-abi.c | 3 -
> gdb/f-lang.c | 6 +
> gdb/infrun.c | 5 +
> gdb/interps.c | 1
> gdb/language.h | 3 -
> gdb/python/py-cmd.c | 5 +
> gdb/symtab.c | 166 ++++++++++++++++++-------------
> gdb/symtab.h | 35 +++----
> gdb/testsuite/gdb.base/completion.exp | 78 +++++++++++++++
> gdb/top.c | 2
> 19 files changed, 492 insertions(+), 265 deletions(-)
>
> diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
> index 562627a..161e3aa 100644
> --- a/gdb/ada-lang.c
> +++ b/gdb/ada-lang.c
> @@ -6191,7 +6191,8 @@ ada_complete_symbol_matcher (const char *name, void *user_data)
> the entire command on which completion is made. */
>
> static VEC (char_ptr) *
> -ada_make_symbol_completion_list (const char *text0, const char *word,
> +ada_make_symbol_completion_list (completion_tracker_t tracker,
> + const char *text0, const char *word,
> enum type_code code)
> {
> char *text;
> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index a7cc6cb..2773f53 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -1020,6 +1020,7 @@ set_breakpoint_condition (struct breakpoint *b, char *exp,
>
> static VEC (char_ptr) *
> condition_completer (struct cmd_list_element *cmd,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> const char *space;
> @@ -1058,7 +1059,7 @@ condition_completer (struct cmd_list_element *cmd,
>
> /* We're completing the expression part. */
> text = skip_spaces_const (space);
> - return expression_completer (cmd, text, word);
> + return expression_completer (cmd, tracker, text, word);
> }
>
> /* condition N EXP -- set break condition of breakpoint N to EXP. */
> @@ -15393,11 +15394,12 @@ catching_syscall_number (int syscall_number)
> /* Complete syscall names. Used by "catch syscall". */
> static VEC (char_ptr) *
> catch_syscall_completer (struct cmd_list_element *cmd,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> const char **list = get_syscall_names (get_current_arch ());
> VEC (char_ptr) *retlist
> - = (list == NULL) ? NULL : complete_on_enum (list, word, word);
> + = (list == NULL) ? NULL : complete_on_enum (tracker, list, word, word);
>
> xfree (list);
> return retlist;
> diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
> index e46f036..c2b4604 100644
> --- a/gdb/cli/cli-cmds.c
> +++ b/gdb/cli/cli-cmds.c
> @@ -1344,7 +1344,7 @@ valid_command_p (const char *command)
> {
> struct cmd_list_element *c;
>
> - c = lookup_cmd_1 (& command, cmdlist, NULL, 1);
> + c = lookup_cmd_1 (NULL, &command, cmdlist, NULL, 1);
>
> if (c == NULL || c == (struct cmd_list_element *) -1)
> return FALSE;
> @@ -1467,12 +1467,12 @@ alias_command (char *args, int from_tty)
> alias_prefix = dyn_string_buf (alias_prefix_dyn_string);
> command_prefix = dyn_string_buf (command_prefix_dyn_string);
>
> - c_command = lookup_cmd_1 (& command_prefix, cmdlist, NULL, 1);
> + c_command = lookup_cmd_1 (NULL, &command_prefix, cmdlist, NULL, 1);
> /* We've already tried to look up COMMAND. */
> gdb_assert (c_command != NULL
> && c_command != (struct cmd_list_element *) -1);
> gdb_assert (c_command->prefixlist != NULL);
> - c_alias = lookup_cmd_1 (& alias_prefix, cmdlist, NULL, 1);
> + c_alias = lookup_cmd_1 (NULL, &alias_prefix, cmdlist, NULL, 1);
> if (c_alias != c_command)
> error (_("ALIAS and COMMAND prefixes do not match."));
>
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index 4ec6ec0..b31772c 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -35,7 +35,8 @@ static struct cmd_list_element *delete_cmd (const char *name,
> struct cmd_list_element **posthook,
> struct cmd_list_element **posthookee);
>
> -static struct cmd_list_element *find_cmd (const char *command,
> +static struct cmd_list_element *find_cmd (completion_tracker_t tracker,
> + const char *command,
> int len,
> struct cmd_list_element *clist,
> int ignore_help_classes,
> @@ -649,6 +650,7 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class class,
>
> static VEC (char_ptr) *
> integer_unlimited_completer (struct cmd_list_element *ignore,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> static const char * const keywords[] =
> @@ -657,7 +659,7 @@ integer_unlimited_completer (struct cmd_list_element *ignore,
> NULL,
> };
>
> - return complete_on_enum (keywords, text, word);
> + return complete_on_enum (tracker, keywords, text, word);
> }
>
> /* Add element named NAME to both the set and show command LISTs (the
> @@ -1215,22 +1217,43 @@ help_cmd_list (struct cmd_list_element *list, enum command_class class,
> found in nfound. */
>
> static struct cmd_list_element *
> -find_cmd (const char *command, int len, struct cmd_list_element *clist,
> +find_cmd (completion_tracker_t tracker,
> + const char *command, int len, struct cmd_list_element *clist,
> int ignore_help_classes, int *nfound)
> {
> struct cmd_list_element *found, *c;
> + int max_reached;
>
> found = (struct cmd_list_element *) NULL;
> *nfound = 0;
> - for (c = clist; c; c = c->next)
> + for (c = clist, max_reached = 0; c; c = c->next)
> if (!strncmp (command, c->name, len)
> && (!ignore_help_classes || c->func))
> {
> - found = c;
> - (*nfound)++;
> - if (c->name[len] == '\0')
> + enum maybe_add_completion_enum add_status;
> +
> + add_status = (tracker == NULL
> + ? MAYBE_ADD_COMPLETION_OK
> + : maybe_add_completion (tracker, c->name));
> + switch (add_status)
> {
> - *nfound = 1;
> + case MAYBE_ADD_COMPLETION_OK:
> + found = c;
> + (*nfound)++;
> + if (c->name[len] == '\0')
> + {
> + *nfound = 1;
> + return found;
> + }
> + break;
> + case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
> + found = c;
> + (*nfound)++;
> + return found;
> + case MAYBE_ADD_COMPLETION_MAX_REACHED:
> + return found;
> + case MAYBE_ADD_COMPLETION_DUPLICATE:
> + continue;
> break;
> }
> }
> @@ -1336,7 +1359,8 @@ valid_user_defined_cmd_name_p (const char *name)
> the struct cmd_list_element is NULL). */
>
> struct cmd_list_element *
> -lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
> +lookup_cmd_1 (completion_tracker_t tracker,
> + const char **text, struct cmd_list_element *clist,
> struct cmd_list_element **result_list, int ignore_help_classes)
> {
> char *command;
> @@ -1365,7 +1389,8 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
> /* Look it up. */
> found = 0;
> nfound = 0;
> - found = find_cmd (command, len, clist, ignore_help_classes, &nfound);
> + found = find_cmd (tracker, command, len, clist, ignore_help_classes,
> + &nfound);
>
> /* We didn't find the command in the entered case, so lower case it
> and search again. */
> @@ -1377,7 +1402,8 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
>
> command[tmp] = isupper (x) ? tolower (x) : x;
> }
> - found = find_cmd (command, len, clist, ignore_help_classes, &nfound);
> + found = find_cmd (tracker, command, len, clist, ignore_help_classes,
> + &nfound);
> }
>
> /* If nothing matches, we have a simple failure. */
> @@ -1414,7 +1440,7 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
>
> if (found->prefixlist)
> {
> - c = lookup_cmd_1 (text, *found->prefixlist, result_list,
> + c = lookup_cmd_1 (tracker, text, *found->prefixlist, result_list,
> ignore_help_classes);
> if (!c)
> {
> @@ -1491,7 +1517,7 @@ lookup_cmd (const char **line, struct cmd_list_element *list, char *cmdtype,
> if (!*line)
> error (_("Lack of needed %scommand"), cmdtype);
>
> - c = lookup_cmd_1 (line, list, &last_list, ignore_help_classes);
> + c = lookup_cmd_1 (NULL, line, list, &last_list, ignore_help_classes);
>
> if (!c)
> {
> @@ -1718,7 +1744,7 @@ lookup_cmd_composition (const char *text,
> /* Look it up. */
> *cmd = 0;
> nfound = 0;
> - *cmd = find_cmd (command, len, cur_list, 1, &nfound);
> + *cmd = find_cmd (NULL, command, len, cur_list, 1, &nfound);
>
> /* We didn't find the command in the entered case, so lower case
> it and search again.
> @@ -1731,7 +1757,7 @@ lookup_cmd_composition (const char *text,
>
> command[tmp] = isupper (x) ? tolower (x) : x;
> }
> - *cmd = find_cmd (command, len, cur_list, 1, &nfound);
> + *cmd = find_cmd (NULL, command, len, cur_list, 1, &nfound);
> }
>
> if (*cmd == CMD_LIST_AMBIGUOUS)
> @@ -1842,7 +1868,7 @@ complete_on_cmdlist (struct cmd_list_element *list,
> "oobar"; if WORD is "baz/foo", return "baz/foobar". */
>
> VEC (char_ptr) *
> -complete_on_enum (const char *const *enumlist,
> +complete_on_enum (completion_tracker_t ignore, const char *const *enumlist,
> const char *text, const char *word)
> {
> VEC (char_ptr) *matchlist = NULL;
> diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
> index ec89325..bbedb52 100644
> --- a/gdb/cli/cli-decode.h
> +++ b/gdb/cli/cli-decode.h
> @@ -23,6 +23,7 @@
>
> /* Include the public interfaces. */
> #include "command.h"
> +#include "completer.h"
>
> struct re_pattern_buffer;
>
> diff --git a/gdb/command.h b/gdb/command.h
> index 956eeaa..35d61bd 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -156,19 +156,6 @@ typedef void cmd_sfunc_ftype (char *args, int from_tty,
> extern void set_cmd_sfunc (struct cmd_list_element *cmd,
> cmd_sfunc_ftype *sfunc);
>
> -typedef VEC (char_ptr) *completer_ftype (struct cmd_list_element *,
> - const char *, const char *);
> -
> -typedef void completer_ftype_void (struct cmd_list_element *,
> - const char *, const char *);
> -
> -extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
> -
> -/* Set the completer_handle_brkchars callback. */
> -
> -extern void set_cmd_completer_handle_brkchars (struct cmd_list_element *,
> - completer_ftype_void *);
> -
> /* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
> around in cmd objects to test the value of the commands sfunc(). */
> extern int cmd_cfunc_eq (struct cmd_list_element *cmd,
> @@ -189,18 +176,6 @@ extern void execute_cmd_post_hook (struct cmd_list_element *cmd);
> /* Return the type of the command. */
> extern enum cmd_types cmd_type (struct cmd_list_element *cmd);
>
> -/* Flag for an ambiguous cmd_list result. */
> -#define CMD_LIST_AMBIGUOUS ((struct cmd_list_element *) -1)
> -
> -extern struct cmd_list_element *lookup_cmd (const char **,
> - struct cmd_list_element *, char *,
> - int, int);
> -
====
Seems odd to remove CMD_LIST_AMBIGUOUS, lookup_cmd, lookup_cmd_1 here.
Can we keep them here, at least for now?
I can imagine the motivation is an inclusion order issue.
If we passed around struct completion_state instead of completion_tracker_t
then we could just forward declare it in command.h.
Another way, of course, if one wanted to just pass tracker, is to change the
type to struct completion_tracker and forward declare that in command.h.
> -extern struct cmd_list_element *lookup_cmd_1 (const char **,
> - struct cmd_list_element *,
> - struct cmd_list_element **,
> - int);
> -
> extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *,
> const char * );
>
> @@ -225,12 +200,6 @@ extern struct cmd_list_element *add_info (const char *,
> extern struct cmd_list_element *add_info_alias (const char *, const char *,
> int);
>
> -extern VEC (char_ptr) *complete_on_cmdlist (struct cmd_list_element *,
> - const char *, const char *, int);
> -
> -extern VEC (char_ptr) *complete_on_enum (const char *const *enumlist,
> - const char *, const char *);
> -
> /* Functions that implement commands about CLI commands. */
>
> extern void help_list (struct cmd_list_element *, const char *,
> diff --git a/gdb/completer.c b/gdb/completer.c
> index add79cc..67644d1 100644
> --- a/gdb/completer.c
> +++ b/gdb/completer.c
> @@ -107,7 +107,8 @@ readline_line_completion_function (const char *text, int matches)
> /* This can be used for functions which don't want to complete on
> symbols but don't want to complete on anything else either. */
> VEC (char_ptr) *
> -noop_completer (struct cmd_list_element *ignore,
> +noop_completer (struct cmd_list_element *ignore,
> + completion_tracker_t ignore2,
> const char *text, const char *prefix)
> {
> return NULL;
> @@ -115,7 +116,8 @@ noop_completer (struct cmd_list_element *ignore,
>
> /* Complete on filenames. */
> VEC (char_ptr) *
> -filename_completer (struct cmd_list_element *ignore,
> +filename_completer (struct cmd_list_element *ignore,
> + completion_tracker_t ignore2,
> const char *text, const char *word)
> {
> int subsequent_name;
> @@ -184,7 +186,8 @@ filename_completer (struct cmd_list_element *ignore,
> etc. */
>
> VEC (char_ptr) *
> -location_completer (struct cmd_list_element *ignore,
> +location_completer (struct cmd_list_element *ignore,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> int n_syms, n_files, ix;
> @@ -260,18 +263,41 @@ location_completer (struct cmd_list_element *ignore,
> symbols as well as on files. */
> if (colon)
> {
> - list = make_file_symbol_completion_list (symbol_start, word,
> - file_to_match);
> + struct gdb_exception e;
> +
> + TRY_CATCH (e, RETURN_MASK_ERROR)
> + {
> + list = make_file_symbol_completion_list (tracker, symbol_start,
> + word, file_to_match);
> + }
====
Why is this now wrapped in a try/catch?
And similarly below.
Is it because MAX_COMPLETIONS_REACHED_ERROR may be thrown?
I'd expect make_file_symbol_completion_list to catch it
and return the list collected thus far.
> + if (e.reason < 0)
> + list = NULL;
> xfree (file_to_match);
> }
> else
> {
> - list = make_symbol_completion_list (symbol_start, word);
> + struct gdb_exception e;
> +
> + TRY_CATCH (e, RETURN_MASK_ERROR)
> + {
> + list = make_symbol_completion_list (tracker, symbol_start, word);
> + }
> + if (e.reason < 0)
> + list = NULL;
> +
> /* If text includes characters which cannot appear in a file
> name, they cannot be asking for completion on files. */
> if (strcspn (text,
> gdb_completer_file_name_break_characters) == text_len)
> - fn_list = make_source_files_completion_list (text, text);
> + {
> + TRY_CATCH (e, RETURN_MASK_ERROR)
> + {
> + fn_list = make_source_files_completion_list (tracker, text,
> + text);
> + }
> + if (e.reason < 0)
> + fn_list = NULL;
> + }
> }
>
> n_syms = VEC_length (char_ptr, list);
> @@ -326,7 +352,14 @@ location_completer (struct cmd_list_element *ignore,
> {
> /* No completions at all. As the final resort, try completing
> on the entire text as a symbol. */
> - list = make_symbol_completion_list (orig_text, word);
> + struct gdb_exception e;
> +
> + TRY_CATCH (e, RETURN_MASK_ERROR)
> + {
> + list = make_symbol_completion_list (tracker, orig_text, word);
> + }
> + if (e.reason < 0)
> + list = NULL;
> }
>
> return list;
> @@ -389,7 +422,8 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
> names, but some language parsers also have support for completing
> field names. */
> VEC (char_ptr) *
> -expression_completer (struct cmd_list_element *ignore,
> +expression_completer (struct cmd_list_element *ignore,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> struct type *type = NULL;
> @@ -434,7 +468,8 @@ expression_completer (struct cmd_list_element *ignore,
> VEC (char_ptr) *result;
> struct cleanup *cleanup = make_cleanup (xfree, fieldname);
>
> - result = make_symbol_completion_type (fieldname, fieldname, code);
> + result = make_symbol_completion_type (tracker, fieldname, fieldname,
> + code);
> do_cleanups (cleanup);
> return result;
> }
> @@ -448,7 +483,7 @@ expression_completer (struct cmd_list_element *ignore,
> ;
>
> /* Not ideal but it is what we used to do before... */
> - return location_completer (ignore, p, word);
> + return location_completer (ignore, tracker, p, word);
> }
>
> /* See definition in completer.h. */
> @@ -526,7 +561,7 @@ complete_line_internal_reason;
> */
>
> static VEC (char_ptr) *
> -complete_line_internal (const char *text,
> +complete_line_internal (completion_tracker_t tracker, const char *text,
> const char *line_buffer, int point,
> complete_line_internal_reason reason)
> {
> @@ -572,7 +607,21 @@ complete_line_internal (const char *text,
> }
> else
> {
> - c = lookup_cmd_1 (&p, cmdlist, &result_list, ignore_help_classes);
> + /* There are two possible scenarios:
> + 1) The user is searching for completions to a command
> + 2) The user is attempting to complete the argument of
> + a specific command.
> +
> + In either case, lookup_cmd_1 will increment the tracker.
> + However, for case #2, the tracker's results must be cleared,
> + since we don't want the actual command lookup to be counted
> + by that command's completer function. If we didn't do this,
> + we'd end up with an off-by-one error between the tracker
> + and the returned completion list. */
> + c = lookup_cmd_1 (tracker, &p, cmdlist, &result_list,
> + ignore_help_classes);
> + if (c != CMD_LIST_AMBIGUOUS && tracker != NULL)
> + htab_empty (tracker);
> }
>
> /* Move p up to the next interesting thing. */
> @@ -658,7 +707,7 @@ complete_line_internal (const char *text,
> else if (c->enums)
> {
> if (reason != handle_brkchars)
> - list = complete_on_enum (c->enums, p, word);
> + list = complete_on_enum (tracker, c->enums, p, word);
> rl_completer_word_break_characters =
> gdb_completer_command_word_break_characters;
> }
> @@ -698,7 +747,7 @@ complete_line_internal (const char *text,
> && c->completer_handle_brkchars != NULL)
> (*c->completer_handle_brkchars) (c, p, word);
> if (reason != handle_brkchars && c->completer != NULL)
> - list = (*c->completer) (c, p, word);
> + list = (*c->completer) (c, tracker, p, word);
> }
> }
> else
> @@ -744,7 +793,7 @@ complete_line_internal (const char *text,
> else if (c->enums)
> {
> if (reason != handle_brkchars)
> - list = complete_on_enum (c->enums, p, word);
> + list = complete_on_enum (tracker, c->enums, p, word);
> }
> else
> {
> @@ -774,7 +823,7 @@ complete_line_internal (const char *text,
> && c->completer_handle_brkchars != NULL)
> (*c->completer_handle_brkchars) (c, p, word);
> if (reason != handle_brkchars && c->completer != NULL)
> - list = (*c->completer) (c, p, word);
> + list = (*c->completer) (c, tracker, p, word);
> }
> }
> }
> @@ -825,7 +874,7 @@ make_cleanup_free_completion_tracker (completion_tracker_t *tracker_ptr)
> /* See completer.h. */
>
> enum maybe_add_completion_enum
> -maybe_add_completion (completion_tracker_t tracker, char *name)
> +maybe_add_completion (completion_tracker_t tracker, const char *name)
> {
> void **slot;
>
> @@ -844,7 +893,7 @@ maybe_add_completion (completion_tracker_t tracker, char *name)
> if (*slot != HTAB_EMPTY_ENTRY)
> return MAYBE_ADD_COMPLETION_DUPLICATE;
>
> - *slot = name;
> + *slot = (void *) name;
>
> return (htab_elements (tracker) < max_completions
> ? MAYBE_ADD_COMPLETION_OK
> @@ -875,52 +924,75 @@ complete_line (const char *text, const char *line_buffer, int point)
> {
> VEC (char_ptr) *list;
> VEC (char_ptr) *result = NULL;
> - struct cleanup *cleanups;
> + struct cleanup *tracker_cleanup, *cleanups;
> completion_tracker_t tracker;
> char *candidate;
> int ix, max_reached;
>
> if (max_completions == 0)
> return NULL;
> - list = complete_line_internal (text, line_buffer, point,
> - handle_completions);
> - if (max_completions < 0)
> - return list;
>
> tracker = new_completion_tracker ();
> cleanups = make_cleanup_free_completion_tracker (&tracker);
> - make_cleanup_free_char_ptr_vec (list);
>
> - /* Do a final test for too many completions. Individual completers may
> - do some of this, but are not required to. Duplicates are also removed
> - here. Otherwise the user is left scratching his/her head: readline and
> - complete_command will remove duplicates, and if removal of duplicates
> - there brings the total under max_completions the user may think gdb quit
> - searching too early. */
> + list = complete_line_internal (tracker, text, line_buffer, point,
> + handle_completions);
>
> - for (ix = 0, max_reached = 0;
> - !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
> - ++ix)
> + if (max_completions < 0)
> {
> - enum maybe_add_completion_enum add_status;
> + do_cleanups (cleanups);
> + return list;
> + }
> +
> + make_cleanup_free_char_ptr_vec (list);
> +
> + /* If complete_line_internal returned more completions than were
> + recorded by the completion tracker, then the completer function that
> + was run does not support completion tracking. In this case,
> + do a final test for too many completions.
>
> - add_status = maybe_add_completion (tracker, candidate);
> + Duplicates are also removed here. Otherwise the user is left
> + scratching his/her head: readline and complete_command will remove
> + duplicates, and if removal of duplicates there brings the total under
> + max_completions the user may think gdb quit searching too early. */
>
> - switch (add_status)
> + if (VEC_length (char_ptr, list) > htab_elements (tracker))
> + {
> + /* Clear the tracker so that we can re-use it to count the number
> + of returned completions. */
> + htab_empty (tracker);
> +
> + for (ix = 0, max_reached = 0;
> + !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
> + ++ix)
> {
> - case MAYBE_ADD_COMPLETION_OK:
> - VEC_safe_push (char_ptr, result, xstrdup (candidate));
> - break;
> - case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
> - VEC_safe_push (char_ptr, result, xstrdup (candidate));
> - max_reached = 1;
> - break;
> - case MAYBE_ADD_COMPLETION_MAX_REACHED:
> - gdb_assert_not_reached ("more than max completions reached");
> - case MAYBE_ADD_COMPLETION_DUPLICATE:
> - break;
> + enum maybe_add_completion_enum add_status;
> +
> + add_status = maybe_add_completion (tracker, candidate);
> +
> + switch (add_status)
> + {
> + case MAYBE_ADD_COMPLETION_OK:
> + VEC_safe_push (char_ptr, result, xstrdup (candidate));
> + break;
> + case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
> + VEC_safe_push (char_ptr, result, xstrdup (candidate));
> + max_reached = 1;
> + break;
> + case MAYBE_ADD_COMPLETION_MAX_REACHED:
> + gdb_assert_not_reached ("more than max completions reached");
> + case MAYBE_ADD_COMPLETION_DUPLICATE:
> + break;
> + }
> }
> }
> + else
> + {
> + /* There is a valid tracker for the completion -- simply copy
> + the returned list into the return result. */
> + for (ix = 0; VEC_iterate (char_ptr, list, ix, candidate); ++ix)
> + VEC_safe_push (char_ptr, result, xstrdup (candidate));
====
Can we just return list instead of making a copy of it?
E.g.,
if (VEC_length (char_ptr, list) <= htab_elements (tracker))
{
discard_cleanups (list_cleanups);
do_cleanups (cleanups);
return list;
}
/* Clear the tracker so that we can re-use it to count the number
of returned completions. */
...
> + }
>
> do_cleanups (cleanups);
>
> @@ -929,10 +1001,11 @@ complete_line (const char *text, const char *line_buffer, int point)
>
> /* Complete on command names. Used by "help". */
> VEC (char_ptr) *
> -command_completer (struct cmd_list_element *ignore,
> +command_completer (struct cmd_list_element *ignore,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> - return complete_line_internal (word, text,
> + return complete_line_internal (tracker, word, text,
> strlen (text), handle_help);
> }
>
> @@ -940,6 +1013,7 @@ command_completer (struct cmd_list_element *ignore,
>
> VEC (char_ptr) *
> signal_completer (struct cmd_list_element *ignore,
> + completion_tracker_t ignore2,
> const char *text, const char *word)
> {
> VEC (char_ptr) *return_val = NULL;
> @@ -970,6 +1044,7 @@ signal_completer (struct cmd_list_element *ignore,
>
> VEC (char_ptr) *
> reg_or_group_completer (struct cmd_list_element *ignore,
> + completion_tracker_t ingore2,
> const char *text, const char *word)
> {
> VEC (char_ptr) *result = NULL;
> @@ -1013,7 +1088,7 @@ gdb_completion_word_break_characters (void)
> {
> VEC (char_ptr) *list;
>
> - list = complete_line_internal (rl_line_buffer, rl_line_buffer, rl_point,
> + list = complete_line_internal (NULL, rl_line_buffer, rl_line_buffer, rl_point,
> handle_brkchars);
> gdb_assert (list == NULL);
> return rl_completer_word_break_characters;
> diff --git a/gdb/completer.h b/gdb/completer.h
> index 56e1a2b..7bc0543 100644
> --- a/gdb/completer.h
> +++ b/gdb/completer.h
> @@ -18,7 +18,6 @@
> #define COMPLETER_H 1
>
> #include "gdb_vecs.h"
> -#include "command.h"
>
> /* Types of functions in struct match_list_displayer. */
>
> @@ -33,6 +32,70 @@ typedef void mld_erase_entire_line_ftype (const struct match_list_displayer *);
> typedef void mld_beep_ftype (const struct match_list_displayer *);
> typedef int mld_read_key_ftype (const struct match_list_displayer *);
>
> +/* Maximum number of candidates to consider before the completer
> + bails by throwing MAX_COMPLETIONS_REACHED_ERROR. Negative values
> + disable limiting. */
> +
> +extern int max_completions;
> +
> +/* Object to track how many unique completions have been generated.
> + Used to limit the size of generated completion lists. */
> +
> +typedef htab_t completion_tracker_t;
> +
> +/* Create a new completion tracker.
> + The result is a hash table to track added completions, or NULL
> + if max_completions <= 0. If max_completions < 0, tracking is disabled.
> + If max_completions == 0, the max is indeed zero. */
> +
> +extern completion_tracker_t new_completion_tracker (void);
> +
> +/* Make a cleanup to free a completion tracker, and reset its pointer
> + to NULL. */
> +
> +extern struct cleanup *make_cleanup_free_completion_tracker
> + (completion_tracker_t *tracker_ptr);
> +
> +/* Return values for maybe_add_completion. */
> +
> +enum maybe_add_completion_enum
> +{
> + /* NAME has been recorded and max_completions has not been reached,
> + or completion tracking is disabled (max_completions < 0). */
> + MAYBE_ADD_COMPLETION_OK,
> +
> + /* NAME has been recorded and max_completions has been reached
> + (thus the caller can stop searching). */
> + MAYBE_ADD_COMPLETION_OK_MAX_REACHED,
> +
> + /* max-completions entries has been reached.
> + Whether NAME is a duplicate or not is not determined. */
> + MAYBE_ADD_COMPLETION_MAX_REACHED,
> +
> + /* NAME has already been recorded.
> + Note that this is never returned if completion tracking is disabled
> + (max_completions < 0). */
> + MAYBE_ADD_COMPLETION_DUPLICATE
> +};
> +
> +/* Add the completion NAME to the list of generated completions if
> + it is not there already.
> + If max_completions is negative, nothing is done, not even watching
> + for duplicates, and MAYBE_ADD_COMPLETION_OK is always returned.
> +
> + If MAYBE_ADD_COMPLETION_MAX_REACHED is returned, callers are required to
> + record at least one more completion. The final list will be pruned to
> + max_completions, but recording at least one more than max_completions is
> + the signal to the completion machinery that too many completions were
> + found. */
> +
> +extern enum maybe_add_completion_enum
> + maybe_add_completion (completion_tracker_t tracker, const char *name);
> +
> +/* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR. */
> +
> +extern void throw_max_completions_reached_error (void);
> +
> /* Interface between CLI/TUI and gdb_match_list_displayer. */
>
> struct match_list_displayer
> @@ -76,106 +139,83 @@ extern char *readline_line_completion_function (const char *text,
> int matches);
>
> extern VEC (char_ptr) *noop_completer (struct cmd_list_element *,
> + completion_tracker_t,
> const char *, const char *);
>
> extern VEC (char_ptr) *filename_completer (struct cmd_list_element *,
> + completion_tracker_t,
> const char *, const char *);
>
> extern VEC (char_ptr) *expression_completer (struct cmd_list_element *,
> + completion_tracker_t,
> const char *, const char *);
>
> extern VEC (char_ptr) *location_completer (struct cmd_list_element *,
> + completion_tracker_t,
> const char *, const char *);
>
> extern VEC (char_ptr) *command_completer (struct cmd_list_element *,
> + completion_tracker_t,
> const char *, const char *);
>
> extern VEC (char_ptr) *signal_completer (struct cmd_list_element *,
> + completion_tracker_t,
> const char *, const char *);
>
> extern VEC (char_ptr) *reg_or_group_completer (struct cmd_list_element *,
> + completion_tracker_t,
> const char *, const char *);
>
> extern char *get_gdb_completer_quote_characters (void);
>
> extern char *gdb_completion_word_break_characters (void);
>
> -/* Set the word break characters array to the corresponding set of
> - chars, based on FN. This function is useful for cases when the
> - completer doesn't know the type of the completion until some
> - calculation is done (e.g., for Python functions). */
> -
> -extern void set_gdb_completion_word_break_characters (completer_ftype *fn);
> -
> -/* Exported to linespec.c */
> -
> -extern const char *skip_quoted_chars (const char *, const char *,
> - const char *);
> -
> -extern const char *skip_quoted (const char *);
> -
> -/* Maximum number of candidates to consider before the completer
> - bails by throwing MAX_COMPLETIONS_REACHED_ERROR. Negative values
> - disable limiting. */
> -
> -extern int max_completions;
> -
> -/* Object to track how many unique completions have been generated.
> - Used to limit the size of generated completion lists. */
> +typedef VEC (char_ptr) *completer_ftype (struct cmd_list_element *,
> + completion_tracker_t,
> + const char *, const char *);
>
> -typedef htab_t completion_tracker_t;
> +typedef void completer_ftype_void (struct cmd_list_element *,
> + const char *, const char *);
>
> -/* Create a new completion tracker.
> - The result is a hash table to track added completions, or NULL
> - if max_completions <= 0. If max_completions < 0, tracking is disabled.
> - If max_completions == 0, the max is indeed zero. */
> +extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
>
> -extern completion_tracker_t new_completion_tracker (void);
> +/* Set the completer_handle_brkchars callback. */
>
> -/* Make a cleanup to free a completion tracker, and reset its pointer
> - to NULL. */
> +extern void set_cmd_completer_handle_brkchars (struct cmd_list_element *,
> + completer_ftype_void *);
>
> -extern struct cleanup *make_cleanup_free_completion_tracker
> - (completion_tracker_t *tracker_ptr);
> -
> -/* Return values for maybe_add_completion. */
> +/* Set the word break characters array to the corresponding set of
> + chars, based on FN. This function is useful for cases when the
> + completer doesn't know the type of the completion until some
> + calculation is done (e.g., for Python functions). */
>
> -enum maybe_add_completion_enum
> -{
> - /* NAME has been recorded and max_completions has not been reached,
> - or completion tracking is disabled (max_completions < 0). */
> - MAYBE_ADD_COMPLETION_OK,
> +extern void set_gdb_completion_word_break_characters (completer_ftype *fn);
>
> - /* NAME has been recorded and max_completions has been reached
> - (thus the caller can stop searching). */
> - MAYBE_ADD_COMPLETION_OK_MAX_REACHED,
> +extern VEC (char_ptr) *complete_on_cmdlist (struct cmd_list_element *,
> + const char *, const char *, int);
>
> - /* max-completions entries has been reached.
> - Whether NAME is a duplicate or not is not determined. */
> - MAYBE_ADD_COMPLETION_MAX_REACHED,
> +extern VEC (char_ptr) *complete_on_enum (completion_tracker_t,
> + const char *const *enumlist,
> + const char *, const char *);
>
> - /* NAME has already been recorded.
> - Note that this is never returned if completion tracking is disabled
> - (max_completions < 0). */
> - MAYBE_ADD_COMPLETION_DUPLICATE
> -};
> +/* Flag for an ambiguous cmd_list result. */
> +#define CMD_LIST_AMBIGUOUS ((struct cmd_list_element *) -1)
>
> -/* Add the completion NAME to the list of generated completions if
> - it is not there already.
> - If max_completions is negative, nothing is done, not even watching
> - for duplicates, and MAYBE_ADD_COMPLETION_OK is always returned.
> +extern struct cmd_list_element *lookup_cmd (const char **,
> + struct cmd_list_element *, char *,
> + int, int);
>
> - If MAYBE_ADD_COMPLETION_MAX_REACHED is returned, callers are required to
> - record at least one more completion. The final list will be pruned to
> - max_completions, but recording at least one more than max_completions is
> - the signal to the completion machinery that too many completions were
> - found. */
> +extern struct cmd_list_element *lookup_cmd_1 (completion_tracker_t,
> + const char **,
> + struct cmd_list_element *,
> + struct cmd_list_element **,
> + int);
>
> -extern enum maybe_add_completion_enum
> - maybe_add_completion (completion_tracker_t tracker, char *name);
> +/* Exported to linespec.c */
>
> -/* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR. */
> +extern const char *skip_quoted_chars (const char *, const char *,
> + const char *);
>
> -extern void throw_max_completions_reached_error (void);
> +extern const char *skip_quoted (const char *);
>
> #endif /* defined (COMPLETER_H) */
> diff --git a/gdb/corefile.c b/gdb/corefile.c
> index a042e6d..309a93d 100644
> --- a/gdb/corefile.c
> +++ b/gdb/corefile.c
> @@ -469,6 +469,7 @@ set_gnutarget_command (char *ignore, int from_tty,
>
> static VEC (char_ptr) *
> complete_set_gnutarget (struct cmd_list_element *cmd,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> static const char **bfd_targets;
> @@ -486,7 +487,7 @@ complete_set_gnutarget (struct cmd_list_element *cmd,
> bfd_targets[last + 1] = NULL;
> }
>
> - return complete_on_enum (bfd_targets, text, word);
> + return complete_on_enum (tracker, bfd_targets, text, word);
> }
>
> /* Set the gnutarget. */
> diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c
> index 9316c4c..4086d0c 100644
> --- a/gdb/cp-abi.c
> +++ b/gdb/cp-abi.c
> @@ -355,6 +355,7 @@ set_cp_abi_cmd (char *args, int from_tty)
>
> static VEC (char_ptr) *
> cp_abi_completer (struct cmd_list_element *ignore,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> static const char **cp_abi_names;
> @@ -369,7 +370,7 @@ cp_abi_completer (struct cmd_list_element *ignore,
> cp_abi_names[i] = NULL;
> }
>
> - return complete_on_enum (cp_abi_names, text, word);
> + return complete_on_enum (tracker, cp_abi_names, text, word);
> }
>
> /* Show the currently selected C++ ABI. */
> diff --git a/gdb/f-lang.c b/gdb/f-lang.c
> index 8b61028..d772cfa 100644
> --- a/gdb/f-lang.c
> +++ b/gdb/f-lang.c
> @@ -229,10 +229,12 @@ f_word_break_characters (void)
> class. */
>
> static VEC (char_ptr) *
> -f_make_symbol_completion_list (const char *text, const char *word,
> +f_make_symbol_completion_list (completion_tracker_t tracker,
> + const char *text, const char *word,
> enum type_code code)
> {
> - return default_make_symbol_completion_list_break_on (text, word, ":", code);
> + return default_make_symbol_completion_list_break_on (tracker, text, word,
> + ":", code);
> }
>
> const struct language_defn f_language_defn =
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 15589b6..75fe2e3 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -6934,6 +6934,7 @@ Are you sure you want to change it? "),
>
> static VEC (char_ptr) *
> handle_completer (struct cmd_list_element *ignore,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> VEC (char_ptr) *vec_signals, *vec_keywords, *return_val;
> @@ -6951,8 +6952,8 @@ handle_completer (struct cmd_list_element *ignore,
> NULL,
> };
>
> - vec_signals = signal_completer (ignore, text, word);
> - vec_keywords = complete_on_enum (keywords, word, word);
> + vec_signals = signal_completer (ignore, tracker, text, word);
> + vec_keywords = complete_on_enum (tracker, keywords, word, word);
>
> return_val = VEC_merge (char_ptr, vec_signals, vec_keywords);
> VEC_free (char_ptr, vec_signals);
> diff --git a/gdb/interps.c b/gdb/interps.c
> index 90b5b2d..2215ac1 100644
> --- a/gdb/interps.c
> +++ b/gdb/interps.c
> @@ -438,6 +438,7 @@ interpreter_exec_cmd (char *args, int from_tty)
> /* List the possible interpreters which could complete the given text. */
> static VEC (char_ptr) *
> interpreter_completer (struct cmd_list_element *ignore,
> + completion_tracker_t ingore2,
> const char *text, const char *word)
> {
> int textlen;
> diff --git a/gdb/language.h b/gdb/language.h
> index 436fd6e..b095093 100644
> --- a/gdb/language.h
> +++ b/gdb/language.h
> @@ -301,7 +301,8 @@ struct language_defn
> completion is being made. If CODE is TYPE_CODE_UNDEF, then all
> symbols should be examined; otherwise, only STRUCT_DOMAIN
> symbols whose type has a code of CODE should be matched. */
> - VEC (char_ptr) *(*la_make_symbol_completion_list) (const char *text,
> + VEC (char_ptr) *(*la_make_symbol_completion_list) (completion_tracker_t,
> + const char *text,
> const char *word,
> enum type_code code);
>
> diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
> index a5e96d6..2f95b46 100644
> --- a/gdb/python/py-cmd.c
> +++ b/gdb/python/py-cmd.c
> @@ -347,6 +347,7 @@ cmdpy_completer_handle_brkchars (struct cmd_list_element *command,
>
> static VEC (char_ptr) *
> cmdpy_completer (struct cmd_list_element *command,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> PyObject *resultobj = NULL;
> @@ -378,7 +379,7 @@ cmdpy_completer (struct cmd_list_element *command,
> PyErr_Clear ();
> }
> else if (value >= 0 && value < (long) N_COMPLETERS)
> - result = completers[value].completer (command, text, word);
> + result = completers[value].completer (command, tracker, text, word);
> }
> else
> {
> @@ -485,7 +486,7 @@ gdbpy_parse_command_name (const char *name,
> prefix_text[i + 1] = '\0';
>
> prefix_text2 = prefix_text;
> - elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1);
> + elt = lookup_cmd_1 (NULL, &prefix_text2, *start_list, NULL, 1);
> if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
> {
> PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."),
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 5302afa..9f04966 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -4994,29 +4994,21 @@ do_free_completion_list (void *list)
>
> static VEC (char_ptr) *return_val;
>
> -#define COMPLETION_LIST_ADD_SYMBOL(symbol, sym_text, len, text, word) \
> - completion_list_add_name \
> - (SYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
> +#define COMPLETION_LIST_ADD_SYMBOL(tracker, symbol, sym_text, len, text, word) \
> + completion_list_add_name \
> + (tracker, SYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
>
> -#define MCOMPLETION_LIST_ADD_SYMBOL(symbol, sym_text, len, text, word) \
> - completion_list_add_name \
> - (MSYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
> -
> -/* Tracker for how many unique completions have been generated. Used
> - to terminate completion list generation early if the list has grown
> - to a size so large as to be useless. This helps avoid GDB seeming
> - to lock up in the event the user requests to complete on something
> - vague that necessitates the time consuming expansion of many symbol
> - tables. */
> -
> -static completion_tracker_t completion_tracker;
> +#define MCOMPLETION_LIST_ADD_SYMBOL(tracker, symbol, sym_text, len, text, word) \
> + completion_list_add_name \
> + ((tracker), MSYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
>
> /* Test to see if the symbol specified by SYMNAME (which is already
> demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
> characters. If so, add it to the current completion list. */
>
> static void
> -completion_list_add_name (const char *symname,
> +completion_list_add_name (completion_tracker_t tracker,
> + const char *symname,
> const char *sym_text, int sym_text_len,
> const char *text, const char *word)
> {
> @@ -5051,7 +5043,7 @@ completion_list_add_name (const char *symname,
> strcat (new, symname);
> }
>
> - add_status = maybe_add_completion (completion_tracker, new);
> + add_status = maybe_add_completion (tracker, new);
>
> switch (add_status)
> {
> @@ -5075,7 +5067,8 @@ completion_list_add_name (const char *symname,
> again and feed all the selectors into the mill. */
>
> static void
> -completion_list_objc_symbol (struct minimal_symbol *msymbol,
> +completion_list_objc_symbol (completion_tracker_t tracker,
> + struct minimal_symbol *msymbol,
> const char *sym_text, int sym_text_len,
> const char *text, const char *word)
> {
> @@ -5093,7 +5086,8 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
>
> if (sym_text[0] == '[')
> /* Complete on shortened method method. */
> - completion_list_add_name (method + 1, sym_text, sym_text_len, text, word);
> + completion_list_add_name (tracker, method + 1, sym_text, sym_text_len,
> + text, word);
>
> while ((strlen (method) + 1) >= tmplen)
> {
> @@ -5114,9 +5108,11 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
> memcpy (tmp, method, (category - method));
> tmp[category - method] = ' ';
> memcpy (tmp + (category - method) + 1, selector, strlen (selector) + 1);
> - completion_list_add_name (tmp, sym_text, sym_text_len, text, word);
> + completion_list_add_name (tracker, tmp, sym_text, sym_text_len, text,
> + word);
> if (sym_text[0] == '[')
> - completion_list_add_name (tmp + 1, sym_text, sym_text_len, text, word);
> + completion_list_add_name (tracker, tmp + 1, sym_text, sym_text_len,
> + text, word);
> }
>
> if (selector != NULL)
> @@ -5127,7 +5123,8 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
> if (tmp2 != NULL)
> *tmp2 = '\0';
>
> - completion_list_add_name (tmp, sym_text, sym_text_len, text, word);
> + completion_list_add_name (tracker, tmp, sym_text, sym_text_len, text,
> + word);
> }
> }
>
> @@ -5178,9 +5175,9 @@ language_search_unquoted_string (const char *text, const char *p)
> }
>
> static void
> -completion_list_add_fields (struct symbol *sym, const char *sym_text,
> - int sym_text_len, const char *text,
> - const char *word)
> +completion_list_add_fields (completion_tracker_t tracker, struct symbol *sym,
> + const char *sym_text, int sym_text_len,
> + const char *text, const char *word)
> {
> if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
> {
> @@ -5191,7 +5188,7 @@ completion_list_add_fields (struct symbol *sym, const char *sym_text,
> if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
> for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
> if (TYPE_FIELD_NAME (t, j))
> - completion_list_add_name (TYPE_FIELD_NAME (t, j),
> + completion_list_add_name (tracker, TYPE_FIELD_NAME (t, j),
> sym_text, sym_text_len, text, word);
> }
> }
> @@ -5206,6 +5203,7 @@ struct add_name_data
> int sym_text_len;
> const char *text;
> const char *word;
> + completion_tracker_t tracker;
>
> /* Extra argument required for add_symtab_completions. */
> enum type_code code;
> @@ -5221,7 +5219,7 @@ add_macro_name (const char *name, const struct macro_definition *ignore,
> {
> struct add_name_data *datum = (struct add_name_data *) user_data;
>
> - completion_list_add_name (name,
> + completion_list_add_name (datum->tracker, name,
> datum->sym_text, datum->sym_text_len,
> datum->text, datum->word);
> }
> @@ -5240,6 +5238,7 @@ symbol_completion_matcher (const char *name, void *user_data)
>
> static void
> add_symtab_completions (struct compunit_symtab *cust,
> + completion_tracker_t tracker,
> const char *sym_text, int sym_text_len,
> const char *text, const char *word,
> enum type_code code)
> @@ -5258,7 +5257,7 @@ add_symtab_completions (struct compunit_symtab *cust,
> if (code == TYPE_CODE_UNDEF
> || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
> && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
> - COMPLETION_LIST_ADD_SYMBOL (sym,
> + COMPLETION_LIST_ADD_SYMBOL (tracker, sym,
> sym_text, sym_text_len,
> text, word);
> }
> @@ -5274,14 +5273,15 @@ symtab_expansion_callback (struct compunit_symtab *symtab,
> {
> struct add_name_data *datum = (struct add_name_data *) user_data;
>
> - add_symtab_completions (symtab,
> + add_symtab_completions (symtab, datum->tracker,
> datum->sym_text, datum->sym_text_len,
> datum->text, datum->word,
> datum->code);
> }
>
> static void
> -default_make_symbol_completion_list_break_on_1 (const char *text,
> +default_make_symbol_completion_list_break_on_1 (completion_tracker_t tracker,
> + const char *text,
> const char *word,
> const char *break_on,
> enum type_code code)
> @@ -5302,7 +5302,6 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
> /* Length of sym_text. */
> int sym_text_len;
> struct add_name_data datum;
> - struct cleanup *cleanups;
>
> /* Now look for the symbol we are supposed to complete on. */
> {
> @@ -5373,14 +5372,12 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
> }
> gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
>
> - completion_tracker = new_completion_tracker ();
> - cleanups = make_cleanup_free_completion_tracker (&completion_tracker);
> -
> datum.sym_text = sym_text;
> datum.sym_text_len = sym_text_len;
> datum.text = text;
> datum.word = word;
> datum.code = code;
> + datum.tracker = tracker;
>
> /* At this point scan through the misc symbol vectors and add each
> symbol you find to the list. Eventually we want to ignore
> @@ -5392,17 +5389,17 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
> ALL_MSYMBOLS (objfile, msymbol)
> {
> QUIT;
> - MCOMPLETION_LIST_ADD_SYMBOL (msymbol, sym_text, sym_text_len, text,
> - word);
> + MCOMPLETION_LIST_ADD_SYMBOL (tracker, msymbol, sym_text,
> + sym_text_len, text, word);
>
> - completion_list_objc_symbol (msymbol, sym_text, sym_text_len, text,
> - word);
> + completion_list_objc_symbol (tracker, msymbol, sym_text,
> + sym_text_len, text, word);
> }
> }
>
> /* Add completions for all currently loaded symbol tables. */
> ALL_COMPUNITS (objfile, cust)
> - add_symtab_completions (cust, sym_text, sym_text_len, text, word,
> + add_symtab_completions (cust, tracker, sym_text, sym_text_len, text, word,
> code);
>
> /* Look through the partial symtabs for all symbols which begin
> @@ -5430,15 +5427,15 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
> {
> if (code == TYPE_CODE_UNDEF)
> {
> - COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
> - word);
> - completion_list_add_fields (sym, sym_text, sym_text_len, text,
> - word);
> + COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text,
> + sym_text_len, text, word);
> + completion_list_add_fields (tracker, sym, sym_text,
> + sym_text_len, text, word);
> }
> else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
> && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
> - COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
> - word);
> + COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text,
> + sym_text_len, text, word);
> }
>
> /* Stop when we encounter an enclosing function. Do not stop for
> @@ -5455,11 +5452,13 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
> {
> if (surrounding_static_block != NULL)
> ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
> - completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
> + completion_list_add_fields (tracker, sym, sym_text, sym_text_len,
> + text, word);
>
> if (surrounding_global_block != NULL)
> ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
> - completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
> + completion_list_add_fields (tracker, sym, sym_text, sym_text_len,
> + text, word);
> }
>
> /* Skip macros if we are completing a struct tag -- arguable but
> @@ -5487,12 +5486,11 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
> /* User-defined macros are always visible. */
> macro_for_each (macro_user_macros, add_macro_name, &datum);
> }
> -
> - do_cleanups (cleanups);
> }
>
> VEC (char_ptr) *
> -default_make_symbol_completion_list_break_on (const char *text,
> +default_make_symbol_completion_list_break_on (completion_tracker_t tracker,
> + const char *text,
> const char *word,
> const char *break_on,
> enum type_code code)
> @@ -5505,7 +5503,7 @@ default_make_symbol_completion_list_break_on (const char *text,
>
> TRY_CATCH (except, RETURN_MASK_ERROR)
> {
> - default_make_symbol_completion_list_break_on_1 (text, word,
> + default_make_symbol_completion_list_break_on_1 (tracker, text, word,
> break_on, code);
> }
> if (except.reason < 0)
> @@ -5519,10 +5517,12 @@ default_make_symbol_completion_list_break_on (const char *text,
> }
>
> VEC (char_ptr) *
> -default_make_symbol_completion_list (const char *text, const char *word,
> +default_make_symbol_completion_list (completion_tracker_t tracker,
> + const char *text, const char *word,
> enum type_code code)
> {
> - return default_make_symbol_completion_list_break_on (text, word, "", code);
> + return default_make_symbol_completion_list_break_on (tracker, text, word,
> + "", code);
> }
>
> /* Return a vector of all symbols (regardless of class) which begin by
> @@ -5530,9 +5530,10 @@ default_make_symbol_completion_list (const char *text, const char *word,
> is NULL. */
>
> VEC (char_ptr) *
> -make_symbol_completion_list (const char *text, const char *word)
> +make_symbol_completion_list (completion_tracker_t tracker, const char *text,
> + const char *word)
> {
> - return current_language->la_make_symbol_completion_list (text, word,
> + return current_language->la_make_symbol_completion_list (tracker, text, word,
> TYPE_CODE_UNDEF);
> }
>
> @@ -5540,13 +5541,14 @@ make_symbol_completion_list (const char *text, const char *word)
> symbols whose type code is CODE. */
>
> VEC (char_ptr) *
> -make_symbol_completion_type (const char *text, const char *word,
> - enum type_code code)
> +make_symbol_completion_type (completion_tracker_t tracker, const char *text,
> + const char *word, enum type_code code)
> {
> gdb_assert (code == TYPE_CODE_UNION
> || code == TYPE_CODE_STRUCT
> || code == TYPE_CODE_ENUM);
> - return current_language->la_make_symbol_completion_list (text, word, code);
> + return current_language->la_make_symbol_completion_list (tracker, text,
> + word, code);
> }
>
> /* Like make_symbol_completion_list, but suitable for use as a
> @@ -5554,16 +5556,18 @@ make_symbol_completion_type (const char *text, const char *word,
>
> VEC (char_ptr) *
> make_symbol_completion_list_fn (struct cmd_list_element *ignore,
> + completion_tracker_t tracker,
> const char *text, const char *word)
> {
> - return make_symbol_completion_list (text, word);
> + return make_symbol_completion_list (tracker, text, word);
> }
>
> /* Like make_symbol_completion_list, but returns a list of symbols
> defined in a source file FILE. */
>
> VEC (char_ptr) *
> -make_file_symbol_completion_list (const char *text, const char *word,
> +make_file_symbol_completion_list (completion_tracker_t tracker,
> + const char *text, const char *word,
> const char *srcfile)
> {
> struct symbol *sym;
> @@ -5645,13 +5649,15 @@ make_file_symbol_completion_list (const char *text, const char *word,
> b = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (s), GLOBAL_BLOCK);
> ALL_BLOCK_SYMBOLS (b, iter, sym)
> {
> - COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
> + COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text, sym_text_len, text,
> + word);
> }
>
> b = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (s), STATIC_BLOCK);
> ALL_BLOCK_SYMBOLS (b, iter, sym)
> {
> - COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
> + COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text, sym_text_len, text,
> + word);
> }
>
> return (return_val);
> @@ -5662,11 +5668,13 @@ make_file_symbol_completion_list (const char *text, const char *word,
> list as necessary. */
>
> static void
> -add_filename_to_list (const char *fname, const char *text, const char *word,
> +add_filename_to_list (completion_tracker_t tracker,
> + const char *fname, const char *text, const char *word,
> VEC (char_ptr) **list)
> {
> char *new;
> size_t fnlen = strlen (fname);
> + enum maybe_add_completion_enum add_status;
>
> if (word == text)
> {
> @@ -5688,7 +5696,22 @@ add_filename_to_list (const char *fname, const char *text, const char *word,
> new[text - word] = '\0';
> strcat (new, fname);
> }
> - VEC_safe_push (char_ptr, *list, new);
> +
> + add_status = maybe_add_completion (tracker, new);
> + switch (add_status)
> + {
> + case MAYBE_ADD_COMPLETION_OK:
> + VEC_safe_push (char_ptr, *list, new);
> + break;
> + case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
> + VEC_safe_push (char_ptr, *list, new);
> + /* fall through */
> + case MAYBE_ADD_COMPLETION_MAX_REACHED:
====
memory leak of new in MAYBE_ADD_COMPLETION_MAX_REACHED case.
> + throw_max_completions_reached_error ();
> + break;
> + case MAYBE_ADD_COMPLETION_DUPLICATE:
> + xfree (new);
> + }
> }
>
> static int
> @@ -5717,6 +5740,7 @@ struct add_partial_filename_data
> const char *word;
> int text_len;
> VEC (char_ptr) **list;
> + completion_tracker_t tracker;
> };
>
> /* A callback for map_partial_symbol_filenames. */
> @@ -5734,7 +5758,8 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
> {
> /* This file matches for a completion; add it to the
> current list of matches. */
> - add_filename_to_list (filename, data->text, data->word, data->list);
> + add_filename_to_list (data->tracker, filename, data->text, data->word,
> + data->list);
> }
> else
> {
> @@ -5743,7 +5768,8 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
> if (base_name != filename
> && !filename_seen (data->filename_seen_cache, base_name, 1)
> && filename_ncmp (base_name, data->text, data->text_len) == 0)
> - add_filename_to_list (base_name, data->text, data->word, data->list);
> + add_filename_to_list (data->tracker, base_name, data->text,
> + data->word, data->list);
> }
> }
>
> @@ -5753,7 +5779,8 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
> NULL. */
>
> VEC (char_ptr) *
> -make_source_files_completion_list (const char *text, const char *word)
> +make_source_files_completion_list (completion_tracker_t tracker,
> + const char *text, const char *word)
> {
> struct compunit_symtab *cu;
> struct symtab *s;
> @@ -5783,7 +5810,7 @@ make_source_files_completion_list (const char *text, const char *word)
> {
> /* This file matches for a completion; add it to the current
> list of matches. */
> - add_filename_to_list (s->filename, text, word, &list);
> + add_filename_to_list (tracker, s->filename, text, word, &list);
> }
> else
> {
> @@ -5795,7 +5822,7 @@ make_source_files_completion_list (const char *text, const char *word)
> if (base_name != s->filename
> && !filename_seen (filename_seen_cache, base_name, 1)
> && filename_ncmp (base_name, text, text_len) == 0)
> - add_filename_to_list (base_name, text, word, &list);
> + add_filename_to_list (tracker, base_name, text, word, &list);
> }
> }
>
> @@ -5804,6 +5831,7 @@ make_source_files_completion_list (const char *text, const char *word)
> datum.word = word;
> datum.text_len = text_len;
> datum.list = &list;
> + datum.tracker = tracker;
> map_symbol_filenames (maybe_add_partial_symtab_filename, &datum,
> 0 /*need_fullname*/);
>
> diff --git a/gdb/symtab.h b/gdb/symtab.h
> index 0eb3a5b..572738a 100644
> --- a/gdb/symtab.h
> +++ b/gdb/symtab.h
> @@ -23,6 +23,7 @@
> #include "vec.h"
> #include "gdb_vecs.h"
> #include "gdbtypes.h"
> +#include "completer.h"
>
> /* Opaque declarations. */
> struct ui_file;
> @@ -1444,24 +1445,22 @@ extern void forget_cached_source_info (void);
> extern void select_source_symtab (struct symtab *);
>
> extern VEC (char_ptr) *default_make_symbol_completion_list_break_on
> - (const char *text, const char *word, const char *break_on,
> - enum type_code code);
> -extern VEC (char_ptr) *default_make_symbol_completion_list (const char *,
> - const char *,
> - enum type_code);
> -extern VEC (char_ptr) *make_symbol_completion_list (const char *, const char *);
> -extern VEC (char_ptr) *make_symbol_completion_type (const char *, const char *,
> - enum type_code);
> -extern VEC (char_ptr) *make_symbol_completion_list_fn (struct cmd_list_element *,
> - const char *,
> - const char *);
> -
> -extern VEC (char_ptr) *make_file_symbol_completion_list (const char *,
> - const char *,
> - const char *);
> -
> -extern VEC (char_ptr) *make_source_files_completion_list (const char *,
> - const char *);
> + (completion_tracker_t tracker, const char *text, const char *word,
> + const char *break_on, enum type_code code);
> +extern VEC (char_ptr) *default_make_symbol_completion_list
> + (completion_tracker_t tracker, const char *, const char *, enum type_code);
> +extern VEC (char_ptr) *make_symbol_completion_list
> + (completion_tracker_t tracker, const char *, const char *);
> +extern VEC (char_ptr) *make_symbol_completion_type
> + (completion_tracker_t, const char *, const char *, enum type_code);
> +extern VEC (char_ptr) *make_symbol_completion_list_fn
> + (struct cmd_list_element *, completion_tracker_t, const char *, const char *);
> +
> +extern VEC (char_ptr) *make_file_symbol_completion_list
> + (completion_tracker_t, const char *, const char *, const char *);
> +
> +extern VEC (char_ptr) *make_source_files_completion_list
> + (completion_tracker_t, const char *, const char *);
>
> /* symtab.c */
>
> diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
> index f77bfe2..5afd851 100644
> --- a/gdb/testsuite/gdb.base/completion.exp
> +++ b/gdb/testsuite/gdb.base/completion.exp
> @@ -777,6 +777,84 @@ gdb_test_multiple "" "$test" {
> }
>
> #
> +# Tests for the location completer
> +#
> +
> +# Turn off pending breakpoint support so that we don't get queried
> +# all the time.
> +gdb_test_no_output "set breakpoint pending off"
> +
> +set subsrc [string range $srcfile 0 [expr {[string length $srcfile] - 3}]]
> +set test "tab complete break $subsrc"
> +send_gdb "break $subsrc\t\t"
> +gdb_test_multiple "" $test {
> + -re "break\.c.*break1\.c.*$gdb_prompt " {
> + send_gdb "1\t\n"
> + gdb_test_multiple "" $test {
> + -re ".*Function \"$srcfile2\" not defined\..*$gdb_prompt " {
> + pass $test
> + }
> + -re "$gdb_prompt p$" {
> + fail $test
> + }
> + }
> + }
> +
> + -re "$gdb_prompt p$" {
> + fail $test
> + }
> +}
> +
> +gdb_test "complete break $subsrc" "break\.c.*break1\.c"
> +
> +# gdb/17960
> +set test "tab complete break $srcfile:ma"
> +send_gdb "break $srcfile:ma\t"
> +gdb_test_multiple "" $test {
> + -re "break $srcfile:main " {
> + send_gdb "\n"
> + gdb_test_multiple "" $test {
> + -re ".*Breakpoint.*at .*/$srcfile, line .*$gdb_prompt " {
> + pass $test
> + gdb_test_no_output "delete breakpoint \$bpnum" \
> + "delete breakpoint for $test"
> + }
> + -re "$gdb_prompt p$" {
> + fail $test
> + }
> + }
> + }
> + -re "$gdb_prompt p$" {
> + fail $test
> + }
> +}
> +
> +gdb_test "complete break $srcfile:ma" "break\.c:main"
> +
> +set test "tab complete break need"
> +send_gdb "break need\t"
> +gdb_test_multiple "" $test {
> + -re "break need_malloc " {
> + send_gdb "\n"
> + gdb_test_multiple "" $test {
> + -re ".*Breakpoint.*at .*/$srcfile, line .*$gdb_prompt " {
> + pass $test
> + gdb_test_no_output "delete breakpoint \$bpnum" \
> + "delete breakpoint for $test"
> + }
> + -re "$gdb_prompt p$" {
> + fail $test
> + }
> + }
> + }
> + -re "$gdb_prompt p$" {
> + fail $test
> + }
> +}
> +
> +gdb_test "complete break need" "need_malloc"
> +
> +#
> # Completion limiting.
> #
>
> diff --git a/gdb/top.c b/gdb/top.c
> index 8242e12..af5d923 100644
> --- a/gdb/top.c
> +++ b/gdb/top.c
> @@ -1673,7 +1673,7 @@ set_verbose (char *args, int from_tty, struct cmd_list_element *c)
> const char *cmdname = "verbose";
> struct cmd_list_element *showcmd;
>
> - showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, 1);
> + showcmd = lookup_cmd_1 (NULL, &cmdname, showlist, NULL, 1);
> gdb_assert (showcmd != NULL && showcmd != CMD_LIST_AMBIGUOUS);
>
> if (info_verbose)