X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Flinespec.c;h=b69ab629eec86f4507cafffbecea1478ff934ebd;hb=f1735a53a63040cc4b4a735bf18a3f20d308e519;hp=7a595ed575dcd2941a297a04c7da5ef20e698399;hpb=acaa662f29836ce4d4c3a0abc2132f285f9df585;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/linespec.c b/gdb/linespec.c index 7a595ed575..b69ab629ee 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -1,6 +1,6 @@ /* Parser for linespec for the GNU debugger, GDB. - Copyright (C) 1986-2015 Free Software Foundation, Inc. + Copyright (C) 1986-2017 Free Software Foundation, Inc. This file is part of GDB. @@ -44,6 +44,37 @@ #include "ada-lang.h" #include "stack.h" #include "location.h" +#include "common/function-view.h" +#include "common/def-vector.h" + +/* An enumeration of the various things a user might attempt to + complete for a linespec location. */ + +enum class linespec_complete_what +{ + /* Nothing, no possible completion. */ + NOTHING, + + /* A function/method name. Due to ambiguity between + + (gdb) b source[TAB] + source_file.c + source_function + + this can also indicate a source filename, iff we haven't seen a + separate source filename component, as in "b source.c:function". */ + FUNCTION, + + /* A label symbol. E.g., break file.c:function:LABEL. */ + LABEL, + + /* An expression. E.g., "break foo if EXPR", or "break *EXPR". */ + EXPRESSION, + + /* A linespec keyword ("if"/"thread"/"task"). + E.g., "break func threa". */ + KEYWORD, +}; typedef struct symbol *symbolp; DEF_VEC_P (symbolp); @@ -123,6 +154,10 @@ struct linespec_state /* The program space as seen when the module was entered. */ struct program_space *program_space; + /* If not NULL, the search is restricted to just this program + space. */ + struct program_space *search_pspace; + /* The default symtab to use, if no other symtab is specified. */ struct symtab *default_symtab; @@ -139,7 +174,7 @@ struct linespec_state /* The 'canonical' value passed to decode_line_full, or NULL. */ struct linespec_result *canonical; - /* Canonical strings that mirror the symtabs_and_lines result. */ + /* Canonical strings that mirror the std::vector result. */ struct linespec_canonical_name *canonical_names; /* This is a set of address_entry objects which is used to prevent @@ -167,8 +202,23 @@ struct collect_info VEC (symbolp) *symbols; VEC (bound_minimal_symbol_d) *minimal_symbols; } result; + + /* Possibly add a symbol to the results. */ + bool add_symbol (symbol *sym); }; +bool +collect_info::add_symbol (symbol *sym) +{ + /* In list mode, add all matching symbols, regardless of class. + This allows the user to type "list a_global_variable". */ + if (SYMBOL_CLASS (sym) == LOC_BLOCK || this->state->list_mode) + VEC_safe_push (symbolp, this->result.symbols, sym); + + /* Continue iterating. */ + return true; +} + /* Token types */ enum ls_token_type @@ -196,9 +246,9 @@ enum ls_token_type }; typedef enum ls_token_type linespec_token_type; -/* List of keywords */ - -static const char * const linespec_keywords[] = { "if", "thread", "task" }; +/* List of keywords. This is NULL-terminated so that it can be used + as enum completer. */ +const char * const linespec_keywords[] = { "if", "thread", "task", NULL }; #define IF_KEYWORD_INDEX 0 /* A token of the linespec lexer */ @@ -251,6 +301,29 @@ struct ls_parser /* The result of the parse. */ struct linespec result; #define PARSER_RESULT(PPTR) (&(PPTR)->result) + + /* What the parser believes the current word point should complete + to. */ + linespec_complete_what complete_what; + + /* The completion word point. The parser advances this as it skips + tokens. At some point the input string will end or parsing will + fail, and then we attempt completion at the captured completion + word point, interpreting the string at completion_word as + COMPLETE_WHAT. */ + const char *completion_word; + + /* If the current token was a quoted string, then this is the + quoting character (either " or '). */ + int completion_quote_char; + + /* If the current token was a quoted string, then this points at the + end of the quoted string. */ + const char *completion_quote_end; + + /* If parsing for completion, then this points at the completion + tracker. Otherwise, this is NULL. */ + struct completion_tracker *completion_tracker; }; typedef struct ls_parser linespec_parser; @@ -260,26 +333,27 @@ typedef struct ls_parser linespec_parser; /* Prototypes for local functions. */ -static void iterate_over_file_blocks (struct symtab *symtab, - const char *name, domain_enum domain, - symbol_found_callback_ftype *callback, - void *data); +static void iterate_over_file_blocks + (struct symtab *symtab, const char *name, domain_enum domain, + gdb::function_view callback); static void initialize_defaults (struct symtab **default_symtab, int *default_line); CORE_ADDR linespec_expression_to_pc (const char **exp_ptr); -static struct symtabs_and_lines decode_objc (struct linespec_state *self, - linespec_p ls, - const char *arg); +static std::vector decode_objc (struct linespec_state *self, + linespec_p ls, + const char *arg); -static VEC (symtab_ptr) *symtabs_from_filename (const char *); +static VEC (symtab_ptr) *symtabs_from_filename (const char *, + struct program_space *pspace); static VEC (symbolp) *find_label_symbols (struct linespec_state *self, VEC (symbolp) *function_symbols, VEC (symbolp) **label_funcs_ret, - const char *name); + const char *name, + bool completion_mode = false); static void find_linespec_symbols (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs, @@ -302,22 +376,24 @@ static void add_all_symbol_names_from_pspace (struct collect_info *info, struct program_space *pspace, VEC (const_char_ptr) *names); -static VEC (symtab_ptr) *collect_symtabs_from_filename (const char *file); +static VEC (symtab_ptr) * + collect_symtabs_from_filename (const char *file, + struct program_space *pspace); -static void decode_digits_ordinary (struct linespec_state *self, - linespec_p ls, - int line, - struct symtabs_and_lines *sals, - struct linetable_entry **best_entry); +static std::vector decode_digits_ordinary + (struct linespec_state *self, + linespec_p ls, + int line, + linetable_entry **best_entry); -static void decode_digits_list_mode (struct linespec_state *self, - linespec_p ls, - struct symtabs_and_lines *values, - struct symtab_and_line val); +static std::vector decode_digits_list_mode + (struct linespec_state *self, + linespec_p ls, + struct symtab_and_line val); static void minsym_found (struct linespec_state *self, struct objfile *objfile, struct minimal_symbol *msymbol, - struct symtabs_and_lines *result); + std::vector *result); static int compare_symbols (const void *a, const void *b); @@ -378,7 +454,7 @@ linespec_lexer_lex_keyword (const char *p) if (p != NULL) { - for (i = 0; i < ARRAY_SIZE (linespec_keywords); ++i) + for (i = 0; linespec_keywords[i] != NULL; ++i) { int len = strlen (linespec_keywords[i]); @@ -398,8 +474,8 @@ linespec_lexer_lex_keyword (const char *p) if (i != IF_KEYWORD_INDEX) { p += len; - p = skip_spaces_const (p); - for (j = 0; j < ARRAY_SIZE (linespec_keywords); ++j) + p = skip_spaces (p); + for (j = 0; linespec_keywords[j] != NULL; ++j) { int nextlen = strlen (linespec_keywords[j]); @@ -520,6 +596,30 @@ find_parameter_list_end (const char *input) return p; } +/* If the [STRING, STRING_LEN) string ends with what looks like a + keyword, return the keyword start offset in STRING. Return -1 + otherwise. */ + +static size_t +string_find_incomplete_keyword_at_end (const char * const *keywords, + const char *string, size_t string_len) +{ + const char *end = string + string_len; + const char *p = end; + + while (p > string && *p != ' ') + --p; + if (p > string) + { + p++; + size_t len = end - p; + for (size_t i = 0; keywords[i] != NULL; ++i) + if (strncmp (keywords[i], p, len) == 0) + return p - string; + } + + return -1; +} /* Lex a string from the input in PARSER. */ @@ -567,13 +667,31 @@ linespec_lexer_lex_string (linespec_parser *parser) /* Skip to the ending quote. */ end = skip_quote_char (PARSER_STREAM (parser), quote_char); - /* Error if the input did not terminate properly. */ - if (end == NULL) - error (_("unmatched quote")); + /* This helps the completer mode decide whether we have a + complete string. */ + parser->completion_quote_char = quote_char; + parser->completion_quote_end = end; - /* Skip over the ending quote and mark the length of the string. */ - PARSER_STREAM (parser) = (char *) ++end; - LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start; + /* Error if the input did not terminate properly, unless in + completion mode. */ + if (end == NULL) + { + if (parser->completion_tracker == NULL) + error (_("unmatched quote")); + + /* In completion mode, we'll try to complete the incomplete + token. */ + token.type = LSTOKEN_STRING; + while (*PARSER_STREAM (parser) != '\0') + PARSER_STREAM (parser)++; + LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 1 - start; + } + else + { + /* Skip over the ending quote and mark the length of the string. */ + PARSER_STREAM (parser) = (char *) ++end; + LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start; + } } else { @@ -591,7 +709,7 @@ linespec_lexer_lex_string (linespec_parser *parser) { if (isspace (*PARSER_STREAM (parser))) { - p = skip_spaces_const (PARSER_STREAM (parser)); + p = skip_spaces (PARSER_STREAM (parser)); /* When we get here we know we've found something followed by a space (we skip over parens and templates below). So if we find a keyword now, we know it is a keyword and not, @@ -651,14 +769,50 @@ linespec_lexer_lex_string (linespec_parser *parser) else if (*PARSER_STREAM (parser) == '<' || *PARSER_STREAM (parser) == '(') { - const char *p; + /* Don't interpret 'operator<' / 'operator<<' as a + template parameter list though. */ + if (*PARSER_STREAM (parser) == '<' + && (PARSER_STATE (parser)->language->la_language + == language_cplus) + && (PARSER_STREAM (parser) - start) >= CP_OPERATOR_LEN) + { + const char *p = PARSER_STREAM (parser); + + while (p > start && isspace (p[-1])) + p--; + if (p - start >= CP_OPERATOR_LEN) + { + p -= CP_OPERATOR_LEN; + if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0 + && (p == start + || !(isalnum (p[-1]) || p[-1] == '_'))) + { + /* This is an operator name. Keep going. */ + ++(PARSER_STREAM (parser)); + if (*PARSER_STREAM (parser) == '<') + ++(PARSER_STREAM (parser)); + continue; + } + } + } + + const char *p = find_parameter_list_end (PARSER_STREAM (parser)); + PARSER_STREAM (parser) = p; - p = find_parameter_list_end (PARSER_STREAM (parser)); - if (p != NULL) + /* Don't loop around to the normal \0 case above because + we don't want to misinterpret a potential keyword at + the end of the token when the string isn't + "()<>"-balanced. This handles "b + function(thread" in completion mode. */ + if (*p == '\0') { - PARSER_STREAM (parser) = p; - continue; + LS_TOKEN_STOKEN (token).ptr = start; + LS_TOKEN_STOKEN (token).length + = PARSER_STREAM (parser) - start; + return token; } + else + continue; } /* Commas are terminators, but not if they are part of an operator name. */ @@ -666,10 +820,9 @@ linespec_lexer_lex_string (linespec_parser *parser) { if ((PARSER_STATE (parser)->language->la_language == language_cplus) - && (PARSER_STREAM (parser) - start) > 8 - /* strlen ("operator") */) + && (PARSER_STREAM (parser) - start) > CP_OPERATOR_LEN) { - const char *p = strstr (start, "operator"); + const char *p = strstr (start, CP_OPERATOR_STR); if (p != NULL && is_operator_name (p)) { @@ -703,7 +856,7 @@ linespec_lexer_lex_one (linespec_parser *parser) if (parser->lexer.current.type == LSTOKEN_CONSUMED) { /* Skip any whitespace. */ - PARSER_STREAM (parser) = skip_spaces_const (PARSER_STREAM (parser)); + PARSER_STREAM (parser) = skip_spaces (PARSER_STREAM (parser)); /* Check for a keyword, they end the linespec. */ keyword = linespec_lexer_lex_keyword (PARSER_STREAM (parser)); @@ -777,13 +930,48 @@ linespec_lexer_lex_one (linespec_parser *parser) } /* Consume the current token and return the next token in PARSER's - input stream. */ + input stream. Also advance the completion word for completion + mode. */ static linespec_token linespec_lexer_consume_token (linespec_parser *parser) { + gdb_assert (parser->lexer.current.type != LSTOKEN_EOI); + + bool advance_word = (parser->lexer.current.type != LSTOKEN_STRING + || *PARSER_STREAM (parser) != '\0'); + + /* If we're moving past a string to some other token, it must be the + quote was terminated. */ + if (parser->completion_quote_char) + { + gdb_assert (parser->lexer.current.type == LSTOKEN_STRING); + + /* If the string was the last (non-EOI) token, we're past the + quote, but remember that for later. */ + if (*PARSER_STREAM (parser) != '\0') + { + parser->completion_quote_char = '\0'; + parser->completion_quote_end = NULL;; + } + } + parser->lexer.current.type = LSTOKEN_CONSUMED; - return linespec_lexer_lex_one (parser); + linespec_lexer_lex_one (parser); + + if (parser->lexer.current.type == LSTOKEN_STRING) + { + /* Advance the completion word past a potential initial + quote-char. */ + parser->completion_word = LS_TOKEN_STOKEN (parser->lexer.current).ptr; + } + else if (advance_word) + { + /* Advance the completion word past any whitespace. */ + parser->completion_word = PARSER_STREAM (parser); + } + + return parser->lexer.current; } /* Return the next token without consuming the current token. */ @@ -794,26 +982,21 @@ linespec_lexer_peek_token (linespec_parser *parser) linespec_token next; const char *saved_stream = PARSER_STREAM (parser); linespec_token saved_token = parser->lexer.current; + int saved_completion_quote_char = parser->completion_quote_char; + const char *saved_completion_quote_end = parser->completion_quote_end; + const char *saved_completion_word = parser->completion_word; next = linespec_lexer_consume_token (parser); PARSER_STREAM (parser) = saved_stream; parser->lexer.current = saved_token; + parser->completion_quote_char = saved_completion_quote_char; + parser->completion_quote_end = saved_completion_quote_end; + parser->completion_word = saved_completion_word; return next; } /* Helper functions. */ -/* Add SAL to SALS. */ - -static void -add_sal_to_sals_basic (struct symtabs_and_lines *sals, - struct symtab_and_line *sal) -{ - ++sals->nelts; - sals->sals = xrealloc (sals->sals, sals->nelts * sizeof (sals->sals[0])); - sals->sals[sals->nelts - 1] = *sal; -} - /* Add SAL to SALS, and also update SELF->CANONICAL_NAMES to reflect the new sal, if needed. If not NULL, SYMNAME is the name of the symbol to use when constructing the new canonical name. @@ -823,23 +1006,23 @@ add_sal_to_sals_basic (struct symtabs_and_lines *sals, static void add_sal_to_sals (struct linespec_state *self, - struct symtabs_and_lines *sals, + std::vector *sals, struct symtab_and_line *sal, const char *symname, int literal_canonical) { - add_sal_to_sals_basic (sals, sal); + sals->push_back (*sal); if (self->canonical) { struct linespec_canonical_name *canonical; - self->canonical_names = xrealloc (self->canonical_names, - (sals->nelts - * sizeof (*self->canonical_names))); - canonical = &self->canonical_names[sals->nelts - 1]; + self->canonical_names = XRESIZEVEC (struct linespec_canonical_name, + self->canonical_names, + sals->size ()); + canonical = &self->canonical_names[sals->size () - 1]; if (!literal_canonical && sal->symtab) { - const char *fullname = symtab_to_fullname (sal->symtab); + symtab_to_fullname (sal->symtab); /* Note that the filter doesn't have to be a valid linespec input. We only apply the ":LINE" treatment to Ada for @@ -869,7 +1052,7 @@ add_sal_to_sals (struct linespec_state *self, static hashval_t hash_address_entry (const void *p) { - const struct address_entry *aep = p; + const struct address_entry *aep = (const struct address_entry *) p; hashval_t hash; hash = iterative_hash_object (aep->pspace, 0); @@ -881,8 +1064,8 @@ hash_address_entry (const void *p) static int eq_address_entry (const void *a, const void *b) { - const struct address_entry *aea = a; - const struct address_entry *aeb = b; + const struct address_entry *aea = (const struct address_entry *) a; + const struct address_entry *aeb = (const struct address_entry *) b; return aea->pspace == aeb->pspace && aea->addr == aeb->addr; } @@ -910,81 +1093,26 @@ maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr) return 1; } -/* A callback function and the additional data to call it with. */ - -struct symbol_and_data_callback -{ - /* The callback to use. */ - symbol_found_callback_ftype *callback; - - /* Data to be passed to the callback. */ - void *data; -}; - -/* A helper for iterate_over_all_matching_symtabs that is used to - restrict calls to another callback to symbols representing inline - symbols only. */ - -static int -iterate_inline_only (struct symbol *sym, void *d) -{ - if (SYMBOL_INLINED (sym)) - { - struct symbol_and_data_callback *cad = d; - - return cad->callback (sym, cad->data); - } - return 1; /* Continue iterating. */ -} - -/* Some data for the expand_symtabs_matching callback. */ - -struct symbol_matcher_data -{ - /* The lookup name against which symbol name should be compared. */ - const char *lookup_name; - - /* The routine to be used for comparison. */ - symbol_name_cmp_ftype symbol_name_cmp; -}; - -/* A helper for iterate_over_all_matching_symtabs that is passed as a - callback to the expand_symtabs_matching method. */ - -static int -iterate_name_matcher (const char *name, void *d) -{ - const struct symbol_matcher_data *data = d; - - if (data->symbol_name_cmp (name, data->lookup_name) == 0) - return 1; /* Expand this symbol's symbol table. */ - return 0; /* Skip this symbol. */ -} - /* A helper that walks over all matching symtabs in all objfiles and calls CALLBACK for each symbol matching NAME. If SEARCH_PSPACE is not NULL, then the search is restricted to just that program - space. If INCLUDE_INLINE is nonzero then symbols representing + space. If INCLUDE_INLINE is true then symbols representing inlined instances of functions will be included in the result. */ static void -iterate_over_all_matching_symtabs (struct linespec_state *state, - const char *name, - const domain_enum domain, - symbol_found_callback_ftype *callback, - void *data, - struct program_space *search_pspace, - int include_inline) +iterate_over_all_matching_symtabs + (struct linespec_state *state, const char *name, const domain_enum domain, + struct program_space *search_pspace, bool include_inline, + gdb::function_view callback) { struct objfile *objfile; struct program_space *pspace; - struct symbol_matcher_data matcher_data; - matcher_data.lookup_name = name; - matcher_data.symbol_name_cmp = - state->language->la_get_symbol_name_cmp != NULL - ? state->language->la_get_symbol_name_cmp (name) - : strcmp_iw; + /* The routine to be used for comparison. */ + symbol_name_cmp_ftype symbol_name_cmp + = (state->language->la_get_symbol_name_cmp != NULL + ? state->language->la_get_symbol_name_cmp (name) + : strcmp_iw); ALL_PSPACES (pspace) { @@ -1000,20 +1128,24 @@ iterate_over_all_matching_symtabs (struct linespec_state *state, struct compunit_symtab *cu; if (objfile->sf) - objfile->sf->qf->expand_symtabs_matching (objfile, NULL, - iterate_name_matcher, - NULL, ALL_DOMAIN, - &matcher_data); + objfile->sf->qf->expand_symtabs_matching + (objfile, + NULL, + [&] (const char *symbol_name) + { + return symbol_name_cmp (symbol_name, name) == 0; + }, + NULL, + ALL_DOMAIN); ALL_OBJFILE_COMPUNITS (objfile, cu) { struct symtab *symtab = COMPUNIT_FILETABS (cu); - iterate_over_file_blocks (symtab, name, domain, callback, data); + iterate_over_file_blocks (symtab, name, domain, callback); if (include_inline) { - struct symbol_and_data_callback cad = { callback, data }; struct block *block; int i; @@ -1023,7 +1155,14 @@ iterate_over_all_matching_symtabs (struct linespec_state *state, { block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), i); state->language->la_iterate_over_symbols - (block, name, domain, iterate_inline_only, &cad); + (block, name, domain, [&] (symbol *sym) + { + /* Restrict calls to CALLBACK to symbols + representing inline symbols only. */ + if (SYMBOL_INLINED (sym)) + return callback (sym); + return true; + }); } } } @@ -1052,16 +1191,16 @@ get_current_search_block (void) /* Iterate over static and global blocks. */ static void -iterate_over_file_blocks (struct symtab *symtab, - const char *name, domain_enum domain, - symbol_found_callback_ftype *callback, void *data) +iterate_over_file_blocks + (struct symtab *symtab, const char *name, domain_enum domain, + gdb::function_view callback) { struct block *block; for (block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), STATIC_BLOCK); block != NULL; block = BLOCK_SUPERBLOCK (block)) - LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback, data); + LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback); } /* A helper for find_method. This finds all methods in type T which @@ -1135,7 +1274,7 @@ find_methods (struct type *t, const char *name, /* Find an instance of the character C in the string S that is outside of all parenthesis pairs, single-quoted strings, and double-quoted strings. Also, ignore the char within a template name, like a ',' - within foo. */ + within foo, while considering C++ operator') && depth > 0) depth--; + else if (*scan == 'o' && !quoted && depth == 0) + { + /* Handle C++ operator names. */ + if (strncmp (scan, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0) + { + scan += CP_OPERATOR_LEN; + if (*scan == c) + return scan; + while (isspace (*scan)) + { + ++scan; + if (*scan == c) + return scan; + } + if (*scan == '\0') + break; + + switch (*scan) + { + /* Skip over one less than the appropriate number of + characters: the for loop will skip over the last + one. */ + case '<': + if (scan[1] == '<') + { + scan++; + if (*scan == c) + return scan; + } + break; + case '>': + if (scan[1] == '>') + { + scan++; + if (*scan == c) + return scan; + } + break; + } + } + } } return 0; @@ -1216,7 +1396,7 @@ canonical_to_fullform (const struct linespec_canonical_name *canonical) static void filter_results (struct linespec_state *self, - struct symtabs_and_lines *result, + std::vector *result, VEC (const_char_ptr) *filters) { int i; @@ -1224,12 +1404,9 @@ filter_results (struct linespec_state *self, for (i = 0; VEC_iterate (const_char_ptr, filters, i, name); ++i) { - struct linespec_sals lsal; - int j; + linespec_sals lsal; - memset (&lsal, 0, sizeof (lsal)); - - for (j = 0; j < result->nelts; ++j) + for (size_t j = 0; j < result->size (); ++j) { const struct linespec_canonical_name *canonical; char *fullform; @@ -1240,15 +1417,15 @@ filter_results (struct linespec_state *self, cleanup = make_cleanup (xfree, fullform); if (strcmp (name, fullform) == 0) - add_sal_to_sals_basic (&lsal.sals, &result->sals[j]); + lsal.sals.push_back ((*result)[j]); do_cleanups (cleanup); } - if (lsal.sals.nelts > 0) + if (!lsal.sals.empty ()) { lsal.canonical = xstrdup (name); - VEC_safe_push (linespec_sals, self->canonical->sals, &lsal); + self->canonical->lsals.push_back (std::move (lsal)); } } @@ -1259,13 +1436,13 @@ filter_results (struct linespec_state *self, static void convert_results_to_lsals (struct linespec_state *self, - struct symtabs_and_lines *result) + std::vector *result) { struct linespec_sals lsal; lsal.canonical = NULL; - lsal.sals = *result; - VEC_safe_push (linespec_sals, self->canonical->sals, &lsal); + lsal.sals = std::move (*result); + self->canonical->lsals.push_back (std::move (lsal)); } /* A structure that contains two string representations of a struct @@ -1295,8 +1472,8 @@ struct decode_line_2_item static int decode_line_2_compare_items (const void *ap, const void *bp) { - const struct decode_line_2_item *a = ap; - const struct decode_line_2_item *b = bp; + const struct decode_line_2_item *a = (const struct decode_line_2_item *) ap; + const struct decode_line_2_item *b = (const struct decode_line_2_item *) bp; int retval; retval = strcmp (a->displayform, b->displayform); @@ -1313,25 +1490,25 @@ decode_line_2_compare_items (const void *ap, const void *bp) static void decode_line_2 (struct linespec_state *self, - struct symtabs_and_lines *result, + std::vector *result, const char *select_mode) { - char *args, *prompt; + char *args; + const char *prompt; int i; struct cleanup *old_chain; VEC (const_char_ptr) *filters = NULL; - struct get_number_or_range_state state; struct decode_line_2_item *items; int items_count; gdb_assert (select_mode != multiple_symbols_all); gdb_assert (self->canonical != NULL); - gdb_assert (result->nelts >= 1); + gdb_assert (!result->empty ()); old_chain = make_cleanup (VEC_cleanup (const_char_ptr), &filters); /* Prepare ITEMS array. */ - items_count = result->nelts; + items_count = result->size (); items = XNEWVEC (struct decode_line_2_item, items_count); make_cleanup (xfree, items); for (i = 0; i < items_count; ++i) @@ -1401,12 +1578,10 @@ decode_line_2 (struct linespec_state *self, if (args == 0 || *args == 0) error_no_arg (_("one or more choice numbers")); - init_number_or_range (&state, args); - while (!state.finished) + number_or_range_parser parser (args); + while (!parser.finished ()) { - int num; - - num = get_number_or_range (&state); + int num = parser.get_number (); if (num == 0) error (_("canceled")); @@ -1511,10 +1686,9 @@ unexpected_linespec_error (linespec_parser *parser) || token.type == LSTOKEN_KEYWORD) { char *string; - struct cleanup *cleanup; string = copy_token_string (token); - cleanup = make_cleanup (xfree, string); + make_cleanup (xfree, string); throw_error (GENERIC_ERROR, _("malformed linespec error: unexpected %s, \"%s\""), token_type_strings[token.type], string); @@ -1548,6 +1722,17 @@ source_file_not_found_error (const char *name) throw_error (NOT_FOUND_ERROR, _("No source file named %s."), name); } +/* Unless at EIO, save the current stream position as completion word + point, and consume the next token. */ + +static linespec_token +save_stream_and_consume_token (linespec_parser *parser) +{ + if (linespec_lexer_peek_token (parser).type != LSTOKEN_EOI) + parser->completion_word = PARSER_STREAM (parser); + return linespec_lexer_consume_token (parser); +} + /* See description in linespec.h. */ struct line_offset @@ -1575,6 +1760,26 @@ linespec_parse_line_offset (const char *string) return line_offset; } +/* In completion mode, if the user is still typing the number, there's + no possible completion to offer. But if there's already input past + the number, setup to expect NEXT. */ + +static void +set_completion_after_number (linespec_parser *parser, + linespec_complete_what next) +{ + if (*PARSER_STREAM (parser) == ' ') + { + parser->completion_word = skip_spaces (PARSER_STREAM (parser) + 1); + parser->complete_what = next; + } + else + { + parser->completion_word = PARSER_STREAM (parser); + parser->complete_what = linespec_complete_what::NOTHING; + } +} + /* Parse the basic_spec in PARSER's input. */ static void @@ -1590,11 +1795,20 @@ linespec_parse_basic (linespec_parser *parser) token = linespec_lexer_lex_one (parser); /* If it is EOI or KEYWORD, issue an error. */ - if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI) - unexpected_linespec_error (parser); + if (token.type == LSTOKEN_KEYWORD) + { + parser->complete_what = linespec_complete_what::NOTHING; + unexpected_linespec_error (parser); + } + else if (token.type == LSTOKEN_EOI) + { + unexpected_linespec_error (parser); + } /* If it is a LSTOKEN_NUMBER, we have an offset. */ else if (token.type == LSTOKEN_NUMBER) { + set_completion_after_number (parser, linespec_complete_what::KEYWORD); + /* Record the line offset and get the next token. */ name = copy_token_string (token); cleanup = make_cleanup (xfree, name); @@ -1606,7 +1820,10 @@ linespec_parse_basic (linespec_parser *parser) /* If the next token is a comma, stop parsing and return. */ if (token.type == LSTOKEN_COMMA) - return; + { + parser->complete_what = linespec_complete_what::NOTHING; + return; + } /* If the next token is anything but EOI or KEYWORD, issue an error. */ @@ -1619,12 +1836,58 @@ linespec_parse_basic (linespec_parser *parser) /* Next token must be LSTOKEN_STRING. */ if (token.type != LSTOKEN_STRING) - unexpected_linespec_error (parser); + { + parser->complete_what = linespec_complete_what::NOTHING; + unexpected_linespec_error (parser); + } /* The current token will contain the name of a function, method, or label. */ - name = copy_token_string (token); - cleanup = make_cleanup (xfree, name); + name = copy_token_string (token); + cleanup = make_cleanup (free_current_contents, &name); + + if (parser->completion_tracker != NULL) + { + /* If the function name ends with a ":", then this may be an + incomplete "::" scope operator instead of a label separator. + E.g., + "b klass:" + which should expand to: + "b klass::method()" + + Do a tentative completion assuming the later. If we find + completions, advance the stream past the colon token and make + it part of the function name/token. */ + + if (!parser->completion_quote_char + && strcmp (PARSER_STREAM (parser), ":") == 0) + { + completion_tracker tmp_tracker; + const char *source_filename + = PARSER_EXPLICIT (parser)->source_filename; + + linespec_complete_function (tmp_tracker, + parser->completion_word, + source_filename); + + if (tmp_tracker.have_completions ()) + { + PARSER_STREAM (parser)++; + LS_TOKEN_STOKEN (token).length++; + + xfree (name); + name = savestring (parser->completion_word, + (PARSER_STREAM (parser) + - parser->completion_word)); + } + } + + PARSER_EXPLICIT (parser)->function_name = name; + discard_cleanups (cleanup); + } + else + { + /* XXX Reindent before pushing. */ /* Try looking it up as a function/method. */ find_linespec_symbols (PARSER_STATE (parser), @@ -1684,11 +1947,19 @@ linespec_parse_basic (linespec_parser *parser) return; } } + } + + int previous_qc = parser->completion_quote_char; /* Get the next token. */ token = linespec_lexer_consume_token (parser); - if (token.type == LSTOKEN_COLON) + if (token.type == LSTOKEN_EOI) + { + if (previous_qc && !parser->completion_quote_char) + parser->complete_what = linespec_complete_what::KEYWORD; + } + else if (token.type == LSTOKEN_COLON) { /* User specified a label or a lineno. */ token = linespec_lexer_consume_token (parser); @@ -1697,17 +1968,56 @@ linespec_parse_basic (linespec_parser *parser) { /* User specified an offset. Record the line offset and get the next token. */ + set_completion_after_number (parser, linespec_complete_what::KEYWORD); + name = copy_token_string (token); cleanup = make_cleanup (xfree, name); PARSER_EXPLICIT (parser)->line_offset = linespec_parse_line_offset (name); do_cleanups (cleanup); - /* Ge the next token. */ + /* Get the next token. */ token = linespec_lexer_consume_token (parser); } + else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL) + { + parser->complete_what = linespec_complete_what::LABEL; + } else if (token.type == LSTOKEN_STRING) { + parser->complete_what = linespec_complete_what::LABEL; + + /* If we have text after the label separated by whitespace + (e.g., "b func():lab i"), don't consider it part of + the label. In completion mode that should complete to + "if", in normal mode, the 'i' should be treated as + garbage. */ + if (parser->completion_quote_char == '\0') + { + const char *ptr = LS_TOKEN_STOKEN (token).ptr; + for (size_t i = 0; i < LS_TOKEN_STOKEN (token).length; i++) + { + if (ptr[i] == ' ') + { + LS_TOKEN_STOKEN (token).length = i; + PARSER_STREAM (parser) = skip_spaces (ptr + i + 1); + break; + } + } + } + + if (parser->completion_tracker != NULL) + { + if (PARSER_STREAM (parser)[-1] == ' ') + { + parser->completion_word = PARSER_STREAM (parser); + parser->complete_what = linespec_complete_what::KEYWORD; + } + } + else + { + /* XXX Reindent before pushing. */ + /* Grab a copy of the label's name and look it up. */ name = copy_token_string (token); cleanup = make_cleanup (xfree, name); @@ -1730,8 +2040,10 @@ linespec_parse_basic (linespec_parser *parser) name); } + } + /* Check for a line offset. */ - token = linespec_lexer_consume_token (parser); + token = save_stream_and_consume_token (parser); if (token.type == LSTOKEN_COLON) { /* Get the next token. */ @@ -1741,7 +2053,7 @@ linespec_parse_basic (linespec_parser *parser) if (token.type != LSTOKEN_NUMBER) unexpected_linespec_error (parser); - /* Record the lione offset and get the next token. */ + /* Record the line offset and get the next token. */ name = copy_token_string (token); cleanup = make_cleanup (xfree, name); @@ -1776,8 +2088,9 @@ canonicalize_linespec (struct linespec_state *state, const linespec_p ls) return; /* Save everything as an explicit location. */ - canon = state->canonical->location + state->canonical->location = new_explicit_location (&ls->explicit_loc); + canon = state->canonical->location.get (); explicit_loc = get_explicit_location (canon); if (explicit_loc->label_name != NULL) @@ -1810,18 +2123,12 @@ canonicalize_linespec (struct linespec_state *state, const linespec_p ls) /* Given a line offset in LS, construct the relevant SALs. */ -static struct symtabs_and_lines +static std::vector create_sals_line_offset (struct linespec_state *self, linespec_p ls) { - struct symtabs_and_lines values; - struct symtab_and_line val; int use_default = 0; - init_sal (&val); - values.sals = NULL; - values.nelts = 0; - /* This is where we need to make sure we have good defaults. We must guarantee that this section of code is never executed when we are called with just a function name, since @@ -1841,10 +2148,12 @@ create_sals_line_offset (struct linespec_state *self, fullname = symtab_to_fullname (self->default_symtab); VEC_pop (symtab_ptr, ls->file_symtabs); VEC_free (symtab_ptr, ls->file_symtabs); - ls->file_symtabs = collect_symtabs_from_filename (fullname); + ls->file_symtabs = collect_symtabs_from_filename (fullname, + self->search_pspace); use_default = 1; } + symtab_and_line val; val.line = ls->explicit_loc.line_offset.offset; switch (ls->explicit_loc.line_offset.sign) { @@ -1868,27 +2177,20 @@ create_sals_line_offset (struct linespec_state *self, break; /* No need to adjust val.line. */ } + std::vector values; if (self->list_mode) - decode_digits_list_mode (self, ls, &values, val); + values = decode_digits_list_mode (self, ls, val); else { struct linetable_entry *best_entry = NULL; - int *filter; - const struct block **blocks; - struct cleanup *cleanup; - struct symtabs_and_lines intermediate_results; int i, j; - intermediate_results.sals = NULL; - intermediate_results.nelts = 0; - - decode_digits_ordinary (self, ls, val.line, &intermediate_results, - &best_entry); - if (intermediate_results.nelts == 0 && best_entry != NULL) - decode_digits_ordinary (self, ls, best_entry->line, - &intermediate_results, &best_entry); - - cleanup = make_cleanup (xfree, intermediate_results.sals); + std::vector intermediate_results + = decode_digits_ordinary (self, ls, val.line, &best_entry); + if (intermediate_results.empty () && best_entry != NULL) + intermediate_results = decode_digits_ordinary (self, ls, + best_entry->line, + &best_entry); /* For optimized code, the compiler can scatter one source line across disjoint ranges of PC values, even when no duplicate @@ -1900,24 +2202,22 @@ create_sals_line_offset (struct linespec_state *self, above, we see if there are other PCs that are in the same block. If yes, the other PCs are filtered out. */ - filter = XNEWVEC (int, intermediate_results.nelts); - make_cleanup (xfree, filter); - blocks = XNEWVEC (const struct block *, intermediate_results.nelts); - make_cleanup (xfree, blocks); + gdb::def_vector filter (intermediate_results.size ()); + gdb::def_vector blocks (intermediate_results.size ()); - for (i = 0; i < intermediate_results.nelts; ++i) + for (i = 0; i < intermediate_results.size (); ++i) { - set_current_program_space (intermediate_results.sals[i].pspace); + set_current_program_space (intermediate_results[i].pspace); filter[i] = 1; - blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc, - intermediate_results.sals[i].section); + blocks[i] = block_for_pc_sect (intermediate_results[i].pc, + intermediate_results[i].section); } - for (i = 0; i < intermediate_results.nelts; ++i) + for (i = 0; i < intermediate_results.size (); ++i) { if (blocks[i] != NULL) - for (j = i + 1; j < intermediate_results.nelts; ++j) + for (j = i + 1; j < intermediate_results.size (); ++j) { if (blocks[j] == blocks[i]) { @@ -1927,7 +2227,7 @@ create_sals_line_offset (struct linespec_state *self, } } - for (i = 0; i < intermediate_results.nelts; ++i) + for (i = 0; i < intermediate_results.size (); ++i) if (filter[i]) { struct symbol *sym = (blocks[i] @@ -1935,18 +2235,16 @@ create_sals_line_offset (struct linespec_state *self, : NULL); if (self->funfirstline) - skip_prologue_sal (&intermediate_results.sals[i]); + skip_prologue_sal (&intermediate_results[i]); /* Make sure the line matches the request, not what was found. */ - intermediate_results.sals[i].line = val.line; - add_sal_to_sals (self, &values, &intermediate_results.sals[i], + intermediate_results[i].line = val.line; + add_sal_to_sals (self, &values, &intermediate_results[i], sym ? SYMBOL_NATURAL_NAME (sym) : NULL, 0); } - - do_cleanups (cleanup); } - if (values.nelts == 0) + if (values.empty ()) { if (ls->explicit_loc.source_filename) throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."), @@ -1961,17 +2259,16 @@ create_sals_line_offset (struct linespec_state *self, /* Convert the given ADDRESS into SaLs. */ -static struct symtabs_and_lines +static std::vector convert_address_location_to_sals (struct linespec_state *self, CORE_ADDR address) { - struct symtab_and_line sal; - struct symtabs_and_lines sals = {NULL, 0}; - - sal = find_pc_line (address, 0); + symtab_and_line sal = find_pc_line (address, 0); sal.pc = address; sal.section = find_pc_overlay (address); sal.explicit_pc = 1; + + std::vector sals; add_sal_to_sals (self, &sals, &sal, core_addr_to_string (address), 1); return sals; @@ -1979,10 +2276,10 @@ convert_address_location_to_sals (struct linespec_state *self, /* Create and return SALs from the linespec LS. */ -static struct symtabs_and_lines +static std::vector convert_linespec_to_sals (struct linespec_state *state, linespec_p ls) { - struct symtabs_and_lines sals = {NULL, 0}; + std::vector sals; if (ls->labels.label_symbols != NULL) { @@ -2072,36 +2369,39 @@ convert_linespec_to_sals (struct linespec_state *state, linespec_p ls) canonicalize_linespec (state, ls); - if (sals.nelts > 0 && state->canonical != NULL) + if (!sals.empty () && state->canonical != NULL) state->canonical->pre_expanded = 1; return sals; } -/* Convert the explicit location EXPLICIT_LOC into SaLs. */ +/* Build RESULT from the explicit location components SOURCE_FILENAME, + FUNCTION_NAME, LABEL_NAME and LINE_OFFSET. */ -static struct symtabs_and_lines -convert_explicit_location_to_sals (struct linespec_state *self, - linespec_p result, - const struct explicit_location *explicit_loc) +static void +convert_explicit_location_to_linespec (struct linespec_state *self, + linespec_p result, + const char *source_filename, + const char *function_name, + const char *label_name, + struct line_offset line_offset) { VEC (symbolp) *symbols, *labels; VEC (bound_minimal_symbol_d) *minimal_symbols; - if (explicit_loc->source_filename != NULL) + if (source_filename != NULL) { TRY { result->file_symtabs - = symtabs_from_filename (explicit_loc->source_filename); + = symtabs_from_filename (source_filename, self->search_pspace); } CATCH (except, RETURN_MASK_ERROR) { - source_file_not_found_error (explicit_loc->source_filename); + source_file_not_found_error (source_filename); } END_CATCH - result->explicit_loc.source_filename - = xstrdup (explicit_loc->source_filename); + result->explicit_loc.source_filename = xstrdup (source_filename); } else { @@ -2109,41 +2409,53 @@ convert_explicit_location_to_sals (struct linespec_state *self, VEC_safe_push (symtab_ptr, result->file_symtabs, NULL); } - if (explicit_loc->function_name != NULL) + if (function_name != NULL) { find_linespec_symbols (self, result->file_symtabs, - explicit_loc->function_name, &symbols, + function_name, &symbols, &minimal_symbols); if (symbols == NULL && minimal_symbols == NULL) - symbol_not_found_error (explicit_loc->function_name, + symbol_not_found_error (function_name, result->explicit_loc.source_filename); - result->explicit_loc.function_name - = xstrdup (explicit_loc->function_name); + result->explicit_loc.function_name = xstrdup (function_name); result->function_symbols = symbols; result->minimal_symbols = minimal_symbols; } - if (explicit_loc->label_name != NULL) + if (label_name != NULL) { symbols = NULL; labels = find_label_symbols (self, result->function_symbols, - &symbols, explicit_loc->label_name); + &symbols, label_name); if (labels == NULL) undefined_label_error (result->explicit_loc.function_name, - explicit_loc->label_name); + label_name); - result->explicit_loc.label_name = xstrdup (explicit_loc->label_name); + result->explicit_loc.label_name = xstrdup (label_name); result->labels.label_symbols = labels; result->labels.function_symbols = symbols; } - if (explicit_loc->line_offset.sign != LINE_OFFSET_UNKNOWN) - result->explicit_loc.line_offset = explicit_loc->line_offset; + if (line_offset.sign != LINE_OFFSET_UNKNOWN) + result->explicit_loc.line_offset = line_offset; +} - return convert_linespec_to_sals (self, result); +/* Convert the explicit location EXPLICIT_LOC into SaLs. */ + +static std::vector +convert_explicit_location_to_sals (struct linespec_state *self, + linespec_p result, + const struct explicit_location *explicit_loc) +{ + convert_explicit_location_to_linespec (self, result, + explicit_loc->source_filename, + explicit_loc->function_name, + explicit_loc->label_name, + explicit_loc->line_offset); + return convert_linespec_to_sals (self, result); } /* Parse a string that specifies a linespec. @@ -2196,11 +2508,10 @@ convert_explicit_location_to_sals (struct linespec_state *self, /* Parse the linespec in ARG. */ -static struct symtabs_and_lines +static std::vector parse_linespec (linespec_parser *parser, const char *arg) { linespec_token token; - struct symtabs_and_lines values; struct gdb_exception file_exception = exception_none; struct cleanup *cleanup; @@ -2208,7 +2519,8 @@ parse_linespec (linespec_parser *parser, const char *arg) IDEs to work around bugs in the previous parser by quoting the entire linespec, so we attempt to deal with this nicely. */ parser->is_quote_enclosed = 0; - if (!is_ada_operator (arg) + if (parser->completion_tracker == NULL + && !is_ada_operator (arg) && strchr (linespec_quote_characters, *arg) != NULL) { const char *end; @@ -2225,20 +2537,35 @@ parse_linespec (linespec_parser *parser, const char *arg) parser->lexer.saved_arg = arg; parser->lexer.stream = arg; + parser->completion_word = arg; + parser->complete_what = linespec_complete_what::FUNCTION; /* Initialize the default symtab and line offset. */ initialize_defaults (&PARSER_STATE (parser)->default_symtab, &PARSER_STATE (parser)->default_line); /* Objective-C shortcut. */ - values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg); - if (values.sals != NULL) - return values; + if (parser->completion_tracker == NULL) + { + std::vector values + = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg); + if (!values.empty ()) + return values; + } + else + { + /* "-"/"+" is either an objc selector, or a number. There's + nothing to complete the latter to, so just let the caller + complete on functions, which finds objc selectors, if there's + any. */ + if ((arg[0] == '-' || arg[0] == '+') && arg[1] == '\0') + return {}; + } /* Start parsing. */ /* Get the first token. */ - token = linespec_lexer_lex_one (parser); + token = linespec_lexer_consume_token (parser); /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER. */ if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$') @@ -2246,7 +2573,8 @@ parse_linespec (linespec_parser *parser, const char *arg) char *var; /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ - VEC_safe_push (symtab_ptr, PARSER_RESULT (parser)->file_symtabs, NULL); + if (parser->completion_tracker == NULL) + VEC_safe_push (symtab_ptr, PARSER_RESULT (parser)->file_symtabs, NULL); /* User specified a convenience variable or history value. */ var = copy_token_string (token); @@ -2265,8 +2593,16 @@ parse_linespec (linespec_parser *parser, const char *arg) goto convert_to_sals; } } + else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL) + { + /* Let the default linespec_complete_what::FUNCTION kick in. */ + unexpected_linespec_error (parser); + } else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER) - unexpected_linespec_error (parser); + { + parser->complete_what = linespec_complete_what::NOTHING; + unexpected_linespec_error (parser); + } /* Shortcut: If the next token is not LSTOKEN_COLON, we know that this token cannot represent a filename. */ @@ -2284,7 +2620,8 @@ parse_linespec (linespec_parser *parser, const char *arg) TRY { PARSER_RESULT (parser)->file_symtabs - = symtabs_from_filename (user_filename); + = symtabs_from_filename (user_filename, + PARSER_STATE (parser)->search_pspace); } CATCH (ex, RETURN_MASK_ERROR) { @@ -2313,8 +2650,9 @@ parse_linespec (linespec_parser *parser, const char *arg) } } /* If the next token is not EOI, KEYWORD, or COMMA, issue an error. */ - else if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD - && token.type != LSTOKEN_COMMA) + else if (parser->completion_tracker == NULL + && (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD + && token.type != LSTOKEN_COMMA)) { /* TOKEN is the _next_ token, not the one currently in the parser. Consuming the token will give the correct error message. */ @@ -2330,7 +2668,8 @@ parse_linespec (linespec_parser *parser, const char *arg) /* Parse the rest of the linespec. */ linespec_parse_basic (parser); - if (PARSER_RESULT (parser)->function_symbols == NULL + if (parser->completion_tracker == NULL + && PARSER_RESULT (parser)->function_symbols == NULL && PARSER_RESULT (parser)->labels.label_symbols == NULL && PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN && PARSER_RESULT (parser)->minimal_symbols == NULL) @@ -2351,13 +2690,23 @@ parse_linespec (linespec_parser *parser, const char *arg) if necessary. */ token = linespec_lexer_lex_one (parser); if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD) - PARSER_STREAM (parser) = LS_TOKEN_STOKEN (token).ptr; + unexpected_linespec_error (parser); + else if (token.type == LSTOKEN_KEYWORD) + { + /* Setup the completion word past the keyword. Lexing never + advances past a keyword automatically, so skip it + manually. */ + parser->completion_word + = skip_spaces (skip_to_space (PARSER_STREAM (parser))); + parser->complete_what = linespec_complete_what::EXPRESSION; + } /* Convert the data in PARSER_RESULT to SALs. */ - values = convert_linespec_to_sals (PARSER_STATE (parser), + if (parser->completion_tracker == NULL) + return convert_linespec_to_sals (PARSER_STATE (parser), PARSER_RESULT (parser)); - return values; + return {}; } @@ -2366,6 +2715,7 @@ parse_linespec (linespec_parser *parser, const char *arg) static void linespec_state_constructor (struct linespec_state *self, int flags, const struct language_defn *language, + struct program_space *search_pspace, struct symtab *default_symtab, int default_line, struct linespec_result *canonical) @@ -2374,6 +2724,7 @@ linespec_state_constructor (struct linespec_state *self, self->language = language; self->funfirstline = (flags & DECODE_LINE_FUNFIRSTLINE) ? 1 : 0; self->list_mode = (flags & DECODE_LINE_LIST_MODE) ? 1 : 0; + self->search_pspace = search_pspace; self->default_symtab = default_symtab; self->default_line = default_line; self->canonical = canonical; @@ -2388,6 +2739,7 @@ linespec_state_constructor (struct linespec_state *self, static void linespec_parser_new (linespec_parser *parser, int flags, const struct language_defn *language, + struct program_space *search_pspace, struct symtab *default_symtab, int default_line, struct linespec_result *canonical) @@ -2397,6 +2749,7 @@ linespec_parser_new (linespec_parser *parser, memset (PARSER_RESULT (parser), 0, sizeof (struct linespec)); PARSER_EXPLICIT (parser)->line_offset.sign = LINE_OFFSET_UNKNOWN; linespec_state_constructor (PARSER_STATE (parser), flags, language, + search_pspace, default_symtab, default_line, canonical); } @@ -2450,7 +2803,7 @@ linespec_lex_to_end (char **stringp) if (stringp == NULL || *stringp == NULL) return; - linespec_parser_new (&parser, 0, current_language, NULL, 0, NULL); + linespec_parser_new (&parser, 0, current_language, NULL, NULL, 0, NULL); cleanup = make_cleanup (linespec_parser_delete, &parser); parser.lexer.saved_arg = *stringp; PARSER_STREAM (&parser) = orig = *stringp; @@ -2470,14 +2823,352 @@ linespec_lex_to_end (char **stringp) do_cleanups (cleanup); } +/* See linespec.h. */ + +void +linespec_complete_function (completion_tracker &tracker, + const char *function, + const char *source_filename) +{ + complete_symbol_mode mode = complete_symbol_mode::LINESPEC; + + if (source_filename != NULL) + { + collect_file_symbol_completion_matches (tracker, mode, + function, function, + source_filename); + } + else + collect_symbol_completion_matches (tracker, mode, function, function); +} + +/* Helper for complete_linespec to simplify it. SOURCE_FILENAME is + only meaningful if COMPONENT is FUNCTION. */ + +static void +complete_linespec_component (linespec_parser *parser, + completion_tracker &tracker, + const char *text, + linespec_complete_what component, + const char *source_filename) +{ + if (component == linespec_complete_what::KEYWORD) + { + complete_on_enum (tracker, linespec_keywords, text, text); + } + else if (component == linespec_complete_what::EXPRESSION) + { + const char *word + = advance_to_expression_complete_word_point (tracker, text); + complete_expression (tracker, text, word); + } + else if (component == linespec_complete_what::FUNCTION) + { + completion_list fn_list; + + linespec_complete_function (tracker, text, source_filename); + if (source_filename == NULL) + { + /* Haven't seen a source component, like in "b + file.c:function[TAB]". Maybe this wasn't a function, but + a filename instead, like "b file.[TAB]". */ + fn_list = complete_source_filenames (text); + } + + /* If we only have a single filename completion, append a ':' for + the user, since that's the only thing that can usefully follow + the filename. */ + if (fn_list.size () == 1 && !tracker.have_completions ()) + { + char *fn = fn_list[0].release (); + + /* If we also need to append a quote char, it needs to be + appended before the ':'. Append it now, and make ':' the + new "quote" char. */ + if (tracker.quote_char ()) + { + char quote_char_str[2] = { tracker.quote_char () }; + + fn = reconcat (fn, fn, quote_char_str, (char *) NULL); + tracker.set_quote_char (':'); + } + else + fn = reconcat (fn, fn, ":", (char *) NULL); + fn_list[0].reset (fn); + + /* Tell readline to skip appending a space. */ + tracker.set_suppress_append_ws (true); + } + tracker.add_completions (std::move (fn_list)); + } +} + +/* Helper for linespec_complete_label. Find labels that match + LABEL_NAME in the function symbols listed in the PARSER, and add + them to the tracker. */ + +static void +complete_label (completion_tracker &tracker, + linespec_parser *parser, + const char *label_name) +{ + VEC (symbolp) *label_function_symbols = NULL; + VEC (symbolp) *labels + = find_label_symbols (PARSER_STATE (parser), + PARSER_RESULT (parser)->function_symbols, + &label_function_symbols, + label_name, true); + + symbol *label; + for (int ix = 0; + VEC_iterate (symbolp, labels, ix, label); ++ix) + { + char *match = xstrdup (SYMBOL_SEARCH_NAME (label)); + tracker.add_completion (gdb::unique_xmalloc_ptr (match)); + } + VEC_free (symbolp, labels); +} + +/* See linespec.h. */ + +void +linespec_complete_label (completion_tracker &tracker, + const struct language_defn *language, + const char *source_filename, + const char *function_name, + const char *label_name) +{ + linespec_parser parser; + struct cleanup *cleanup; + + linespec_parser_new (&parser, 0, language, NULL, NULL, 0, NULL); + cleanup = make_cleanup (linespec_parser_delete, &parser); + + line_offset unknown_offset = { 0, LINE_OFFSET_UNKNOWN }; + + TRY + { + convert_explicit_location_to_linespec (PARSER_STATE (&parser), + PARSER_RESULT (&parser), + source_filename, + function_name, + NULL, unknown_offset); + } + CATCH (ex, RETURN_MASK_ERROR) + { + do_cleanups (cleanup); + return; + } + END_CATCH + + complete_label (tracker, &parser, label_name); + + do_cleanups (cleanup); +} + +/* See description in linespec.h. */ + +void +linespec_complete (completion_tracker &tracker, const char *text) +{ + linespec_parser parser; + struct cleanup *cleanup; + const char *orig = text; + + linespec_parser_new (&parser, 0, current_language, NULL, NULL, 0, NULL); + cleanup = make_cleanup (linespec_parser_delete, &parser); + parser.lexer.saved_arg = text; + PARSER_STREAM (&parser) = text; + + parser.completion_tracker = &tracker; + PARSER_STATE (&parser)->is_linespec = 1; + + /* Parse as much as possible. parser.completion_word will hold + furthest completion point we managed to parse to. */ + TRY + { + parse_linespec (&parser, text); + } + CATCH (except, RETURN_MASK_ERROR) + { + } + END_CATCH + + if (parser.completion_quote_char != '\0' + && parser.completion_quote_end != NULL + && parser.completion_quote_end[1] == '\0') + { + /* If completing a quoted string with the cursor right at + terminating quote char, complete the completion word without + interpretation, so that readline advances the cursor one + whitespace past the quote, even if there's no match. This + makes these cases behave the same: + + before: "b function()" + after: "b function() " + + before: "b 'function()'" + after: "b 'function()' " + + and trusts the user in this case: + + before: "b 'not_loaded_function_yet()'" + after: "b 'not_loaded_function_yet()' " + */ + parser.complete_what = linespec_complete_what::NOTHING; + parser.completion_quote_char = '\0'; + + gdb::unique_xmalloc_ptr text_copy + (xstrdup (parser.completion_word)); + tracker.add_completion (std::move (text_copy)); + } + + tracker.set_quote_char (parser.completion_quote_char); + + if (parser.complete_what == linespec_complete_what::LABEL) + { + parser.complete_what = linespec_complete_what::NOTHING; + + const char *func_name = PARSER_EXPLICIT (&parser)->function_name; + + VEC (symbolp) *function_symbols; + VEC (bound_minimal_symbol_d) *minimal_symbols; + find_linespec_symbols (PARSER_STATE (&parser), + PARSER_RESULT (&parser)->file_symtabs, + func_name, + &function_symbols, &minimal_symbols); + + PARSER_RESULT (&parser)->function_symbols = function_symbols; + PARSER_RESULT (&parser)->minimal_symbols = minimal_symbols; + + complete_label (tracker, &parser, parser.completion_word); + } + else if (parser.complete_what == linespec_complete_what::FUNCTION) + { + /* While parsing/lexing, we didn't know whether the completion + word completes to a unique function/source name already or + not. + + E.g.: + "b function() " + may need to complete either to: + "b function() const" + or to: + "b function() if/thread/task" + + Or, this: + "b foo t" + may need to complete either to: + "b foo template_fun()" + with "foo" being the template function's return type, or to: + "b foo thread/task" + + Or, this: + "b file" + may need to complete either to a source file name: + "b file.c" + or this, also a filename, but a unique completion: + "b file.c:" + or to a function name: + "b file_function" + + Address that by completing assuming source or function, and + seeing if we find a completion that matches exactly the + completion word. If so, then it must be a function (see note + below) and we advance the completion word to the end of input + and switch to KEYWORD completion mode. + + Note: if we find a unique completion for a source filename, + then it won't match the completion word, because the LCD will + contain a trailing ':'. And if we're completing at or after + the ':', then complete_linespec_component won't try to + complete on source filenames. */ + + const char *text = parser.completion_word; + const char *word = parser.completion_word; + + complete_linespec_component (&parser, tracker, + parser.completion_word, + linespec_complete_what::FUNCTION, + PARSER_EXPLICIT (&parser)->source_filename); + + parser.complete_what = linespec_complete_what::NOTHING; + + if (tracker.quote_char ()) + { + /* The function/file name was not close-quoted, so this + can't be a keyword. Note: complete_linespec_component + may have swapped the original quote char for ':' when we + get here, but that still indicates the same. */ + } + else if (!tracker.have_completions ()) + { + size_t key_start; + size_t wordlen = strlen (parser.completion_word); + + key_start + = string_find_incomplete_keyword_at_end (linespec_keywords, + parser.completion_word, + wordlen); + + if (key_start != -1 + || (wordlen > 0 + && parser.completion_word[wordlen - 1] == ' ')) + { + parser.completion_word += key_start; + parser.complete_what = linespec_complete_what::KEYWORD; + } + } + else if (tracker.completes_to_completion_word (word)) + { + /* Skip the function and complete on keywords. */ + parser.completion_word += strlen (word); + parser.complete_what = linespec_complete_what::KEYWORD; + tracker.discard_completions (); + } + } + + tracker.advance_custom_word_point_by (parser.completion_word - orig); + + complete_linespec_component (&parser, tracker, + parser.completion_word, + parser.complete_what, + PARSER_EXPLICIT (&parser)->source_filename); + + /* If we're past the "filename:function:label:offset" linespec, and + didn't find any match, then assume the user might want to create + a pending breakpoint anyway and offer the keyword + completions. */ + if (!parser.completion_quote_char + && (parser.complete_what == linespec_complete_what::FUNCTION + || parser.complete_what == linespec_complete_what::LABEL + || parser.complete_what == linespec_complete_what::NOTHING) + && !tracker.have_completions ()) + { + const char *end + = parser.completion_word + strlen (parser.completion_word); + + if (end > orig && end[-1] == ' ') + { + tracker.advance_custom_word_point_by (end - parser.completion_word); + + complete_linespec_component (&parser, tracker, end, + linespec_complete_what::KEYWORD, + NULL); + } + } + + do_cleanups (cleanup); +} + /* A helper function for decode_line_full and decode_line_1 to - turn LOCATION into symtabs_and_lines. */ + turn LOCATION into std::vector. */ -static struct symtabs_and_lines +static std::vector event_location_to_sals (linespec_parser *parser, const struct event_location *location) { - struct symtabs_and_lines result = {NULL, 0}; + std::vector result; switch (event_location_type (location)) { @@ -2497,9 +3188,27 @@ event_location_to_sals (linespec_parser *parser, break; case ADDRESS_LOCATION: - result - = convert_address_location_to_sals (PARSER_STATE (parser), - get_address_location (location)); + { + const char *addr_string = get_address_string_location (location); + CORE_ADDR addr = get_address_location (location); + + if (addr_string != NULL) + { + char *expr = xstrdup (addr_string); + const char *const_expr = expr; + struct cleanup *cleanup = make_cleanup (xfree, expr); + + addr = linespec_expression_to_pc (&const_expr); + if (PARSER_STATE (parser)->canonical != NULL) + PARSER_STATE (parser)->canonical->location + = copy_event_location (location); + + do_cleanups (cleanup); + } + + result = convert_address_location_to_sals (PARSER_STATE (parser), + addr); + } break; case EXPLICIT_LOCATION: @@ -2529,12 +3238,12 @@ event_location_to_sals (linespec_parser *parser, void decode_line_full (const struct event_location *location, int flags, + struct program_space *search_pspace, struct symtab *default_symtab, int default_line, struct linespec_result *canonical, const char *select_mode, const char *filter) { - struct symtabs_and_lines result; struct cleanup *cleanups; VEC (const_char_ptr) *filters = NULL; linespec_parser parser; @@ -2549,24 +3258,27 @@ decode_line_full (const struct event_location *location, int flags, || select_mode == multiple_symbols_cancel); gdb_assert ((flags & DECODE_LINE_LIST_MODE) == 0); - linespec_parser_new (&parser, flags, current_language, default_symtab, + linespec_parser_new (&parser, flags, current_language, + search_pspace, default_symtab, default_line, canonical); cleanups = make_cleanup (linespec_parser_delete, &parser); - save_current_program_space (); - result = event_location_to_sals (&parser, location); + scoped_restore_current_program_space restore_pspace; + + std::vector result = event_location_to_sals (&parser, + location); state = PARSER_STATE (&parser); - gdb_assert (result.nelts == 1 || canonical->pre_expanded); + gdb_assert (result.size () == 1 || canonical->pre_expanded); canonical->pre_expanded = 1; /* Arrange for allocated canonical names to be freed. */ - if (result.nelts > 0) + if (!result.empty ()) { int i; make_cleanup (xfree, state->canonical_names); - for (i = 0; i < result.nelts; ++i) + for (i = 0; i < result.size (); ++i) { gdb_assert (state->canonical_names[i].suffix != NULL); make_cleanup (xfree, state->canonical_names[i].suffix); @@ -2575,7 +3287,7 @@ decode_line_full (const struct event_location *location, int flags, if (select_mode == NULL) { - if (ui_out_is_mi_like_p (interp_ui_out (top_level_interpreter ()))) + if (interp_ui_out (top_level_interpreter ())->is_mi_like_p ()) select_mode = multiple_symbols_all; else select_mode = multiple_symbols_select_mode (); @@ -2600,21 +3312,24 @@ decode_line_full (const struct event_location *location, int flags, /* See linespec.h. */ -struct symtabs_and_lines +std::vector decode_line_1 (const struct event_location *location, int flags, + struct program_space *search_pspace, struct symtab *default_symtab, int default_line) { - struct symtabs_and_lines result; linespec_parser parser; struct cleanup *cleanups; - linespec_parser_new (&parser, flags, current_language, default_symtab, + linespec_parser_new (&parser, flags, current_language, + search_pspace, default_symtab, default_line, NULL); cleanups = make_cleanup (linespec_parser_delete, &parser); - save_current_program_space (); - result = event_location_to_sals (&parser, location); + scoped_restore_current_program_space restore_pspace; + + std::vector result = event_location_to_sals (&parser, + location); do_cleanups (cleanups); return result; @@ -2622,58 +3337,48 @@ decode_line_1 (const struct event_location *location, int flags, /* See linespec.h. */ -struct symtabs_and_lines +std::vector decode_line_with_current_source (char *string, int flags) { - struct symtabs_and_lines sals; - struct symtab_and_line cursal; - struct event_location *location; - struct cleanup *cleanup; - if (string == 0) error (_("Empty line specification.")); /* We use whatever is set as the current source line. We do not try and get a default source symtab+line or it will recursively call us! */ - cursal = get_current_source_symtab_and_line (); + symtab_and_line cursal = get_current_source_symtab_and_line (); - location = string_to_event_location (&string, current_language); - cleanup = make_cleanup_delete_event_location (location); - sals = decode_line_1 (location, flags, - cursal.symtab, cursal.line); + event_location_up location = string_to_event_location (&string, + current_language); + std::vector sals + = decode_line_1 (location.get (), flags, NULL, cursal.symtab, cursal.line); if (*string) error (_("Junk at end of line specification: %s"), string); - do_cleanups (cleanup); return sals; } /* See linespec.h. */ -struct symtabs_and_lines +std::vector decode_line_with_last_displayed (char *string, int flags) { - struct symtabs_and_lines sals; - struct event_location *location; - struct cleanup *cleanup; - if (string == 0) error (_("Empty line specification.")); - location = string_to_event_location (&string, current_language); - cleanup = make_cleanup_delete_event_location (location); - if (last_displayed_sal_is_valid ()) - sals = decode_line_1 (location, flags, - get_last_displayed_symtab (), - get_last_displayed_line ()); - else - sals = decode_line_1 (location, flags, (struct symtab *) NULL, 0); + event_location_up location = string_to_event_location (&string, + current_language); + std::vector sals + = (last_displayed_sal_is_valid () + ? decode_line_1 (location.get (), flags, NULL, + get_last_displayed_symtab (), + get_last_displayed_line ()) + : decode_line_1 (location.get (), flags, NULL, + (struct symtab *) NULL, 0)); if (*string) error (_("Junk at end of line specification: %s"), string); - do_cleanups (cleanup); return sals; } @@ -2725,12 +3430,11 @@ linespec_expression_to_pc (const char **exp_ptr) than one method that could represent the selector, then use some of the existing C++ code to let the user choose one. */ -static struct symtabs_and_lines +static std::vector decode_objc (struct linespec_state *self, linespec_p ls, const char *arg) { struct collect_info info; VEC (const_char_ptr) *symbol_names = NULL; - struct symtabs_and_lines values; const char *new_argptr; struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr), &symbol_names); @@ -2741,24 +3445,23 @@ decode_objc (struct linespec_state *self, linespec_p ls, const char *arg) make_cleanup (VEC_cleanup (symtab_ptr), &info.file_symtabs); info.result.symbols = NULL; info.result.minimal_symbols = NULL; - values.nelts = 0; - values.sals = NULL; new_argptr = find_imps (arg, &symbol_names); if (VEC_empty (const_char_ptr, symbol_names)) { do_cleanups (cleanup); - return values; + return {}; } add_all_symbol_names_from_pspace (&info, NULL, symbol_names); + std::vector values; if (!VEC_empty (symbolp, info.result.symbols) || !VEC_empty (bound_minimal_symbol_d, info.result.minimal_symbols)) { char *saved_arg; - saved_arg = alloca (new_argptr - arg + 1); + saved_arg = (char *) alloca (new_argptr - arg + 1); memcpy (saved_arg, arg, new_argptr - arg); saved_arg[new_argptr - arg] = '\0'; @@ -2791,49 +3494,76 @@ decode_objc (struct linespec_state *self, linespec_p ls, const char *arg) return values; } -/* An instance of this type is used when collecting prefix symbols for - decode_compound. */ +namespace { -struct decode_compound_collector +/* A function object that serves as symbol_found_callback_ftype + callback for iterate_over_symbols. This is used by + lookup_prefix_sym to collect type symbols. */ +class decode_compound_collector { - /* The result vector. */ - VEC (symbolp) *symbols; +public: + decode_compound_collector () + : m_symbols (NULL) + { + m_unique_syms = htab_create_alloc (1, htab_hash_pointer, + htab_eq_pointer, NULL, + xcalloc, xfree); + } + + ~decode_compound_collector () + { + if (m_unique_syms != NULL) + htab_delete (m_unique_syms); + } + /* Releases ownership of the collected symbols and returns them. */ + VEC (symbolp) *release_symbols () + { + VEC (symbolp) *res = m_symbols; + m_symbols = NULL; + return res; + } + + /* Callable as a symbol_found_callback_ftype callback. */ + bool operator () (symbol *sym); + +private: /* A hash table of all symbols we found. We use this to avoid adding any symbol more than once. */ - htab_t unique_syms; -}; + htab_t m_unique_syms; -/* A callback for iterate_over_symbols that is used by - lookup_prefix_sym to collect type symbols. */ + /* The result vector. */ + VEC (symbolp) *m_symbols; +}; -static int -collect_one_symbol (struct symbol *sym, void *d) +bool +decode_compound_collector::operator () (symbol *sym) { - struct decode_compound_collector *collector = d; void **slot; struct type *t; if (SYMBOL_CLASS (sym) != LOC_TYPEDEF) - return 1; /* Continue iterating. */ + return true; /* Continue iterating. */ t = SYMBOL_TYPE (sym); t = check_typedef (t); if (TYPE_CODE (t) != TYPE_CODE_STRUCT && TYPE_CODE (t) != TYPE_CODE_UNION && TYPE_CODE (t) != TYPE_CODE_NAMESPACE) - return 1; /* Continue iterating. */ + return true; /* Continue iterating. */ - slot = htab_find_slot (collector->unique_syms, sym, INSERT); + slot = htab_find_slot (m_unique_syms, sym, INSERT); if (!*slot) { *slot = sym; - VEC_safe_push (symbolp, collector->symbols, sym); + VEC_safe_push (symbolp, m_symbols, sym); } - return 1; /* Continue iterating. */ + return true; /* Continue iterating. */ } +} // namespace + /* Return any symbols corresponding to CLASS_NAME in FILE_SYMTABS. */ static VEC (symbolp) * @@ -2842,28 +3572,16 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs, { int ix; struct symtab *elt; - struct decode_compound_collector collector; - struct cleanup *outer; - struct cleanup *cleanup; - - collector.symbols = NULL; - outer = make_cleanup (VEC_cleanup (symbolp), &collector.symbols); - - collector.unique_syms = htab_create_alloc (1, htab_hash_pointer, - htab_eq_pointer, NULL, - xcalloc, xfree); - cleanup = make_cleanup_htab_delete (collector.unique_syms); + decode_compound_collector collector; for (ix = 0; VEC_iterate (symtab_ptr, file_symtabs, ix, elt); ++ix) { if (elt == NULL) { iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN, - collect_one_symbol, &collector, - NULL, 0); + NULL, false, collector); iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN, - collect_one_symbol, &collector, - NULL, 0); + NULL, false, collector); } else { @@ -2871,16 +3589,12 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs, been filtered out earlier. */ gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup); set_current_program_space (SYMTAB_PSPACE (elt)); - iterate_over_file_blocks (elt, class_name, STRUCT_DOMAIN, - collect_one_symbol, &collector); - iterate_over_file_blocks (elt, class_name, VAR_DOMAIN, - collect_one_symbol, &collector); + iterate_over_file_blocks (elt, class_name, STRUCT_DOMAIN, collector); + iterate_over_file_blocks (elt, class_name, VAR_DOMAIN, collector); } } - do_cleanups (cleanup); - discard_cleanups (outer); - return collector.symbols; + return collector.release_symbols (); } /* A qsort comparison function for symbols. The resulting order does @@ -2890,8 +3604,8 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs, static int compare_symbols (const void *a, const void *b) { - struct symbol * const *sa = a; - struct symbol * const *sb = b; + struct symbol * const *sa = (struct symbol * const*) a; + struct symbol * const *sb = (struct symbol * const*) b; uintptr_t uia, uib; uia = (uintptr_t) SYMTAB_PSPACE (symbol_symtab (*sa)); @@ -2918,8 +3632,10 @@ compare_symbols (const void *a, const void *b) static int compare_msymbols (const void *a, const void *b) { - const struct bound_minimal_symbol *sa = a; - const struct bound_minimal_symbol *sb = b; + const struct bound_minimal_symbol *sa + = (const struct bound_minimal_symbol *) a; + const struct bound_minimal_symbol *sb + = (const struct bound_minimal_symbol *) b; uintptr_t uia, uib; uia = (uintptr_t) sa->objfile->pspace; @@ -3084,71 +3800,106 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs, -/* This object is used when collecting all matching symtabs. */ +namespace { + +/* This function object is a callback for iterate_over_symtabs, used + when collecting all matching symtabs. */ -struct symtab_collector +class symtab_collector { +public: + symtab_collector () + { + m_symtabs = NULL; + m_symtab_table = htab_create (1, htab_hash_pointer, htab_eq_pointer, + NULL); + } + + ~symtab_collector () + { + if (m_symtab_table != NULL) + htab_delete (m_symtab_table); + } + + /* Callable as a symbol_found_callback_ftype callback. */ + bool operator () (symtab *sym); + + /* Releases ownership of the collected symtabs and returns them. */ + VEC (symtab_ptr) *release_symtabs () + { + VEC (symtab_ptr) *res = m_symtabs; + m_symtabs = NULL; + return res; + } + +private: /* The result vector of symtabs. */ - VEC (symtab_ptr) *symtabs; + VEC (symtab_ptr) *m_symtabs; /* This is used to ensure the symtabs are unique. */ - htab_t symtab_table; + htab_t m_symtab_table; }; -/* Callback for iterate_over_symtabs. */ - -static int -add_symtabs_to_list (struct symtab *symtab, void *d) +bool +symtab_collector::operator () (struct symtab *symtab) { - struct symtab_collector *data = d; void **slot; - slot = htab_find_slot (data->symtab_table, symtab, INSERT); + slot = htab_find_slot (m_symtab_table, symtab, INSERT); if (!*slot) { *slot = symtab; - VEC_safe_push (symtab_ptr, data->symtabs, symtab); + VEC_safe_push (symtab_ptr, m_symtabs, symtab); } - return 0; + return false; } -/* Given a file name, return a VEC of all matching symtabs. */ +} // namespace + +/* Given a file name, return a VEC of all matching symtabs. If + SEARCH_PSPACE is not NULL, the search is restricted to just that + program space. */ static VEC (symtab_ptr) * -collect_symtabs_from_filename (const char *file) +collect_symtabs_from_filename (const char *file, + struct program_space *search_pspace) { - struct symtab_collector collector; - struct cleanup *cleanups; - struct program_space *pspace; - - collector.symtabs = NULL; - collector.symtab_table = htab_create (1, htab_hash_pointer, htab_eq_pointer, - NULL); - cleanups = make_cleanup_htab_delete (collector.symtab_table); + symtab_collector collector; /* Find that file's data. */ - ALL_PSPACES (pspace) - { - if (pspace->executing_startup) - continue; + if (search_pspace == NULL) + { + struct program_space *pspace; - set_current_program_space (pspace); - iterate_over_symtabs (file, add_symtabs_to_list, &collector); - } + ALL_PSPACES (pspace) + { + if (pspace->executing_startup) + continue; - do_cleanups (cleanups); - return collector.symtabs; + set_current_program_space (pspace); + iterate_over_symtabs (file, collector); + } + } + else + { + set_current_program_space (search_pspace); + iterate_over_symtabs (file, collector); + } + + return collector.release_symtabs (); } -/* Return all the symtabs associated to the FILENAME. */ +/* Return all the symtabs associated to the FILENAME. If SEARCH_PSPACE is + not NULL, the search is restricted to just that program space. */ static VEC (symtab_ptr) * -symtabs_from_filename (const char *filename) +symtabs_from_filename (const char *filename, + struct program_space *search_pspace) { VEC (symtab_ptr) *result; - result = collect_symtabs_from_filename (filename); + result = collect_symtabs_from_filename (filename, search_pspace); if (VEC_empty (symtab_ptr, result)) { @@ -3185,9 +3936,10 @@ find_function_symbols (struct linespec_state *state, /* Try NAME as an Objective-C selector. */ find_imps (name, &symbol_names); if (!VEC_empty (const_char_ptr, symbol_names)) - add_all_symbol_names_from_pspace (&info, NULL, symbol_names); + add_all_symbol_names_from_pspace (&info, state->search_pspace, + symbol_names); else - add_matching_symbols_to_info (name, &info, NULL); + add_matching_symbols_to_info (name, &info, state->search_pspace); do_cleanups (cleanup); @@ -3218,27 +3970,28 @@ find_linespec_symbols (struct linespec_state *state, VEC (symbolp) **symbols, VEC (bound_minimal_symbol_d) **minsyms) { - struct cleanup *cleanup; - char *canon; + demangle_result_storage demangle_storage; + std::string ada_lookup_storage; const char *lookup_name; - cleanup = demangle_for_lookup (name, state->language->la_language, - &lookup_name); if (state->language->la_language == language_ada) { /* In Ada, the symbol lookups are performed using the encoded name rather than the demangled name. */ - lookup_name = ada_name_for_lookup (name); - make_cleanup (xfree, (void *) lookup_name); + ada_lookup_storage = ada_name_for_lookup (name); + lookup_name = ada_lookup_storage.c_str (); } - - canon = cp_canonicalize_string_no_typedefs (lookup_name); - if (canon != NULL) + else { - lookup_name = canon; - make_cleanup (xfree, canon); + lookup_name = demangle_for_lookup (name, + state->language->la_language, + demangle_storage); } + std::string canon = cp_canonicalize_string_no_typedefs (lookup_name); + if (!canon.empty ()) + lookup_name = canon.c_str (); + /* It's important to not call expand_symtabs_matching unnecessarily as it can really slow things down (by unnecessarily expanding potentially 1000s of symtabs, which when debugging some apps can @@ -3257,7 +4010,7 @@ find_linespec_symbols (struct linespec_state *state, if (VEC_empty (symbolp, *symbols) && VEC_empty (bound_minimal_symbol_d, *minsyms)) { - char *klass, *method; + std::string klass, method; const char *last, *p, *scope_op; VEC (symbolp) *classes; @@ -3265,12 +4018,6 @@ find_linespec_symbols (struct linespec_state *state, name into namespaces${SCOPE_OPERATOR}class_name and method_name. */ scope_op = "::"; p = find_toplevel_string (lookup_name, scope_op); - if (p == NULL) - { - /* No C++ scope operator. Try Java. */ - scope_op = "."; - p = find_toplevel_string (lookup_name, scope_op); - } last = NULL; while (p != NULL) @@ -3283,35 +4030,29 @@ find_linespec_symbols (struct linespec_state *state, we already attempted to lookup the entire name as a symbol and failed. */ if (last == NULL) - { - do_cleanups (cleanup); - return; - } + return; /* LOOKUP_NAME points to the class name. LAST points to the method name. */ - klass = xmalloc ((last - lookup_name + 1) * sizeof (char)); - make_cleanup (xfree, klass); - strncpy (klass, lookup_name, last - lookup_name); - klass[last - lookup_name] = '\0'; + klass = std::string (lookup_name, last - lookup_name); /* Skip past the scope operator. */ last += strlen (scope_op); - method = xmalloc ((strlen (last) + 1) * sizeof (char)); - make_cleanup (xfree, method); - strcpy (method, last); + method = last; /* Find a list of classes named KLASS. */ - classes = lookup_prefix_sym (state, file_symtabs, klass); - make_cleanup (VEC_cleanup (symbolp), &classes); + classes = lookup_prefix_sym (state, file_symtabs, klass.c_str ()); + struct cleanup *old_chain + = make_cleanup (VEC_cleanup (symbolp), &classes); if (!VEC_empty (symbolp, classes)) { /* Now locate a list of suitable methods named METHOD. */ TRY { - find_method (state, file_symtabs, klass, method, classes, - symbols, minsyms); + find_method (state, file_symtabs, + klass.c_str (), method.c_str (), + classes, symbols, minsyms); } /* If successful, we're done. If NOT_FOUND_ERROR @@ -3323,18 +4064,68 @@ find_linespec_symbols (struct linespec_state *state, } END_CATCH } + + do_cleanups (old_chain); } +} - do_cleanups (cleanup); +/* Helper for find_label_symbols. Find all labels that match name + NAME in BLOCK. Return all labels that match in FUNCTION_SYMBOLS. + Return the actual function symbol in which the label was found in + LABEL_FUNC_RET. If COMPLETION_MODE is true, then NAME is + interpreted as a label name prefix. Otherwise, only a label named + exactly NAME match. */ + +static void +find_label_symbols_in_block (const struct block *block, + const char *name, struct symbol *fn_sym, + bool completion_mode, + VEC (symbolp) **result, + VEC (symbolp) **label_funcs_ret) +{ + if (completion_mode) + { + struct block_iterator iter; + struct symbol *sym; + size_t name_len = strlen (name); + + int (*cmp) (const char *, const char *, size_t); + cmp = case_sensitivity == case_sensitive_on ? strncmp : strncasecmp; + + ALL_BLOCK_SYMBOLS (block, iter, sym) + { + if (symbol_matches_domain (SYMBOL_LANGUAGE (sym), + SYMBOL_DOMAIN (sym), LABEL_DOMAIN) + && cmp (SYMBOL_SEARCH_NAME (sym), name, name_len) == 0) + { + VEC_safe_push (symbolp, *result, sym); + VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); + } + } + } + else + { + struct symbol *sym = lookup_symbol (name, block, LABEL_DOMAIN, 0).symbol; + + if (sym != NULL) + { + VEC_safe_push (symbolp, *result, sym); + VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); + } + } } -/* Return all labels named NAME in FUNCTION_SYMBOLS. Return the - actual function symbol in which the label was found in LABEL_FUNC_RET. */ +/* Return all labels that match name NAME in FUNCTION_SYMBOLS. Return + the actual function symbol in which the label was found in + LABEL_FUNC_RET. If COMPLETION_MODE is true, then NAME is + interpreted as a label name prefix. Otherwise, only labels named + exactly NAME match. */ static VEC (symbolp) * find_label_symbols (struct linespec_state *self, VEC (symbolp) *function_symbols, - VEC (symbolp) **label_funcs_ret, const char *name) + VEC (symbolp) **label_funcs_ret, const char *name, + bool completion_mode) { int ix; const struct block *block; @@ -3355,13 +4146,8 @@ find_label_symbols (struct linespec_state *self, return NULL; fn_sym = BLOCK_FUNCTION (block); - sym = lookup_symbol (name, block, LABEL_DOMAIN, 0).symbol; - - if (sym != NULL) - { - VEC_safe_push (symbolp, result, sym); - VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); - } + find_label_symbols_in_block (block, name, fn_sym, completion_mode, + &result, label_funcs_ret); } else { @@ -3370,13 +4156,9 @@ find_label_symbols (struct linespec_state *self, { set_current_program_space (SYMTAB_PSPACE (symbol_symtab (fn_sym))); block = SYMBOL_BLOCK_VALUE (fn_sym); - sym = lookup_symbol (name, block, LABEL_DOMAIN, 0).symbol; - if (sym != NULL) - { - VEC_safe_push (symbolp, result, sym); - VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); - } + find_label_symbols_in_block (block, name, fn_sym, completion_mode, + &result, label_funcs_ret); } } @@ -3387,10 +4169,9 @@ find_label_symbols (struct linespec_state *self, /* A helper for create_sals_line_offset that handles the 'list_mode' case. */ -static void +static std::vector decode_digits_list_mode (struct linespec_state *self, linespec_p ls, - struct symtabs_and_lines *values, struct symtab_and_line val) { int ix; @@ -3398,6 +4179,8 @@ decode_digits_list_mode (struct linespec_state *self, gdb_assert (self->list_mode); + std::vector values; + for (ix = 0; VEC_iterate (symtab_ptr, ls->file_symtabs, ix, elt); ++ix) { @@ -3414,28 +4197,28 @@ decode_digits_list_mode (struct linespec_state *self, val.pc = 0; val.explicit_line = 1; - add_sal_to_sals (self, values, &val, NULL, 0); + add_sal_to_sals (self, &values, &val, NULL, 0); } + + return values; } /* A helper for create_sals_line_offset that iterates over the symtabs, adding lines to the VEC. */ -static void +static std::vector decode_digits_ordinary (struct linespec_state *self, linespec_p ls, int line, - struct symtabs_and_lines *sals, struct linetable_entry **best_entry) { int ix; struct symtab *elt; + std::vector sals; for (ix = 0; VEC_iterate (symtab_ptr, ls->file_symtabs, ix, elt); ++ix) { - int i; - VEC (CORE_ADDR) *pcs; - CORE_ADDR pc; + std::vector pcs; /* The logic above should ensure this. */ gdb_assert (elt != NULL); @@ -3443,20 +4226,18 @@ decode_digits_ordinary (struct linespec_state *self, set_current_program_space (SYMTAB_PSPACE (elt)); pcs = find_pcs_for_symtab_line (elt, line, best_entry); - for (i = 0; VEC_iterate (CORE_ADDR, pcs, i, pc); ++i) + for (CORE_ADDR pc : pcs) { - struct symtab_and_line sal; - - init_sal (&sal); + symtab_and_line sal; sal.pspace = SYMTAB_PSPACE (elt); sal.symtab = elt; sal.line = line; sal.pc = pc; - add_sal_to_sals_basic (sals, &sal); + sals.push_back (std::move (sal)); } - - VEC_free (CORE_ADDR, pcs); } + + return sals; } @@ -3518,20 +4299,6 @@ linespec_parse_variable (struct linespec_state *self, const char *variable) } -/* A callback used to possibly add a symbol to the results. */ - -static int -collect_symbols (struct symbol *sym, void *data) -{ - struct collect_info *info = data; - - /* In list mode, add all matching symbols, regardless of class. - This allows the user to type "list a_global_variable". */ - if (SYMBOL_CLASS (sym) == LOC_BLOCK || info->state->list_mode) - VEC_safe_push (symbolp, info->result.symbols, sym); - return 1; /* Continue iterating. */ -} - /* We've found a minimal symbol MSYMBOL in OBJFILE to associate with our linespec; return the SAL in RESULT. This function should return SALs matching those from find_function_start_sal, otherwise false @@ -3540,7 +4307,7 @@ collect_symbols (struct symbol *sym, void *data) static void minsym_found (struct linespec_state *self, struct objfile *objfile, struct minimal_symbol *msymbol, - struct symtabs_and_lines *result) + std::vector *result) { struct gdbarch *gdbarch = get_objfile_arch (objfile); CORE_ADDR pc; @@ -3631,8 +4398,8 @@ classify_mtype (enum minimal_symbol_type t) static int compare_msyms (const void *a, const void *b) { - const bound_minimal_symbol_d *moa = a; - const bound_minimal_symbol_d *mob = b; + const bound_minimal_symbol_d *moa = (const bound_minimal_symbol_d *) a; + const bound_minimal_symbol_d *mob = (const bound_minimal_symbol_d *) b; enum minimal_symbol_type ta = MSYMBOL_TYPE (moa->minsym); enum minimal_symbol_type tb = MSYMBOL_TYPE (mob->minsym); @@ -3645,7 +4412,7 @@ compare_msyms (const void *a, const void *b) static void add_minsym (struct minimal_symbol *minsym, void *d) { - struct collect_minsyms *info = d; + struct collect_minsyms *info = (struct collect_minsyms *) d; bound_minimal_symbol_d mo; mo.minsym = minsym; @@ -3799,8 +4566,8 @@ add_matching_symbols_to_info (const char *name, if (elt == NULL) { iterate_over_all_matching_symtabs (info->state, name, VAR_DOMAIN, - collect_symbols, info, - pspace, 1); + pspace, true, [&] (symbol *sym) + { return info->add_symbol (sym); }); search_minsyms_for_name (info, name, pspace, NULL); } else if (pspace == NULL || pspace == SYMTAB_PSPACE (elt)) @@ -3811,8 +4578,8 @@ add_matching_symbols_to_info (const char *name, been filtered out earlier. */ gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup); set_current_program_space (SYMTAB_PSPACE (elt)); - iterate_over_file_blocks (elt, name, VAR_DOMAIN, - collect_symbols, info); + iterate_over_file_blocks (elt, name, VAR_DOMAIN, [&] (symbol *sym) + { return info->add_symbol (sym); }); /* If no new symbols were found in this iteration and this symtab is in assembler, we might actually be looking for a label for @@ -3843,7 +4610,7 @@ symbol_to_sal (struct symtab_and_line *result, { if (SYMBOL_CLASS (sym) == LOC_LABEL && SYMBOL_VALUE_ADDRESS (sym) != 0) { - init_sal (result); + *result = {}; result->symtab = symbol_symtab (sym); result->line = SYMBOL_LINE (sym); result->pc = SYMBOL_VALUE_ADDRESS (sym); @@ -3858,7 +4625,7 @@ symbol_to_sal (struct symtab_and_line *result, else if (SYMBOL_LINE (sym) != 0) { /* We know its line number. */ - init_sal (result); + *result = {}; result->symtab = symbol_symtab (sym); result->line = SYMBOL_LINE (sym); result->pspace = SYMTAB_PSPACE (result->symtab); @@ -3869,45 +4636,10 @@ symbol_to_sal (struct symtab_and_line *result, return 0; } -/* See the comment in linespec.h. */ - -void -init_linespec_result (struct linespec_result *lr) -{ - memset (lr, 0, sizeof (*lr)); -} - -/* See the comment in linespec.h. */ - -void -destroy_linespec_result (struct linespec_result *ls) -{ - int i; - struct linespec_sals *lsal; - - delete_event_location (ls->location); - for (i = 0; VEC_iterate (linespec_sals, ls->sals, i, lsal); ++i) - { - xfree (lsal->canonical); - xfree (lsal->sals.sals); - } - VEC_free (linespec_sals, ls->sals); -} - -/* Cleanup function for a linespec_result. */ - -static void -cleanup_linespec_result (void *a) -{ - destroy_linespec_result (a); -} - -/* See the comment in linespec.h. */ - -struct cleanup * -make_cleanup_destroy_linespec_result (struct linespec_result *ls) +linespec_result::~linespec_result () { - return make_cleanup (cleanup_linespec_result, ls); + for (linespec_sals &lsal : lsals) + xfree (lsal.canonical); } /* Return the quote characters permitted by the linespec parser. */