diff options
author | Keith Seitz <keiths@redhat.com> | 2012-04-05 18:50:29 +0000 |
---|---|---|
committer | Keith Seitz <keiths@redhat.com> | 2012-04-05 18:50:29 +0000 |
commit | 40e084e177cc5ab99a018c336d1cb2c923bd96db (patch) | |
tree | 00955be64fe84e7e82a41ab09349e61ff4ca678d /gdb | |
parent | 9d236627e73355de18e401a8dbaeb99997a2478b (diff) | |
download | gdb-40e084e177cc5ab99a018c336d1cb2c923bd96db.zip gdb-40e084e177cc5ab99a018c336d1cb2c923bd96db.tar.gz gdb-40e084e177cc5ab99a018c336d1cb2c923bd96db.tar.bz2 |
linespec rewrite:
* linespec.c (decode_compound): Remove.
(enum offset_relative_sign): New enum.
(struct line_offset): New struct.
(struct linespec): New struct.
(struct linespec_state): Move file_symtabs,
user_filename, and user_function into struct linespec.
Make result an anonymous struct holding vectors of
symbolp and minsym_and_objfile_d.
Add language member.
(enum ls_token_type): New enum.
(linespec_keywords): New array.
(struct ls_token): New struct.
(struct ls_parser): New struct.
(linespec_lexer_lex_number): New function.
(linespec_lexer_lex_keyword): New function.
(is_ada_operator): New function.
(skip_quote_char): New function.
(copy_token_string): New function.
(is_closing_quote_enclosed): New function.
(find_parameter_list_end): New function.
(linespec_lexer_lex_string): New function.
(linespec_lexer_lex_one): New function.
(linespec_lexer_consume_token): New function.
(linespec_lexer_peek_token): New function.
(cplusplus_error): Remove unused function.
(find_methods): Update comment.
(find_toplevel_char): Return const.
(is_objc_method_format): Remove unused function.
(find_toplevel_string): New function.
(is_linespec_boundary): Remove.
(symbol_not_found_error): New function.
(find_method_overload_end): Remove function.
(unexpected_linespec_error): New function.
(keep_name_info): Remove.
(linespec_parse_line_offset): New function.
(linespec_parse_basic): New function.
(canonicalize_linespec): New function.
(decode_line_internal): Remove.
(create_sals_line_offset): New function adapted from
decode_all_digits.
(convert_linespec_to_sals): New function.
(parse_linespec): New function.
(linespec_parser_new): New function.
(linespec_state_destructor): Change parameter type to
struct linespec_state *.
Add language parameter.
Remove freeing of moved members.
(linespec_parser_delete): New function.
(decode_line_full): Use parse_linespec and linespec_parser_new.
(decode_line_1): Likewise.
(decode_indirect): Rename to ...
(linespec_expression_to_pc): ... this and rewrite
to simply find CORE_ADDR, storing this result for later
conversion to SALs.
(locate_first_half): Remove.
(deocde_objc): Add parameter LS.
Initialize new struct collect_info members.
Handle minimal symbols, too.
(decode_compound): Delete.
(lookup_prefix_sym): Rewrite.
(compare_msymbols): New function.
(find_method): Rewrite.
Do not call cplusplus_error.
(symtabs_from_filename): Rewrite.
(collect_function_symbols): Delete.
(find_function_symbols): Rewrite without ARGPTR-style
processing.
(decode_all_digits): Delete. (Rewritten as create_sals_line_offset.)
(decode_dollar): Adapted and renamed to ...
(linespec_parse_variable): ... this.
(find_linespec_symbols): New function.
(decode_label): Adapted and renamed to ...
(find_label_symbols): ... this.
(decode_digits_list_mode): Add and use LS argument.
(decode_digits_ordinary): Likewise.
(collect_symbols): Do not collect SALs, just symbols and msymbols.
If in list mode, allow any symbol class. Otherwise, only
permit LOC_BLOCK symbols.
(minsym_found): Update comments.
(search_minsyms_for_name): Do not convert the matching symbol
into a SAL. Simply push the symbol and objfile into the
result vector.
(decode_variable): Delete. Contents adapted into
find_linespec_symbols.
* cp-support.c (SKIP_SPACE): Remove.
(operator_tokens): Remove unused global.
(cp_validate_operator): Remove.
* cp-support.h (cp_validate_operator): Remove declaration.
* gdb.base/advance.exp: Update error message for
"advance malformed" test.
* gdb.base/break.exp: Likewise for "breakpoint with
trailing garbage" test.
* gdb.base/hbreak2.exp: Likewise for "hardware breakpoint
with trailing garbage" test.
* gdb.base/jump.exp: Likewise for "jump with trailing
argument junk" test.
* gdb.base/sepdebug.exp: Likewise for "breakpoint with
trailng garbage" test.
* gdb.base/until.exp: Likewise for "malformed until" test.
* gdb.cp/ovldbreak.exp: Create the breakpoint table
for "breakpoint info (after setting on all)".
* gdb.cp/userdef.exp: Remove quoting for "break A2::operator+"
tests.
* gdb.cp/cplabel.cc: New file.
* gdb.cp/cplabel.exp: New test.
* gdb.linespec/ls-errs.c: New file.
* gdb.linespec/ls-errs.exp: New test.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 92 | ||||
-rw-r--r-- | gdb/cp-support.c | 117 | ||||
-rw-r--r-- | gdb/cp-support.h | 2 | ||||
-rw-r--r-- | gdb/linespec.c | 3267 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 20 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/advance.exp | 3 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/break.exp | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/hbreak2.exp | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/jump.exp | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/sepdebug.exp | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/until.exp | 3 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/cplabel.cc | 80 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/cplabel.exp | 40 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/ovldbreak.exp | 26 | ||||
-rw-r--r-- | gdb/testsuite/gdb.cp/userdef.exp | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.linespec/ls-errs.c | 29 | ||||
-rw-r--r-- | gdb/testsuite/gdb.linespec/ls-errs.exp | 189 |
17 files changed, 2271 insertions, 1609 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index bdfd9b7..4900388 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,95 @@ +2012-04-05 Keith Seitz <keiths@redhat.com> + + * linespec.c (decode_compound): Remove. + (enum offset_relative_sign): New enum. + (struct line_offset): New struct. + (struct linespec): New struct. + (struct linespec_state): Move file_symtabs, + user_filename, and user_function into struct linespec. + Make result an anonymous struct holding vectors of + symbolp and minsym_and_objfile_d. + Add language member. + (enum ls_token_type): New enum. + (linespec_keywords): New array. + (struct ls_token): New struct. + (struct ls_parser): New struct. + (linespec_lexer_lex_number): New function. + (linespec_lexer_lex_keyword): New function. + (is_ada_operator): New function. + (skip_quote_char): New function. + (copy_token_string): New function. + (is_closing_quote_enclosed): New function. + (find_parameter_list_end): New function. + (linespec_lexer_lex_string): New function. + (linespec_lexer_lex_one): New function. + (linespec_lexer_consume_token): New function. + (linespec_lexer_peek_token): New function. + (cplusplus_error): Remove unused function. + (find_methods): Update comment. + (find_toplevel_char): Return const. + (is_objc_method_format): Remove unused function. + (find_toplevel_string): New function. + (is_linespec_boundary): Remove. + (symbol_not_found_error): New function. + (find_method_overload_end): Remove function. + (unexpected_linespec_error): New function. + (keep_name_info): Remove. + (linespec_parse_line_offset): New function. + (linespec_parse_basic): New function. + (canonicalize_linespec): New function. + (decode_line_internal): Remove. + (create_sals_line_offset): New function adapted from + decode_all_digits. + (convert_linespec_to_sals): New function. + (parse_linespec): New function. + (linespec_parser_new): New function. + (linespec_state_destructor): Change parameter type to + struct linespec_state *. + Add language parameter. + Remove freeing of moved members. + (linespec_parser_delete): New function. + (decode_line_full): Use parse_linespec and linespec_parser_new. + (decode_line_1): Likewise. + (decode_indirect): Rename to ... + (linespec_expression_to_pc): ... this and rewrite + to simply find CORE_ADDR, storing this result for later + conversion to SALs. + (locate_first_half): Remove. + (deocde_objc): Add parameter LS. + Initialize new struct collect_info members. + Handle minimal symbols, too. + (decode_compound): Delete. + (lookup_prefix_sym): Rewrite. + (compare_msymbols): New function. + (find_method): Rewrite. + Do not call cplusplus_error. + (symtabs_from_filename): Rewrite. + (collect_function_symbols): Delete. + (find_function_symbols): Rewrite without ARGPTR-style + processing. + (decode_all_digits): Delete. (Rewritten as create_sals_line_offset.) + (decode_dollar): Adapted and renamed to ... + (linespec_parse_variable): ... this. + (find_linespec_symbols): New function. + (decode_label): Adapted and renamed to ... + (find_label_symbols): ... this. + (decode_digits_list_mode): Add and use LS argument. + (decode_digits_ordinary): Likewise. + (collect_symbols): Do not collect SALs, just symbols and msymbols. + If in list mode, allow any symbol class. Otherwise, only + permit LOC_BLOCK symbols. + (minsym_found): Update comments. + (search_minsyms_for_name): Do not convert the matching symbol + into a SAL. Simply push the symbol and objfile into the + result vector. + (decode_variable): Delete. Contents adapted into + find_linespec_symbols. + + * cp-support.c (SKIP_SPACE): Remove. + (operator_tokens): Remove unused global. + (cp_validate_operator): Remove. + * cp-support.h (cp_validate_operator): Remove declaration. + 2012-04-03 Jan Kratochvil <jan.kratochvil@redhat.com> * cp-valprint.c (cp_print_value_fields): Check valprint_check_validity diff --git a/gdb/cp-support.c b/gdb/cp-support.c index a41bcec..025b4de 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -73,19 +73,6 @@ struct cmd_list_element *maint_cplus_cmd_list = NULL; static void maint_cplus_command (char *arg, int from_tty); static void first_component_command (char *arg, int from_tty); -/* Operator validation. - NOTE: Multi-byte operators (usually the assignment variety - operator) must appear before the single byte version, i.e., "+=" - before "+". */ -static const char *operator_tokens[] = - { - "++", "+=", "+", "->*", "->", "--", "-=", "-", "*=", "*", - "/=", "/", "%=", "%", "!=", "==", "!", "&&", "<<=", "<<", - ">>=", ">>", "<=", "<", ">=", ">", "~", "&=", "&", "|=", - "||", "|", "^=", "^", "=", "()", "[]", ",", "new", "delete" - /* new[] and delete[] require special whitespace handling */ - }; - /* A list of typedefs which should not be substituted by replace_typedefs. */ static const char * const ignore_typedefs[] = { @@ -1459,110 +1446,6 @@ first_component_command (char *arg, int from_tty) extern initialize_file_ftype _initialize_cp_support; /* -Wmissing-prototypes */ -#define SKIP_SPACE(P) \ - do \ - { \ - while (*(P) == ' ' || *(P) == '\t') \ - ++(P); \ - } \ - while (0) - -/* Returns the length of the operator name or 0 if INPUT does not - point to a valid C++ operator. INPUT should start with - "operator". */ -int -cp_validate_operator (const char *input) -{ - int i; - char *copy; - const char *p; - struct expression *expr; - struct value *val; - volatile struct gdb_exception except; - - p = input; - - if (strncmp (p, "operator", 8) == 0) - { - int valid = 0; - - p += 8; - SKIP_SPACE (p); - for (i = 0; - i < sizeof (operator_tokens) / sizeof (operator_tokens[0]); - ++i) - { - int length = strlen (operator_tokens[i]); - - /* By using strncmp here, we MUST have operator_tokens - ordered! See additional notes where operator_tokens is - defined above. */ - if (strncmp (p, operator_tokens[i], length) == 0) - { - const char *op = p; - - valid = 1; - p += length; - - if (strncmp (op, "new", 3) == 0 - || strncmp (op, "delete", 6) == 0) - { - - /* Special case: new[] and delete[]. We must be - careful to swallow whitespace before/in "[]". */ - SKIP_SPACE (p); - - if (*p == '[') - { - ++p; - SKIP_SPACE (p); - if (*p == ']') - ++p; - else - valid = 0; - } - } - - if (valid) - return (p - input); - } - } - - /* Check input for a conversion operator. */ - - /* Skip past base typename. */ - while (*p != '*' && *p != '&' && *p != 0 && *p != ' ') - ++p; - SKIP_SPACE (p); - - /* Add modifiers '*' / '&'. */ - while (*p == '*' || *p == '&') - { - ++p; - SKIP_SPACE (p); - } - - /* Check for valid type. [Remember: input starts with - "operator".] */ - copy = savestring (input + 8, p - input - 8); - expr = NULL; - val = NULL; - TRY_CATCH (except, RETURN_MASK_ALL) - { - expr = parse_expression (copy); - val = evaluate_type (expr); - } - - xfree (copy); - if (expr) - xfree (expr); - - if (val != NULL && value_type (val) != NULL) - return (p - input); - } - - return 0; -} /* Implement "info vtbl". */ diff --git a/gdb/cp-support.h b/gdb/cp-support.h index 8898807..5988418 100644 --- a/gdb/cp-support.h +++ b/gdb/cp-support.h @@ -170,8 +170,6 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types, extern struct type *cp_lookup_rtti_type (const char *name, struct block *block); -extern int cp_validate_operator (const char *input); - /* Functions/variables from cp-namespace.c. */ extern int cp_is_anonymous (const char *namespace); diff --git a/gdb/linespec.c b/gdb/linespec.c index 1e9770e..228214b 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -63,12 +63,108 @@ struct address_entry CORE_ADDR addr; }; +/* A helper struct which just holds a minimal symbol and the object + file from which it came. */ + +typedef struct minsym_and_objfile +{ + struct minimal_symbol *minsym; + struct objfile *objfile; +} minsym_and_objfile_d; + +DEF_VEC_O (minsym_and_objfile_d); + +/* An enumeration of possible signs for a line offset. */ +enum offset_relative_sign +{ + /* No sign */ + LINE_OFFSET_NONE, + + /* A plus sign ("+") */ + LINE_OFFSET_PLUS, + + /* A minus sign ("-") */ + LINE_OFFSET_MINUS, + + /* A special "sign" for unspecified offset. */ + LINE_OFFSET_UNKNOWN +}; + +/* A line offset in a linespec. */ + +struct line_offset +{ + /* Line offset and any specified sign. */ + int offset; + enum offset_relative_sign sign; +}; + +/* A linespec. Elements of this structure are filled in by a parser + (either parse_linespec or some other function). The structure is + then converted into SALs by convert_linespec_to_sals. */ + +struct linespec +{ + /* An expression and the resulting PC. Specifying an expression + currently precludes the use of other members. */ + + /* The expression entered by the user. */ + char *expression; + + /* The resulting PC expression derived from evaluating EXPRESSION. */ + CORE_ADDR expr_pc; + + /* Any specified file symtabs. */ + + /* The user-supplied source filename or NULL if none was specified. */ + char *source_filename; + + /* The list of symtabs to search to which to limit the search. May not + be NULL. If SOURCE_FILENAME is NULL (no user-specified filename), + FILE_SYMTABS should contain one single NULL member. This will + cause the code to use the default symtab. */ + VEC (symtab_p) *file_symtabs; + + /* The name of a function or method and any matching symbols. */ + + /* The user-specified function name. If no function name was + supplied, this may be NULL. */ + char *function_name; + + /* A list of matching function symbols and minimal symbols. Both lists + may be NULL if no matching symbols were found. */ + VEC (symbolp) *function_symbols; + VEC (minsym_and_objfile_d) *minimal_symbols; + + /* The name of a label and matching symbols. */ + + /* The user-specified label name. */ + char *label_name; + + /* A structure of matching label symbols and the corresponding + function symbol in which the label was found. Both may be NULL + or both must be non-NULL. */ + struct + { + VEC (symbolp) *label_symbols; + VEC (symbolp) *function_symbols; + } labels; + + /* Line offset. It may be LINE_OFFSET_UNKNOWN, meaning that no + offset was specified. */ + struct line_offset line_offset; +}; +typedef struct linespec *linespec_p; + /* An instance of this is used to keep all state while linespec operates. This instance is passed around as a 'this' pointer to the various implementation methods. */ struct linespec_state { + /* The language in use during linespec processing. */ + const struct language_defn *language; + /* The program space as seen when the module was entered. */ struct program_space *program_space; @@ -78,19 +174,6 @@ struct linespec_state /* The default line to use. */ int default_line; - /* If the linespec started with "FILE:", this holds all the matching - symtabs. Otherwise, it will hold a single NULL entry, meaning - that the default symtab should be used. */ - VEC (symtab_p) *file_symtabs; - - /* If the linespec started with "FILE:", this holds an xmalloc'd - copy of "FILE". */ - char *user_filename; - - /* If the linespec is "FUNCTION:LABEL", this holds an xmalloc'd copy - of "FUNCTION". */ - char *user_function; - /* The 'funfirstline' value that was passed in to decode_line_1 or decode_line_full. */ int funfirstline; @@ -117,67 +200,128 @@ struct collect_info /* The linespec object in use. */ struct linespec_state *state; + /* A list of symtabs to which to restrict matches. */ + VEC (symtab_p) *file_symtabs; + /* The result being accumulated. */ - struct symtabs_and_lines result; + struct + { + VEC (symbolp) *symbols; + VEC (minsym_and_objfile_d) *minimal_symbols; + } result; }; -/* Prototypes for local functions. */ +/* Token types */ -static void initialize_defaults (struct symtab **default_symtab, - int *default_line); +enum ls_token_type +{ + /* A keyword */ + LSTOKEN_KEYWORD = 0, -static struct symtabs_and_lines decode_indirect (struct linespec_state *self, - char **argptr); + /* A colon "separator" */ + LSTOKEN_COLON, -static char *locate_first_half (char **argptr, int *is_quote_enclosed); + /* A string */ + LSTOKEN_STRING, -static struct symtabs_and_lines decode_objc (struct linespec_state *self, - char **argptr); + /* A number */ + LSTOKEN_NUMBER, + + /* A comma */ + LSTOKEN_COMMA, + + /* EOI (end of input) */ + LSTOKEN_EOI, + + /* Consumed token */ + LSTOKEN_CONSUMED +}; +typedef enum ls_token_type linespec_token_type; -static struct symtabs_and_lines decode_compound (struct linespec_state *self, - char **argptr, - char *saved_arg, - char *p); +/* List of keywords */ -static VEC (symbolp) *lookup_prefix_sym (char **argptr, char *p, - VEC (symtab_p) *, - char **); +static const char * const linespec_keywords[] = { "if", "thread", "task" }; -static struct symtabs_and_lines find_method (struct linespec_state *self, - char *saved_arg, - char *copy, - const char *class_name, - VEC (symbolp) *sym_classes); +/* A token of the linespec lexer */ -static void cplusplus_error (const char *name, const char *fmt, ...) - ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3); +struct ls_token +{ + /* The type of the token */ + linespec_token_type type; -static char *find_toplevel_char (char *s, char c); + /* Data for the token */ + union + { + /* A string, given as a stoken */ + struct stoken string; + + /* A keyword */ + const char *keyword; + } data; +}; +typedef struct ls_token linespec_token; -static int is_objc_method_format (const char *s); +#define LS_TOKEN_STOKEN(TOK) (TOK).data.string +#define LS_TOKEN_KEYWORD(TOK) (TOK).data.keyword -static VEC (symtab_p) *symtabs_from_filename (char **argptr, - char *p, int is_quote_enclosed, - char **user_filename); +/* An instance of the linespec parser. */ -static VEC (symbolp) *find_function_symbols (char **argptr, char *p, - int is_quote_enclosed, - char **user_function); +struct ls_parser +{ + /* Lexer internal data */ + struct + { + /* Save head of input stream. */ + char *saved_arg; -static struct symtabs_and_lines decode_all_digits (struct linespec_state *self, - char **argptr, - char *q); + /* Head of the input stream. */ + char **stream; +#define PARSER_STREAM(P) (*(P)->lexer.stream) -static struct symtabs_and_lines decode_dollar (struct linespec_state *self, - char *copy); + /* The current token. */ + linespec_token current; + } lexer; -static int decode_label (struct linespec_state *self, - VEC (symbolp) *function_symbols, - char *copy, - struct symtabs_and_lines *result); + /* Is the entire linespec quote-enclosed? */ + int is_quote_enclosed; -static struct symtabs_and_lines decode_variable (struct linespec_state *self, - char *copy); + /* The state of the parse. */ + struct linespec_state state; +#define PARSER_STATE(PPTR) (&(PPTR)->state) + + /* The result of the parse. */ + struct linespec result; +#define PARSER_RESULT(PPTR) (&(PPTR)->result) +}; +typedef struct ls_parser linespec_parser; + +/* Prototypes for local functions. */ + +static void initialize_defaults (struct symtab **default_symtab, + int *default_line); + +static CORE_ADDR linespec_expression_to_pc (char **exp_ptr); + +static struct symtabs_and_lines decode_objc (struct linespec_state *self, + linespec_p ls, + char **argptr); + +static VEC (symtab_p) *symtabs_from_filename (const char *); + +static VEC (symbolp) *find_label_symbols (struct linespec_state *self, + VEC (symbolp) *function_symbols, + VEC (symbolp) **label_funcs_ret, + const char *name); + +void find_linespec_symbols (struct linespec_state *self, + VEC (symtab_p) *file_symtabs, + const char *name, + VEC (symbolp) **symbols, + VEC (minsym_and_objfile_d) **minsyms); + +static struct line_offset + linespec_parse_variable (struct linespec_state *self, + const char *variable); static int symbol_to_sal (struct symtab_and_line *result, int funfirstline, struct symbol *sym); @@ -190,6 +334,466 @@ static void add_all_symbol_names_from_pspace (struct collect_info *info, struct program_space *pspace, VEC (const_char_ptr) *names); +static VEC (symtab_p) *collect_symtabs_from_filename (const char *file); + +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 void decode_digits_list_mode (struct linespec_state *self, + linespec_p ls, + struct symtabs_and_lines *values, + 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); + +static int compare_symbols (const void *a, const void *b); + +static int compare_msymbols (const void *a, const void *b); + +static const char *find_toplevel_char (const char *s, char c); + +/* Permitted quote characters for the parser. This is different from the + completer's quote characters to allow backward compatibility with the + previous parser. */ +static const char *const linespec_quote_characters = "\"\'"; + +/* Lexer functions. */ + +/* Lex a number from the input in PARSER. This only supports + decimal numbers. */ + +static linespec_token +linespec_lexer_lex_number (linespec_parser *parser) +{ + linespec_token token; + + token.type = LSTOKEN_NUMBER; + LS_TOKEN_STOKEN (token).length = 0; + LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser); + + /* Keep any sign at the start of the stream. */ + if (*PARSER_STREAM (parser) == '+' || *PARSER_STREAM (parser) == '-') + { + ++LS_TOKEN_STOKEN (token).length; + ++(PARSER_STREAM (parser)); + } + + while (isdigit (*PARSER_STREAM (parser))) + { + ++LS_TOKEN_STOKEN (token).length; + ++(PARSER_STREAM (parser)); + } + + return token; +} + +/* Does P represent one of the keywords? If so, return + the keyword. If not, return NULL. */ + +static const char * +linespec_lexer_lex_keyword (const char *p) +{ + int i; + + if (p != NULL) + { + for (i = 0; i < ARRAY_SIZE (linespec_keywords); ++i) + { + int len = strlen (linespec_keywords[i]); + + /* If P begins with one of the keywords and the next + character is not a valid identifier character, + we have found a keyword. */ + if (strncmp (p, linespec_keywords[i], len) == 0 + && !(isalnum (p[len]) || p[len] == '_')) + return linespec_keywords[i]; + } + } + + return NULL; +} + +/* Does STRING represent an Ada operator? If so, return the length + of the decoded operator name. If not, return 0. */ + +static int +is_ada_operator (const char *string) +{ + const struct ada_opname_map *mapping; + + for (mapping = ada_opname_table; + mapping->encoded != NULL + && strncmp (mapping->decoded, string, + strlen (mapping->decoded)) != 0; ++mapping) + ; + + return mapping->decoded == NULL ? 0 : strlen (mapping->decoded); +} + +/* Find QUOTE_CHAR in STRING, accounting for the ':' terminal. Return + the location of QUOTE_CHAR, or NULL if not found. */ + +static const char * +skip_quote_char (const char *string, char quote_char) +{ + const char *p, *last; + + p = last = find_toplevel_char (string, quote_char); + while (p && *p != '\0' && *p != ':') + { + p = find_toplevel_char (p, quote_char); + if (p != NULL) + last = p++; + } + + return last; +} + +/* Make a writable copy of the string given in TOKEN, trimming + any trailing whitespace. */ + +static char * +copy_token_string (linespec_token token) +{ + char *str, *s; + + if (token.type == LSTOKEN_KEYWORD) + return xstrdup (LS_TOKEN_KEYWORD (token)); + + str = savestring (LS_TOKEN_STOKEN (token).ptr, + LS_TOKEN_STOKEN (token).length); + s = remove_trailing_whitespace (str, str + LS_TOKEN_STOKEN (token).length); + *s = '\0'; + + return str; +} + +/* Does P represent the end of a quote-enclosed linespec? */ + +static int +is_closing_quote_enclosed (const char *p) +{ + if (strchr (linespec_quote_characters, *p)) + ++p; + p = skip_spaces ((char *) p); + return (*p == '\0' || linespec_lexer_lex_keyword (p)); +} + +/* Find the end of the parameter list that starts with *INPUT. + This helper function assists with lexing string segments + which might contain valid (non-terminating) commas. */ + +static char * +find_parameter_list_end (char *input) +{ + char end_char, start_char; + int depth; + char *p; + + start_char = *input; + if (start_char == '(') + end_char = ')'; + else if (start_char == '<') + end_char = '>'; + else + return NULL; + + p = input; + depth = 0; + while (*p) + { + if (*p == start_char) + ++depth; + else if (*p == end_char) + { + if (--depth == 0) + { + ++p; + break; + } + } + ++p; + } + + return p; +} + + +/* Lex a string from the input in PARSER. */ + +static linespec_token +linespec_lexer_lex_string (linespec_parser *parser) +{ + linespec_token token; + char *start = PARSER_STREAM (parser); + + token.type = LSTOKEN_STRING; + + /* If the input stream starts with a quote character, skip to the next + quote character, regardless of the content. */ + if (strchr (linespec_quote_characters, *PARSER_STREAM (parser))) + { + const char *end; + char quote_char = *PARSER_STREAM (parser); + + /* Special case: Ada operators. */ + if (PARSER_STATE (parser)->language->la_language == language_ada + && quote_char == '\"') + { + int len = is_ada_operator (PARSER_STREAM (parser)); + + if (len != 0) + { + /* The input is an Ada operator. Return the quoted string + as-is. */ + LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser); + LS_TOKEN_STOKEN (token).length = len; + PARSER_STREAM (parser) += len; + return token; + } + + /* The input does not represent an Ada operator -- fall through + to normal quoted string handling. */ + } + + /* Skip past the beginning quote. */ + ++(PARSER_STREAM (parser)); + + /* Mark the start of the string. */ + LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (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")); + + /* 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 + { + char *p; + + /* Otherwise, only identifier characters are permitted. + Spaces are the exception. In general, we keep spaces, + but only if the next characters in the input do not resolve + to one of the keywords. + + This allows users to forgo quoting CV-qualifiers, template arguments, + and similar common language constructs. */ + + while (1) + { + if (isspace (*PARSER_STREAM (parser))) + { + p = skip_spaces (PARSER_STREAM (parser)); + if (linespec_lexer_lex_keyword (p) != NULL) + { + LS_TOKEN_STOKEN (token).ptr = start; + LS_TOKEN_STOKEN (token).length + = PARSER_STREAM (parser) - start; + return token; + } + + /* Advance past the whitespace. */ + PARSER_STREAM (parser) = p; + } + + /* If the next character is EOI or (single) ':', the + string is complete; return the token. */ + if (*PARSER_STREAM (parser) == 0) + { + LS_TOKEN_STOKEN (token).ptr = start; + LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start; + return token; + } + else if (PARSER_STREAM (parser)[0] == ':') + { + /* Do not tokenize the C++ scope operator. */ + if (PARSER_STREAM (parser)[1] == ':') + ++(PARSER_STREAM (parser)); + + /* Do not tokenify if the input length so far is one + (i.e, a single-letter drive name) and the next character + is a directory separator. This allows Windows-style + paths to be recognized as filenames without quoting it. */ + else if ((PARSER_STREAM (parser) - start) != 1 + || !IS_DIR_SEPARATOR (PARSER_STREAM (parser)[1])) + { + LS_TOKEN_STOKEN (token).ptr = start; + LS_TOKEN_STOKEN (token).length + = PARSER_STREAM (parser) - start; + return token; + } + } + /* Special case: permit quote-enclosed linespecs. */ + else if (parser->is_quote_enclosed + && strchr (linespec_quote_characters, + *PARSER_STREAM (parser)) + && is_closing_quote_enclosed (PARSER_STREAM (parser))) + { + LS_TOKEN_STOKEN (token).ptr = start; + LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start; + return token; + } + /* Because commas may terminate a linespec and appear in + the middle of valid string input, special cases for + '<' and '(' are necessary. */ + else if (*PARSER_STREAM (parser) == '<' + || *PARSER_STREAM (parser) == '(') + { + char *p; + + p = find_parameter_list_end (PARSER_STREAM (parser)); + if (p != NULL) + { + PARSER_STREAM (parser) = p; + continue; + } + } + /* Commas are terminators, but not if they are part of an + operator name. */ + else if (*PARSER_STREAM (parser) == ',') + { + if ((PARSER_STATE (parser)->language->la_language + == language_cplus) + && (PARSER_STREAM (parser) - start) > 8 + /* strlen ("operator") */) + { + char *p = strstr (start, "operator"); + + if (p != NULL && is_operator_name (p)) + { + /* This is an operator name. Keep going. */ + ++(PARSER_STREAM (parser)); + continue; + } + } + + /* Comma terminates the string. */ + LS_TOKEN_STOKEN (token).ptr = start; + LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start; + return token; + } + + /* Advance the stream. */ + ++(PARSER_STREAM (parser)); + } + } + + return token; +} + +/* Lex a single linespec token from PARSER. */ + +static linespec_token +linespec_lexer_lex_one (linespec_parser *parser) +{ + const char *keyword; + + if (parser->lexer.current.type == LSTOKEN_CONSUMED) + { + /* Skip any whitespace. */ + PARSER_STREAM (parser) = skip_spaces (PARSER_STREAM (parser)); + + /* Check for a keyword. */ + keyword = linespec_lexer_lex_keyword (PARSER_STREAM (parser)); + if (keyword != NULL) + { + parser->lexer.current.type = LSTOKEN_KEYWORD; + LS_TOKEN_KEYWORD (parser->lexer.current) = keyword; + return parser->lexer.current; + } + + /* Handle other tokens. */ + switch (*PARSER_STREAM (parser)) + { + case 0: + parser->lexer.current.type = LSTOKEN_EOI; + break; + + case '+': case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + parser->lexer.current = linespec_lexer_lex_number (parser); + break; + + case ':': + /* If we have a scope operator, lex the input as a string. + Otherwise, return LSTOKEN_COLON. */ + if (PARSER_STREAM (parser)[1] == ':') + parser->lexer.current = linespec_lexer_lex_string (parser); + else + { + parser->lexer.current.type = LSTOKEN_COLON; + ++(PARSER_STREAM (parser)); + } + break; + + case '\'': case '\"': + /* Special case: permit quote-enclosed linespecs. */ + if (parser->is_quote_enclosed + && is_closing_quote_enclosed (PARSER_STREAM (parser))) + { + ++(PARSER_STREAM (parser)); + parser->lexer.current.type = LSTOKEN_EOI; + } + else + parser->lexer.current = linespec_lexer_lex_string (parser); + break; + + case ',': + parser->lexer.current.type = LSTOKEN_COMMA; + LS_TOKEN_STOKEN (parser->lexer.current).ptr + = PARSER_STREAM (parser); + LS_TOKEN_STOKEN (parser->lexer.current).length = 1; + ++(PARSER_STREAM (parser)); + break; + + default: + /* If the input is not a number, it must be a string. + [Keywords were already considered above.] */ + parser->lexer.current = linespec_lexer_lex_string (parser); + break; + } + } + + return parser->lexer.current; +} + +/* Consume the current token and return the next token in PARSER's + input stream. */ + +static linespec_token +linespec_lexer_consume_token (linespec_parser *parser) +{ + parser->lexer.current.type = LSTOKEN_CONSUMED; + return linespec_lexer_lex_one (parser); +} + +/* Return the next token without consuming the current token. */ + +static linespec_token +linespec_lexer_peek_token (linespec_parser *parser) +{ + linespec_token next; + char *saved_stream = PARSER_STREAM (parser); + linespec_token saved_token = parser->lexer.current; + + next = linespec_lexer_consume_token (parser); + PARSER_STREAM (parser) = saved_stream; + parser->lexer.current = saved_token; + return next; +} + /* Helper functions. */ /* Add SAL to SALS. */ @@ -229,7 +833,7 @@ add_sal_to_sals (struct linespec_state *self, input. We only apply the ":LINE" treatment to Ada for the time being. */ if (symname != NULL && sal->line != 0 - && current_language->la_language == language_ada) + && self->language->la_language == language_ada) canonical_name = xstrprintf ("%s:%s:%d", filename, symname, sal->line); else if (symname != NULL) @@ -288,39 +892,6 @@ maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr) return 1; } -/* Issue a helpful hint on using the command completion feature on - single quoted demangled C++ symbols as part of the completion - error. */ - -static void -cplusplus_error (const char *name, const char *fmt, ...) -{ - struct ui_file *tmp_stream; - char *message; - - tmp_stream = mem_fileopen (); - make_cleanup_ui_file_delete (tmp_stream); - - { - va_list args; - - va_start (args, fmt); - vfprintf_unfiltered (tmp_stream, fmt, args); - va_end (args); - } - - while (*name == '\'') - name++; - fprintf_unfiltered (tmp_stream, - ("Hint: try '%s<TAB> or '%s<ESC-?>\n" - "(Note leading single quote.)"), - name, name); - - message = ui_file_xstrdup (tmp_stream, NULL); - make_cleanup (xfree, message); - throw_error (NOT_FOUND_ERROR, "%s", message); -} - /* A callback function and the additional data to call it with. */ struct symbol_and_data_callback @@ -379,7 +950,8 @@ iterate_name_matcher (const char *name, void *d) inlined instances of functions will be included in the result. */ static void -iterate_over_all_matching_symtabs (const char *name, +iterate_over_all_matching_symtabs (struct linespec_state *state, + const char *name, const domain_enum domain, symbol_found_callback_ftype *callback, void *data, @@ -392,8 +964,8 @@ iterate_over_all_matching_symtabs (const char *name, matcher_data.lookup_name = name; matcher_data.symbol_name_cmp = - current_language->la_get_symbol_name_cmp != NULL - ? current_language->la_get_symbol_name_cmp (name) + state->language->la_get_symbol_name_cmp != NULL + ? state->language->la_get_symbol_name_cmp (name) : strcmp_iw; ALL_PSPACES (pspace) @@ -468,7 +1040,7 @@ get_search_block (struct symtab *symtab) } /* A helper for find_method. This finds all methods in type T which - match NAME. It adds resulting symbol names to RESULT_NAMES, and + match NAME. It adds matching symbol names to RESULT_NAMES, and adds T's direct superclasses to SUPERCLASSES. */ static void @@ -542,14 +1114,14 @@ find_methods (struct type *t, const char *name, strings. Also, ignore the char within a template name, like a ',' within foo<int, int>. */ -static char * -find_toplevel_char (char *s, char c) +static const char * +find_toplevel_char (const char *s, char c) { int quoted = 0; /* zero if we're not in quotes; '"' if we're in a double-quoted string; '\'' if we're in a single-quoted string. */ int depth = 0; /* Number of unclosed parens we've seen. */ - char *scan; + const char *scan; for (scan = s; *scan; scan++) { @@ -573,23 +1145,34 @@ find_toplevel_char (char *s, char c) return 0; } -/* Determines if the gives string corresponds to an Objective-C method - representation, such as -[Foo bar:] or +[Foo bar]. Objective-C symbols - are allowed to have spaces and parentheses in them. */ +/* The string equivalent of find_toplevel_char. Returns a pointer + to the location of NEEDLE in HAYSTACK, ignoring any occurrences + inside "()" and "<>". Returns NULL if NEEDLE was not found. */ -static int -is_objc_method_format (const char *s) +static const char * +find_toplevel_string (const char *haystack, const char *needle) { - if (s == NULL || *s == '\0') - return 0; - /* Handle arguments with the format FILENAME:SYMBOL. */ - if ((s[0] == ':') && (strchr ("+-", s[1]) != NULL) - && (s[2] == '[') && strchr(s, ']')) - return 1; - /* Handle arguments that are just SYMBOL. */ - else if ((strchr ("+-", s[0]) != NULL) && (s[1] == '[') && strchr(s, ']')) - return 1; - return 0; + const char *s = haystack; + + do + { + s = find_toplevel_char (s, *needle); + + if (s != NULL) + { + /* Found first char in HAYSTACK; check rest of string. */ + if (strncmp (s, needle, strlen (needle)) == 0) + return s; + + /* Didn't find it; loop over HAYSTACK, looking for the next + instance of the first character of NEEDLE. */ + ++s; + } + } + while (s != NULL && *s != '\0'); + + /* NEEDLE was not found in HAYSTACK. */ + return NULL; } /* Given FILTERS, a list of canonical names, filter the sals in RESULT @@ -758,475 +1341,846 @@ decode_line_2 (struct linespec_state *self, do_cleanups (old_chain); } -/* Valid delimiters for linespec keywords "if", "thread" or "task". */ + -static int -is_linespec_boundary (char c) +/* The parser of linespec itself. */ + +/* Throw an appropriate error when SYMBOL is not found (optionally in + FILENAME). */ + +static void ATTRIBUTE_NORETURN +symbol_not_found_error (char *symbol, char *filename) { - return c == ' ' || c == '\t' || c == '\0' || c == ','; + if (symbol == NULL) + symbol = ""; + + if (!have_full_symbols () + && !have_partial_symbols () + && !have_minimal_symbols ()) + throw_error (NOT_FOUND_ERROR, + _("No symbol table is loaded. Use the \"file\" command.")); + + /* If SYMBOL starts with '$', the user attempted to either lookup + a function/variable in his code starting with '$' or an internal + variable of that name. Since we do not know which, be concise and + explain both possibilities. */ + if (*symbol == '$') + { + if (filename) + throw_error (NOT_FOUND_ERROR, + _("Undefined convenience variable or function \"%s\" " + "not defined in \"%s\"."), symbol, filename); + else + throw_error (NOT_FOUND_ERROR, + _("Undefined convenience variable or function \"%s\" " + "not defined."), symbol); + } + else + { + if (filename) + throw_error (NOT_FOUND_ERROR, + _("Function \"%s\" not defined in \"%s\"."), + symbol, filename); + else + throw_error (NOT_FOUND_ERROR, + _("Function \"%s\" not defined."), symbol); + } } -/* A helper function for decode_line_1 and friends which skips P - past any method overload information at the beginning of P, e.g., - "(const struct foo *)". +/* Throw an appropriate error when an unexpected token is encountered + in the input. */ - This function assumes that P has already been validated to contain - overload information, and it will assert if *P != '('. */ -static char * -find_method_overload_end (char *p) +static void ATTRIBUTE_NORETURN +unexpected_linespec_error (linespec_parser *parser) { - int depth = 0; + linespec_token token; + static const char * token_type_strings[] + = {"keyword", "colon", "string", "number", "comma", "end of input"}; - gdb_assert (*p == '('); + /* Get the token that generated the error. */ + token = linespec_lexer_lex_one (parser); - while (*p) + /* Finally, throw the error. */ + if (token.type == LSTOKEN_STRING || token.type == LSTOKEN_NUMBER + || token.type == LSTOKEN_KEYWORD) { - if (*p == '(') - ++depth; - else if (*p == ')') - { - if (--depth == 0) - { - ++p; - break; - } - } - ++p; + char *string; + struct cleanup *cleanup; + + string = copy_token_string (token); + cleanup = make_cleanup (xfree, string); + throw_error (GENERIC_ERROR, + _("malformed linespec error: unexpected %s, \"%s\""), + token_type_strings[token.type], string); } + else + throw_error (GENERIC_ERROR, + _("malformed linespec error: unexpected %s"), + token_type_strings[token.type]); +} - return p; +/* Parse and return a line offset in STRING. */ + +static struct line_offset +linespec_parse_line_offset (char *string) +{ + struct line_offset line_offset = {0, LINE_OFFSET_NONE}; + + if (*string == '+') + { + line_offset.sign = LINE_OFFSET_PLUS; + ++string; + } + else if (*string == '-') + { + line_offset.sign = LINE_OFFSET_MINUS; + ++string; + } + + /* Right now, we only allow base 10 for offsets. */ + line_offset.offset = atoi (string); + return line_offset; } -/* Keep important information used when looking up a name. This includes - template parameters, overload information, and important keywords, including - the possible Java trailing type. */ +/* Parse the basic_spec in PARSER's input. */ -static char * -keep_name_info (char *p, int on_boundary) +static void +linespec_parse_basic (linespec_parser *parser) { - const char *quotes = get_gdb_completer_quote_characters (); - char *saved_p = p; - int nest = 0; + char *name; + linespec_token token; + VEC (symbolp) *symbols, *labels; + VEC (minsym_and_objfile_d) *minimal_symbols; + struct cleanup *cleanup; - while (*p) + /* Get the next token. */ + 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 it is a LSTOKEN_NUMBER, we have an offset. */ + else if (token.type == LSTOKEN_NUMBER) { - if (strchr (quotes, *p)) - break; + /* Record the line offset and get the next token. */ + name = copy_token_string (token); + cleanup = make_cleanup (xfree, name); + PARSER_RESULT (parser)->line_offset = linespec_parse_line_offset (name); + do_cleanups (cleanup); - if (*p == ',' && !nest) - break; + /* Get the next token. */ + token = linespec_lexer_consume_token (parser); + + /* If the next token is a comma, stop parsing and return. */ + if (token.type == LSTOKEN_COMMA) + return; + + /* If the next token is anything but EOI or KEYWORD, issue + an error. */ + if (token.type != LSTOKEN_KEYWORD && token.type != LSTOKEN_EOI) + unexpected_linespec_error (parser); + } + + if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI) + return; - if (on_boundary && !nest) + /* Next token must be LSTOKEN_STRING. */ + if (token.type != LSTOKEN_STRING) + 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); + + /* Try looking it up as a function/method. */ + find_linespec_symbols (PARSER_STATE (parser), + PARSER_RESULT (parser)->file_symtabs, name, + &symbols, &minimal_symbols); + + if (symbols != NULL || minimal_symbols != NULL) + { + PARSER_RESULT (parser)->function_symbols = symbols; + PARSER_RESULT (parser)->minimal_symbols = minimal_symbols; + PARSER_RESULT (parser)->function_name = name; + symbols = NULL; + discard_cleanups (cleanup); + } + else + { + /* NAME was not a function or a method. So it must be a label + name. */ + labels = find_label_symbols (PARSER_STATE (parser), NULL, + &symbols, name); + if (labels != NULL) + { + PARSER_RESULT (parser)->labels.label_symbols = labels; + PARSER_RESULT (parser)->labels.function_symbols = symbols; + PARSER_RESULT (parser)->label_name = name; + symbols = NULL; + discard_cleanups (cleanup); + } + else { - const char *const words[] = { "if", "thread", "task" }; - int wordi; + /* The name is also not a label. Abort parsing. Do not throw + an error here. parse_linespec will do it for us. */ - for (wordi = 0; wordi < ARRAY_SIZE (words); wordi++) - if (strncmp (p, words[wordi], strlen (words[wordi])) == 0 - && is_linespec_boundary (p[strlen (words[wordi])])) - break; - if (wordi < ARRAY_SIZE (words)) - break; + /* Save a copy of the name we were trying to lookup. */ + PARSER_RESULT (parser)->function_name = name; + discard_cleanups (cleanup); + return; } + } - if (*p == '(' || *p == '<' || *p == '[') - nest++; - else if ((*p == ')' || *p == '>' || *p == ']') && nest > 0) - nest--; + /* Get the next token. */ + token = linespec_lexer_consume_token (parser); - p++; + if (token.type == LSTOKEN_COLON) + { + /* User specified a label or a lineno. */ + token = linespec_lexer_consume_token (parser); - /* The ',' check could fail on "operator ,". */ - p += cp_validate_operator (p); + if (token.type == LSTOKEN_NUMBER) + { + /* User specified an offset. Record the line offset and + get the next token. */ + name = copy_token_string (token); + cleanup = make_cleanup (xfree, name); + PARSER_RESULT (parser)->line_offset + = linespec_parse_line_offset (name); + do_cleanups (cleanup); - on_boundary = is_linespec_boundary (p[-1]); - } + /* Ge the next token. */ + token = linespec_lexer_consume_token (parser); + } + else if (token.type == LSTOKEN_STRING) + { + /* Grab a copy of the label's name and look it up. */ + name = copy_token_string (token); + cleanup = make_cleanup (xfree, name); + labels = find_label_symbols (PARSER_STATE (parser), + PARSER_RESULT (parser)->function_symbols, + &symbols, name); + + if (labels != NULL) + { + PARSER_RESULT (parser)->labels.label_symbols = labels; + PARSER_RESULT (parser)->labels.function_symbols = symbols; + PARSER_RESULT (parser)->label_name = name; + symbols = NULL; + discard_cleanups (cleanup); + } + else + { + /* We don't know what it was, but it isn't a label. */ + throw_error (NOT_FOUND_ERROR, + _("No label \"%s\" defined in function \"%s\"."), + name, PARSER_RESULT (parser)->function_name); + } - while (p > saved_p && is_linespec_boundary (p[-1])) - p--; + /* Check for a line offset. */ + token = linespec_lexer_consume_token (parser); + if (token.type == LSTOKEN_COLON) + { + /* Get the next token. */ + token = linespec_lexer_consume_token (parser); - return p; + /* It must be a line offset. */ + if (token.type != LSTOKEN_NUMBER) + unexpected_linespec_error (parser); + + /* Record the lione offset and get the next token. */ + name = copy_token_string (token); + cleanup = make_cleanup (xfree, name); + + PARSER_RESULT (parser)->line_offset + = linespec_parse_line_offset (name); + do_cleanups (cleanup); + + /* Get the next token. */ + token = linespec_lexer_consume_token (parser); + } + } + else + { + /* Trailing ':' in the input. Issue an error. */ + unexpected_linespec_error (parser); + } + } } - -/* The parser of linespec itself. */ +/* Canonicalize the linespec contained in LS. The result is saved into + STATE->canonical. */ -/* Parse a string that specifies a line number. - Pass the address of a char * variable; that variable will be - advanced over the characters actually parsed. +static void +canonicalize_linespec (struct linespec_state *state, linespec_p ls) +{ + /* If canonicalization was not requested, no need to do anything. */ + if (!state->canonical) + return; - The string can be: + /* Shortcut expressions, which can only appear by themselves. */ + if (ls->expression != NULL) + state->canonical->addr_string = xstrdup (ls->expression); + else + { + struct ui_file *buf; + int need_colon = 0; - LINENUM -- that line number in current file. PC returned is 0. - FILE:LINENUM -- that line in that file. PC returned is 0. - FUNCTION -- line number of openbrace of that function. - PC returned is the start of the function. - LABEL -- a label in the current scope - VARIABLE -- line number of definition of that variable. - PC returned is 0. - FILE:FUNCTION -- likewise, but prefer functions in that file. - *EXPR -- line in which address EXPR appears. + buf = mem_fileopen (); + if (ls->source_filename) + { + fputs_unfiltered (ls->source_filename, buf); + need_colon = 1; + } - This may all be followed by an "if EXPR", which we ignore. + if (ls->function_name) + { + if (need_colon) + fputc_unfiltered (':', buf); + fputs_unfiltered (ls->function_name, buf); + need_colon = 1; + } - FUNCTION may be an undebuggable function found in minimal symbol table. + if (ls->label_name) + { + if (need_colon) + fputc_unfiltered (':', buf); - If the argument FUNFIRSTLINE is nonzero, we want the first line - of real code inside a function when a function is specified, and it is - not OK to specify a variable or type to get its line number. + if (ls->function_name == NULL) + { + struct symbol *s; + + /* No function was specified, so add the symbol name. */ + gdb_assert (ls->labels.function_symbols != NULL + && (VEC_length (symbolp, ls->labels.function_symbols) + == 1)); + s = VEC_index (symbolp, ls->labels.function_symbols, 0); + fputs_unfiltered (SYMBOL_NATURAL_NAME (s), buf); + fputc_unfiltered (':', buf); + } - DEFAULT_SYMTAB specifies the file to use if none is specified. - It defaults to current_source_symtab. - DEFAULT_LINE specifies the line number to use for relative - line numbers (that start with signs). Defaults to current_source_line. - If CANONICAL is non-NULL, store an array of strings containing the canonical - line specs there if necessary. Currently overloaded member functions and - line numbers or static functions without a filename yield a canonical - line spec. The array and the line spec strings are allocated on the heap, - it is the callers responsibility to free them. + fputs_unfiltered (ls->label_name, buf); + need_colon = 1; + state->canonical->special_display = 1; + } - Note that it is possible to return zero for the symtab - if no file is validly specified. Callers must check that. - Also, the line number returned may be invalid. */ + if (ls->line_offset.sign != LINE_OFFSET_UNKNOWN) + { + if (need_colon) + fputc_unfiltered (':', buf); + fprintf_filtered (buf, "%s%d", + (ls->line_offset.sign == LINE_OFFSET_NONE ? "" + : (ls->line_offset.sign + == LINE_OFFSET_PLUS ? "+" : "-")), + ls->line_offset.offset); + } -/* We allow single quotes in various places. This is a hideous - kludge, which exists because the completer can't yet deal with the - lack of single quotes. FIXME: write a linespec_completer which we - can use as appropriate instead of make_symbol_completion_list. */ + state->canonical->addr_string = ui_file_xstrdup (buf, NULL); + ui_file_delete (buf); + } +} + +/* Given a line offset in LS, construct the relevant SALs. */ static struct symtabs_and_lines -decode_line_internal (struct linespec_state *self, char **argptr) +create_sals_line_offset (struct linespec_state *self, + linespec_p ls) { - char *p; - char *q; + struct symtabs_and_lines values; + struct symtab_and_line val; + int use_default = 0; - char *copy; - /* This says whether or not something in *ARGPTR is quoted with - completer_quotes (i.e. with single quotes). */ - int is_quoted; - /* Is *ARGPTR enclosed in double quotes? */ - int is_quote_enclosed; - int is_objc_method = 0; - char *saved_arg = *argptr; - /* If IS_QUOTED, the end of the quoted bit. */ - char *end_quote = NULL; - /* Is *ARGPTR enclosed in single quotes? */ - int is_squote_enclosed = 0; - /* The "first half" of the linespec. */ - char *first_half; - - /* If we are parsing `function:label', this holds the symbols - matching the function name. */ - VEC (symbolp) *function_symbols = NULL; - /* If FUNCTION_SYMBOLS is not NULL, then this is the exception that - was thrown when trying to parse a filename. */ - volatile struct gdb_exception file_exception; + init_sal (&val); + values.sals = NULL; + values.nelts = 0; - struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); + /* 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 anme, since + set_default_source_symtab_and_line uses + select_source_symtab that calls us with such an argument. */ - /* Defaults have defaults. */ + if (VEC_length (symtab_p, ls->file_symtabs) == 1 + && VEC_index (symtab_p, ls->file_symtabs, 0) == NULL) + { + set_current_program_space (self->program_space); - initialize_defaults (&self->default_symtab, &self->default_line); - - /* See if arg is *PC. */ + /* Make sure we have at least a default source line. */ + set_default_source_symtab_and_line (); + initialize_defaults (&self->default_symtab, &self->default_line); + VEC_pop (symtab_p, ls->file_symtabs); + VEC_free (symtab_p, ls->file_symtabs); + ls->file_symtabs + = collect_symtabs_from_filename (self->default_symtab->filename); + use_default = 1; + } - if (**argptr == '*') + val.line = ls->line_offset.offset; + switch (ls->line_offset.sign) { - do_cleanups (cleanup); - return decode_indirect (self, argptr); - } + case LINE_OFFSET_PLUS: + if (ls->line_offset.offset == 0) + val.line = 5; + if (use_default) + val.line = self->default_line + val.line; + break; - is_quoted = (strchr (get_gdb_completer_quote_characters (), - **argptr) != NULL); + case LINE_OFFSET_MINUS: + if (ls->line_offset.offset == 0) + val.line = 15; + if (use_default) + val.line = self->default_line - val.line; + else + val.line = -val.line; + break; - if (is_quoted) + case LINE_OFFSET_NONE: + break; /* No need to adjust val.line. */ + } + + if (self->list_mode) + decode_digits_list_mode (self, ls, &values, val); + else { - end_quote = skip_quoted (*argptr); - if (*end_quote == '\0') - is_squote_enclosed = 1; + struct linetable_entry *best_entry = NULL; + int *filter; + 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); + + /* For optimized code, the compiler can scatter one source line + across disjoint ranges of PC values, even when no duplicate + functions or inline functions are involved. For example, + 'for (;;)' inside a non-template, non-inline, and non-ctor-or-dtor + function can result in two PC ranges. In this case, we don't + want to set a breakpoint on the first PC of each range. To filter + such cases, we use containing blocks -- for each PC found + 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 (struct block *, intermediate_results.nelts); + make_cleanup (xfree, blocks); + + for (i = 0; i < intermediate_results.nelts; ++i) + { + set_current_program_space (intermediate_results.sals[i].pspace); + + filter[i] = 1; + blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc, + intermediate_results.sals[i].section); + } + + for (i = 0; i < intermediate_results.nelts; ++i) + { + if (blocks[i] != NULL) + for (j = i + 1; j < intermediate_results.nelts; ++j) + { + if (blocks[j] == blocks[i]) + { + filter[j] = 0; + break; + } + } + } + + for (i = 0; i < intermediate_results.nelts; ++i) + if (filter[i]) + { + struct symbol *sym = (blocks[i] + ? block_containing_function (blocks[i]) + : NULL); + + if (self->funfirstline) + skip_prologue_sal (&intermediate_results.sals[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], + sym ? SYMBOL_NATURAL_NAME (sym) : NULL); + } + + do_cleanups (cleanup); } - /* Check to see if it's a multipart linespec (with colons or - periods). */ + if (values.nelts == 0) + { + if (ls->source_filename) + throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."), + val.line, ls->source_filename); + else + throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."), + val.line); + } - /* Locate the end of the first half of the linespec. - After the call, for instance, if the argptr string is "foo.c:123" - p will point at ":123". If there is only one part, like "foo", p - will point to "". If this is a C++ name, like "A::B::foo", p will - point to "::B::foo". Argptr is not changed by this call. */ + return values; +} - first_half = p = locate_first_half (argptr, &is_quote_enclosed); +/* Create and return SALs from the linespec LS. */ - /* First things first: if ARGPTR starts with a filename, get its - symtab and strip the filename from ARGPTR. - Avoid calling symtab_from_filename if we know can, - it can be expensive. We know we can avoid the call if we see a - single word (e.g., "break NAME") or if we see a qualified C++ - name ("break QUAL::NAME"). */ +static struct symtabs_and_lines +convert_linespec_to_sals (struct linespec_state *state, linespec_p ls) +{ + struct symtabs_and_lines sals = {NULL, 0}; - if (*p != '\0' && !(p[0] == ':' && p[1] == ':')) + if (ls->expression != NULL) { - TRY_CATCH (file_exception, RETURN_MASK_ERROR) + /* We have an expression. No other attribute is allowed. */ + sals.sals = XMALLOC (struct symtab_and_line); + sals.nelts = 1; + sals.sals[0] = find_pc_line (ls->expr_pc, 0); + sals.sals[0].pc = ls->expr_pc; + sals.sals[0].section = find_pc_overlay (ls->expr_pc); + sals.sals[0].explicit_pc = 1; + } + else if (ls->labels.label_symbols != NULL) + { + /* We have just a bunch of functions/methods or labels. */ + int i; + struct symtab_and_line sal; + struct symbol *sym; + + for (i = 0; VEC_iterate (symbolp, ls->labels.label_symbols, i, sym); ++i) { - self->file_symtabs = symtabs_from_filename (argptr, p, - is_quote_enclosed, - &self->user_filename); + symbol_to_sal (&sal, state->funfirstline, sym); + add_sal_to_sals (state, &sals, &sal, + SYMBOL_NATURAL_NAME (sym)); } + } + else if (ls->function_symbols != NULL || ls->minimal_symbols != NULL) + { + /* We have just a bunch of functions and/or methods. */ + int i; + struct symtab_and_line sal; + struct symbol *sym; + minsym_and_objfile_d *elem; + struct program_space *pspace; - if (file_exception.reason >= 0) + if (ls->function_symbols != NULL) { - /* Check for single quotes on the non-filename part. */ - is_quoted = (**argptr - && strchr (get_gdb_completer_quote_characters (), - **argptr) != NULL); - if (is_quoted) - end_quote = skip_quoted (*argptr); - - /* Locate the next "half" of the linespec. */ - first_half = p = locate_first_half (argptr, &is_quote_enclosed); + /* Sort symbols so that symbols with the same program space are next + to each other. */ + qsort (VEC_address (symbolp, ls->function_symbols), + VEC_length (symbolp, ls->function_symbols), + sizeof (symbolp), compare_symbols); + + for (i = 0; VEC_iterate (symbolp, ls->function_symbols, i, sym); ++i) + { + pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)); + set_current_program_space (pspace); + symbol_to_sal (&sal, state->funfirstline, sym); + if (maybe_add_address (state->addr_set, pspace, sal.pc)) + add_sal_to_sals (state, &sals, &sal, SYMBOL_NATURAL_NAME (sym)); + } } - if (VEC_empty (symtab_p, self->file_symtabs)) + if (ls->minimal_symbols != NULL) { - /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ - VEC_safe_push (symtab_p, self->file_symtabs, NULL); + /* Sort minimal symbols by program space, too. */ + qsort (VEC_address (minsym_and_objfile_d, ls->minimal_symbols), + VEC_length (minsym_and_objfile_d, ls->minimal_symbols), + sizeof (minsym_and_objfile_d), compare_msymbols); + + for (i = 0; + VEC_iterate (minsym_and_objfile_d, ls->minimal_symbols, i, elem); + ++i) + { + pspace = SYMBOL_OBJ_SECTION (elem->minsym)->objfile->pspace; + set_current_program_space (pspace); + minsym_found (state, elem->objfile, elem->minsym, &sals); + } } } + else if (ls->line_offset.sign != LINE_OFFSET_UNKNOWN) + { + /* Only an offset was specified. */ + sals = create_sals_line_offset (state, ls); + + /* Make sure we have a filename for canonicalization. */ + if (ls->source_filename == NULL) + ls->source_filename = xstrdup (state->default_symtab->filename); + } else { - /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ - VEC_safe_push (symtab_p, self->file_symtabs, NULL); + /* We haven't found any results... */ + return sals; } - /* Check if this is an Objective-C method (anything that starts with - a '+' or '-' and a '['). */ - if (is_objc_method_format (p)) - is_objc_method = 1; + canonicalize_linespec (state, ls); - /* Check if the symbol could be an Objective-C selector. */ + if (sals.nelts > 0 && state->canonical != NULL) + state->canonical->pre_expanded = 1; - { - struct symtabs_and_lines values; + return sals; +} - values = decode_objc (self, argptr); - if (values.sals != NULL) - { - do_cleanups (cleanup); - return values; - } - } +/* Parse a string that specifies a linespec. + Pass the address of a char * variable; that variable will be + advanced over the characters actually parsed. - /* Does it look like there actually were two parts? */ + The basic grammar of linespecs: - if (p[0] == ':' || p[0] == '.') - { - /* Is it a C++ or Java compound data structure? - The check on p[1] == ':' is capturing the case of "::", - since p[0]==':' was checked above. - Note that the call to decode_compound does everything - for us, including the lookup on the symbol table, so we - can return now. */ - - if (p[0] == '.' || p[1] == ':') - { - /* We only perform this check for the languages where it might - make sense. For instance, Ada does not use this type of - syntax, and trying to apply this logic on an Ada linespec - may trigger a spurious error (for instance, decode_compound - does not like expressions such as `ops."<"', which is a - valid function name in Ada). */ - if (current_language->la_language == language_c - || current_language->la_language == language_cplus - || current_language->la_language == language_java) - { - struct symtabs_and_lines values; - volatile struct gdb_exception ex; - char *saved_argptr = *argptr; + linespec -> expr_spec | var_spec | basic_spec + expr_spec -> '*' STRING + var_spec -> '$' (STRING | NUMBER) - if (is_quote_enclosed) - ++saved_arg; + basic_spec -> file_offset_spec | function_spec | label_spec + file_offset_spec -> opt_file_spec offset_spec + function_spec -> opt_file_spec function_name_spec opt_label_spec + label_spec -> label_name_spec - /* Initialize it just to avoid a GCC false warning. */ - memset (&values, 0, sizeof (values)); + opt_file_spec -> "" | file_name_spec ':' + opt_label_spec -> "" | ':' label_name_spec - TRY_CATCH (ex, RETURN_MASK_ERROR) - { - values = decode_compound (self, argptr, saved_arg, p); - } - if ((is_quoted || is_squote_enclosed) && **argptr == '\'') - *argptr = *argptr + 1; + file_name_spec -> STRING + function_name_spec -> STRING + label_name_spec -> STRING + function_name_spec -> STRING + offset_spec -> NUMBER + -> '+' NUMBER + -> '-' NUMBER - if (ex.reason >= 0) - { - do_cleanups (cleanup); - return values; - } + This may all be followed by several keywords such as "if EXPR", + which we ignore. - if (ex.error != NOT_FOUND_ERROR) - throw_exception (ex); + A comma will terminate parsing. - *argptr = saved_argptr; - } - } - else - { - /* If there was an exception looking up a specified filename earlier, - then check whether we were really given `function:label'. */ - if (file_exception.reason < 0) - { - function_symbols = find_function_symbols (argptr, p, - is_quote_enclosed, - &self->user_function); + The function may be an undebuggable function found in minimal symbol table. - /* If we did not find a function, re-throw the original - exception. */ - if (!function_symbols) - throw_exception (file_exception); + If the argument FUNFIRSTLINE is nonzero, we want the first line + of real code inside a function when a function is specified, and it is + not OK to specify a variable or type to get its line number. - make_cleanup (VEC_cleanup (symbolp), &function_symbols); - } + DEFAULT_SYMTAB specifies the file to use if none is specified. + It defaults to current_source_symtab. + DEFAULT_LINE specifies the line number to use for relative + line numbers (that start with signs). Defaults to current_source_line. + If CANONICAL is non-NULL, store an array of strings containing the canonical + line specs there if necessary. Currently overloaded member functions and + line numbers or static functions without a filename yield a canonical + line spec. The array and the line spec strings are allocated on the heap, + it is the callers responsibility to free them. - /* Check for single quotes on the non-filename part. */ - if (!is_quoted) - { - is_quoted = (**argptr - && strchr (get_gdb_completer_quote_characters (), - **argptr) != NULL); - if (is_quoted) - end_quote = skip_quoted (*argptr); - } - } - } + Note that it is possible to return zero for the symtab + if no file is validly specified. Callers must check that. + Also, the line number returned may be invalid. */ - /* self->file_symtabs holds the specified file symtabs, or 0 if no file - specified. - If we are parsing `function:symbol', then FUNCTION_SYMBOLS holds the - functions before the `:'. - arg no longer contains the file name. */ +/* Parse the linespec in ARGPTR. */ - /* If the filename was quoted, we must re-check the quotation. */ +static struct symtabs_and_lines +parse_linespec (linespec_parser *parser, char **argptr) +{ + linespec_token token; + struct symtabs_and_lines values; + volatile struct gdb_exception file_exception; + struct cleanup *cleanup; - if (end_quote == first_half && *end_quote!= '\0') + /* A special case to start. It has become quite popular for + 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 (*argptr) + && strchr (linespec_quote_characters, **argptr) != NULL) { - is_quoted = (**argptr - && strchr (get_gdb_completer_quote_characters (), - **argptr) != NULL); - if (is_quoted) - end_quote = skip_quoted (*argptr); + const char *end; + + end = skip_quote_char (*argptr + 1, **argptr); + if (end != NULL && is_closing_quote_enclosed (end)) + { + /* Here's the special case. Skip ARGPTR past the initial + quote. */ + ++(*argptr); + parser->is_quote_enclosed = 1; + } } - /* Check whether arg is all digits (and sign). */ + parser->lexer.saved_arg = *argptr; + parser->lexer.stream = argptr; + file_exception.reason = 0; - q = *argptr; - if (*q == '-' || *q == '+') - q++; - while (*q >= '0' && *q <= '9') - q++; + /* Initialize the default symtab and line offset. */ + initialize_defaults (&PARSER_STATE (parser)->default_symtab, + &PARSER_STATE (parser)->default_line); - if (q != *argptr && (*q == 0 || *q == ' ' || *q == '\t' || *q == ',') - && function_symbols == NULL) - { - struct symtabs_and_lines values; + /* Objective-C shortcut. */ + values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), argptr); + if (values.sals != NULL) + return values; - /* We found a token consisting of all digits -- at least one digit. */ - values = decode_all_digits (self, argptr, q); - do_cleanups (cleanup); - return values; - } + /* Start parsing. */ - /* Arg token is not digits => try it as a variable name - Find the next token (everything up to end or next whitespace). */ + /* Get the first token. */ + token = linespec_lexer_lex_one (parser); - if (**argptr == '$') /* May be a convenience variable. */ - /* One or two $ chars possible. */ - p = skip_quoted (*argptr + (((*argptr)[1] == '$') ? 2 : 1)); - else if (is_quoted || is_squote_enclosed) + /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER. */ + if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '*') { - p = end_quote; - if (p[-1] != '\'') - error (_("Unmatched single quote.")); - } - else if (is_objc_method) - { - /* allow word separators in method names for Obj-C. */ - p = skip_quoted_chars (*argptr, NULL, ""); + char *expr, *copy; + + /* User specified an expression, *EXPR. */ + copy = expr = copy_token_string (token); + cleanup = make_cleanup (xfree, expr); + PARSER_RESULT (parser)->expr_pc = linespec_expression_to_pc (©); + discard_cleanups (cleanup); + PARSER_RESULT (parser)->expression = expr; + + /* This is a little hacky/tricky. If linespec_expression_to_pc + did not evaluate the entire token, then we must find the + string COPY inside the original token buffer. */ + if (*copy != '\0') + { + PARSER_STREAM (parser) = strstr (parser->lexer.saved_arg, copy); + gdb_assert (PARSER_STREAM (parser) != NULL); + } + + /* Consume the token. */ + linespec_lexer_consume_token (parser); + + goto convert_to_sals; } - else + else if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$') { - p = skip_quoted (*argptr); - } + char *var; - /* Keep any important naming information. */ - p = keep_name_info (p, p == saved_arg || is_linespec_boundary (p[-1])); + /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ + VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL); - copy = (char *) alloca (p - *argptr + 1); - memcpy (copy, *argptr, p - *argptr); - copy[p - *argptr] = '\0'; - if (p != *argptr - && copy[0] - && copy[0] == copy[p - *argptr - 1] - && strchr (get_gdb_completer_quote_characters (), copy[0]) != NULL) - { - copy[p - *argptr - 1] = '\0'; - copy++; - } - else if (is_quoted || is_squote_enclosed) - copy[p - *argptr - 1] = '\0'; - - *argptr = skip_spaces (p); + /* User specified a convenience variable or history value. */ + var = copy_token_string (token); + cleanup = make_cleanup (xfree, var); + PARSER_RESULT (parser)->line_offset + = linespec_parse_variable (PARSER_STATE (parser), var); - /* If it starts with $: may be a legitimate variable or routine name - (e.g. HP-UX millicode routines such as $$dyncall), or it may - be history value, or it may be a convenience variable. */ + /* If a line_offset wasn't found (VAR is the name of a user + variable/function), then skip to normal symbol processing. */ + if (PARSER_RESULT (parser)->line_offset.sign != LINE_OFFSET_UNKNOWN) + { + discard_cleanups (cleanup); - if (*copy == '$' && function_symbols == NULL) - { - struct symtabs_and_lines values; + /* Consume this token. */ + linespec_lexer_consume_token (parser); + + goto convert_to_sals; + } - values = decode_dollar (self, copy); do_cleanups (cleanup); - return values; } + else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER) + unexpected_linespec_error (parser); - /* Try the token as a label, but only if no file was specified, - because we can only really find labels in the current scope. */ + /* Shortcut: If the next token is not LSTOKEN_COLON, we know that + this token cannot represent a filename. */ + token = linespec_lexer_peek_token (parser); - if (VEC_length (symtab_p, self->file_symtabs) == 1 - && VEC_index (symtab_p, self->file_symtabs, 0) == NULL) + if (token.type == LSTOKEN_COLON) { - struct symtabs_and_lines label_result; - if (decode_label (self, function_symbols, copy, &label_result)) + char *user_filename; + + /* Get the current token again and extract the filename. */ + token = linespec_lexer_lex_one (parser); + user_filename = copy_token_string (token); + + /* Check if the input is a filename. */ + TRY_CATCH (file_exception, RETURN_MASK_ERROR) { - do_cleanups (cleanup); - return label_result; + PARSER_RESULT (parser)->file_symtabs + = symtabs_from_filename (user_filename); + } + + if (file_exception.reason >= 0) + { + /* Symtabs were found for the file. Record the filename. */ + PARSER_RESULT (parser)->source_filename = user_filename; + + /* Get the next token. */ + token = linespec_lexer_consume_token (parser); + + /* This is LSTOKEN_COLON; consume it. */ + linespec_lexer_consume_token (parser); } + else + { + /* No symtabs found -- discard user_filename. */ + xfree (user_filename); + + /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ + VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL); + } + } + /* 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) + { + /* TOKEN is the _next_ token, not the one currently in the parser. + Consuming the token will give the correct error message. */ + linespec_lexer_consume_token (parser); + unexpected_linespec_error (parser); + } + else + { + /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ + VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL); } - if (function_symbols) - throw_exception (file_exception); + /* Parse the rest of the linespec. */ + linespec_parse_basic (parser); - /* Look up that token as a variable. - If file specified, use that file's per-file block to start with. */ + if (PARSER_RESULT (parser)->function_symbols == NULL + && PARSER_RESULT (parser)->labels.label_symbols == NULL + && PARSER_RESULT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN + && PARSER_RESULT (parser)->minimal_symbols == NULL) + { + /* The linespec didn't parse. Re-throw the file exception if + there was one. */ + if (file_exception.reason < 0) + throw_exception (file_exception); - { - struct symtabs_and_lines values; + /* Otherwise, the symbol is not found. */ + symbol_not_found_error (PARSER_RESULT (parser)->function_name, + PARSER_RESULT (parser)->source_filename); + } - values = decode_variable (self, copy); - do_cleanups (cleanup); - return values; - } + convert_to_sals: + + /* Get the last token and record how much of the input was parsed, + 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; + + /* Convert the data in PARSER_RESULT to SALs. */ + values = convert_linespec_to_sals (PARSER_STATE (parser), + PARSER_RESULT (parser)); + + return values; } + /* A constructor for linespec_state. */ static void linespec_state_constructor (struct linespec_state *self, - int flags, + int flags, const struct language_defn *language, struct symtab *default_symtab, int default_line, struct linespec_result *canonical) { memset (self, 0, sizeof (*self)); + self->language = language; self->funfirstline = (flags & DECODE_LINE_FUNFIRSTLINE) ? 1 : 0; self->list_mode = (flags & DECODE_LINE_LIST_MODE) ? 1 : 0; self->default_symtab = default_symtab; @@ -1237,19 +2191,64 @@ linespec_state_constructor (struct linespec_state *self, xfree, xcalloc, xfree); } +/* Initialize a new linespec parser. */ + +static void +linespec_parser_new (linespec_parser *parser, + int flags, const struct language_defn *language, + struct symtab *default_symtab, + int default_line, + struct linespec_result *canonical) +{ + parser->lexer.current.type = LSTOKEN_CONSUMED; + memset (PARSER_RESULT (parser), 0, sizeof (struct linespec)); + PARSER_RESULT (parser)->line_offset.sign = LINE_OFFSET_UNKNOWN; + linespec_state_constructor (PARSER_STATE (parser), flags, language, + default_symtab, default_line, canonical); +} + /* A destructor for linespec_state. */ static void -linespec_state_destructor (void *arg) +linespec_state_destructor (struct linespec_state *self) { - struct linespec_state *self = arg; - - xfree (self->user_filename); - xfree (self->user_function); - VEC_free (symtab_p, self->file_symtabs); htab_delete (self->addr_set); } +/* Delete a linespec parser. */ + +static void +linespec_parser_delete (void *arg) +{ + linespec_parser *parser = (linespec_parser *) arg; + + if (PARSER_RESULT (parser)->expression) + xfree (PARSER_RESULT (parser)->expression); + if (PARSER_RESULT (parser)->source_filename) + xfree (PARSER_RESULT (parser)->source_filename); + if (PARSER_RESULT (parser)->label_name) + xfree (PARSER_RESULT (parser)->label_name); + if (PARSER_RESULT (parser)->function_name) + xfree (PARSER_RESULT (parser)->function_name); + + if (PARSER_RESULT (parser)->file_symtabs != NULL) + VEC_free (symtab_p, PARSER_RESULT (parser)->file_symtabs); + + if (PARSER_RESULT (parser)->function_symbols != NULL) + VEC_free (symbolp, PARSER_RESULT (parser)->function_symbols); + + if (PARSER_RESULT (parser)->minimal_symbols != NULL) + VEC_free (minsym_and_objfile_d, PARSER_RESULT (parser)->minimal_symbols); + + if (PARSER_RESULT (parser)->labels.label_symbols != NULL) + VEC_free (symbolp, PARSER_RESULT (parser)->labels.label_symbols); + + if (PARSER_RESULT (parser)->labels.function_symbols != NULL) + VEC_free (symbolp, PARSER_RESULT (parser)->labels.function_symbols); + + linespec_state_destructor (PARSER_STATE (parser)); +} + /* See linespec.h. */ void @@ -1260,10 +2259,11 @@ decode_line_full (char **argptr, int flags, const char *filter) { struct symtabs_and_lines result; - struct linespec_state state; struct cleanup *cleanups; char *arg_start = *argptr; VEC (const_char_ptr) *filters = NULL; + linespec_parser parser; + struct linespec_state *state; gdb_assert (canonical != NULL); /* The filter only makes sense for 'all'. */ @@ -1274,12 +2274,13 @@ decode_line_full (char **argptr, int flags, || select_mode == multiple_symbols_cancel); gdb_assert ((flags & DECODE_LINE_LIST_MODE) == 0); - linespec_state_constructor (&state, flags, - default_symtab, default_line, canonical); - cleanups = make_cleanup (linespec_state_destructor, &state); + linespec_parser_new (&parser, flags, current_language, default_symtab, + default_line, canonical); + cleanups = make_cleanup (linespec_parser_delete, &parser); save_current_program_space (); - result = decode_line_internal (&state, argptr); + result = parse_linespec (&parser, argptr); + state = PARSER_STATE (&parser); gdb_assert (result.nelts == 1 || canonical->pre_expanded); gdb_assert (canonical->addr_string != NULL); @@ -1290,15 +2291,15 @@ decode_line_full (char **argptr, int flags, { int i; - if (state.canonical_names == NULL) - state.canonical_names = xcalloc (result.nelts, sizeof (char *)); - make_cleanup (xfree, state.canonical_names); + if (state->canonical_names == NULL) + state->canonical_names = xcalloc (result.nelts, sizeof (char *)); + make_cleanup (xfree, state->canonical_names); for (i = 0; i < result.nelts; ++i) { - if (state.canonical_names[i] == NULL) - state.canonical_names[i] = savestring (arg_start, - *argptr - arg_start); - make_cleanup (xfree, state.canonical_names[i]); + if (state->canonical_names[i] == NULL) + state->canonical_names[i] = savestring (arg_start, + *argptr - arg_start); + make_cleanup (xfree, state->canonical_names[i]); } } @@ -1316,13 +2317,13 @@ decode_line_full (char **argptr, int flags, { make_cleanup (VEC_cleanup (const_char_ptr), &filters); VEC_safe_push (const_char_ptr, filters, filter); - filter_results (&state, &result, filters); + filter_results (state, &result, filters); } else - convert_results_to_lsals (&state, &result); + convert_results_to_lsals (state, &result); } else - decode_line_2 (&state, &result, select_mode); + decode_line_2 (state, &result, select_mode); do_cleanups (cleanups); } @@ -1333,15 +2334,16 @@ decode_line_1 (char **argptr, int flags, int default_line) { struct symtabs_and_lines result; - struct linespec_state state; + linespec_parser parser; struct cleanup *cleanups; - linespec_state_constructor (&state, flags, - default_symtab, default_line, NULL); - cleanups = make_cleanup (linespec_state_destructor, &state); + linespec_parser_new (&parser, flags, current_language, default_symtab, + default_line, NULL); + cleanups = make_cleanup (linespec_parser_delete, &parser); save_current_program_space (); - result = decode_line_internal (&state, argptr); + result = parse_linespec (&parser, argptr); + do_cleanups (cleanups); return result; } @@ -1369,178 +2371,20 @@ initialize_defaults (struct symtab **default_symtab, int *default_line) -/* Decode arg of the form *PC. */ +/* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR, + advancing EXP_PTR past any parsed text. */ -static struct symtabs_and_lines -decode_indirect (struct linespec_state *self, char **argptr) +static CORE_ADDR +linespec_expression_to_pc (char **exp_ptr) { - struct symtabs_and_lines values; - CORE_ADDR pc; - char *initial = *argptr; - if (current_program_space->executing_startup) /* The error message doesn't really matter, because this case should only hit during breakpoint reset. */ throw_error (NOT_FOUND_ERROR, _("cannot evaluate expressions while " "program space is in startup")); - (*argptr)++; - pc = value_as_address (parse_to_comma_and_eval (argptr)); - - values.sals = (struct symtab_and_line *) - xmalloc (sizeof (struct symtab_and_line)); - - values.nelts = 1; - values.sals[0] = find_pc_line (pc, 0); - values.sals[0].pc = pc; - values.sals[0].section = find_pc_overlay (pc); - values.sals[0].explicit_pc = 1; - - if (self->canonical) - self->canonical->addr_string = savestring (initial, *argptr - initial); - - return values; -} - - - -/* Locate the first half of the linespec, ending in a colon, period, - or whitespace. (More or less.) Also, check to see if *ARGPTR is - enclosed in double quotes; if so, set is_quote_enclosed, advance - ARGPTR past that and zero out the trailing double quote. - If ARGPTR is just a simple name like "main", p will point to "" - at the end. */ - -static char * -locate_first_half (char **argptr, int *is_quote_enclosed) -{ - char *ii; - char *p, *p1; - int has_comma; - - /* Check if the linespec starts with an Ada operator (such as "+", - or ">", for instance). */ - p = *argptr; - if (p[0] == '"' - && current_language->la_language == language_ada) - { - const struct ada_opname_map *op; - - for (op = ada_opname_table; op->encoded != NULL; op++) - if (strncmp (op->decoded, p, strlen (op->decoded)) == 0) - break; - if (op->encoded != NULL) - { - *is_quote_enclosed = 0; - return p + strlen (op->decoded); - } - } - - /* Maybe we were called with a line range FILENAME:LINENUM,FILENAME:LINENUM - and we must isolate the first half. Outer layers will call again later - for the second half. - - Don't count commas that appear in argument lists of overloaded - functions, or in quoted strings. It's stupid to go to this much - trouble when the rest of the function is such an obvious roach hotel. */ - ii = find_toplevel_char (*argptr, ','); - has_comma = (ii != 0); - - /* Temporarily zap out second half to not confuse the code below. - This is undone below. Do not change ii!! */ - if (has_comma) - { - *ii = '\0'; - } - - /* Maybe arg is FILE : LINENUM or FILE : FUNCTION. May also be - CLASS::MEMBER, or NAMESPACE::NAME. Look for ':', but ignore - inside of <>. */ - - p = *argptr; - if (p[0] == '"') - { - *is_quote_enclosed = 1; - (*argptr)++; - p++; - } - else - { - *is_quote_enclosed = 0; - if (strchr (get_gdb_completer_quote_characters (), *p)) - { - ++(*argptr); - ++p; - } - } - - - /* Check for a drive letter in the filename. This is done on all hosts - to capture cross-compilation environments. On Unixen, directory - separators are illegal in filenames, so if the user enters "e:/foo.c", - he is referring to a directory named "e:" and a source file named - "foo.c", and we still want to keep these two pieces together. */ - if (isalpha (p[0]) && p[1] == ':' && IS_DIR_SEPARATOR (p[2])) - p += 3; - - for (; *p; p++) - { - if (p[0] == '<') - { - char *temp_end = find_template_name_end (p); - - if (!temp_end) - error (_("malformed template specification in command")); - p = temp_end; - } - - if (p[0] == '(') - p = find_method_overload_end (p); - - /* Check for a colon and a plus or minus and a [ (which - indicates an Objective-C method). */ - if (is_objc_method_format (p)) - { - break; - } - /* Check for the end of the first half of the linespec. End of - line, a tab, a colon or a space. But if enclosed in double - quotes we do not break on enclosed spaces. */ - if (!*p - || p[0] == '\t' - || (p[0] == ':') - || ((p[0] == ' ') && !*is_quote_enclosed)) - break; - if (p[0] == '.' && strchr (p, ':') == NULL) - { - /* Java qualified method. Find the *last* '.', since the - others are package qualifiers. Stop at any open parenthesis - which might provide overload information. */ - for (p1 = p; *p1 && *p1 != '('; p1++) - { - if (*p1 == '.') - p = p1; - } - break; - } - } - p = skip_spaces (p); - - /* If the closing double quote was left at the end, remove it. */ - if (*is_quote_enclosed) - { - char *closing_quote = strchr (p - 1, '"'); - - if (closing_quote && closing_quote[1] == '\0') - *closing_quote = '\0'; - } - - /* Now that we've safely parsed the first half, put back ',' so - outer layers can see it. */ - if (has_comma) - *ii = ','; - - return p; + (*exp_ptr)++; + return value_as_address (parse_to_comma_and_eval (exp_ptr)); } @@ -1553,28 +2397,34 @@ locate_first_half (char **argptr, int *is_quote_enclosed) the existing C++ code to let the user choose one. */ static struct symtabs_and_lines -decode_objc (struct linespec_state *self, char **argptr) +decode_objc (struct linespec_state *self, linespec_p ls, char **argptr) { struct collect_info info; VEC (const_char_ptr) *symbol_names = NULL; + struct symtabs_and_lines values; char *new_argptr; struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr), &symbol_names); info.state = self; - info.result.sals = NULL; - info.result.nelts = 0; + info.file_symtabs = NULL; + VEC_safe_push (symtab_p, info.file_symtabs, NULL); + info.result.symbols = NULL; + info.result.minimal_symbols = NULL; + values.nelts = 0; + values.sals = NULL; new_argptr = find_imps (*argptr, &symbol_names); if (VEC_empty (const_char_ptr, symbol_names)) { do_cleanups (cleanup); - return info.result; + return values; } add_all_symbol_names_from_pspace (&info, NULL, symbol_names); - if (info.result.nelts > 0) + if (!VEC_empty (symbolp, info.result.symbols) + || !VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols)) { char *saved_arg; @@ -1582,12 +2432,16 @@ decode_objc (struct linespec_state *self, char **argptr) memcpy (saved_arg, *argptr, new_argptr - *argptr); saved_arg[new_argptr - *argptr] = '\0'; + ls->function_symbols = info.result.symbols; + ls->minimal_symbols = info.result.minimal_symbols; + values = convert_linespec_to_sals (self, ls); + if (self->canonical) { self->canonical->pre_expanded = 1; - if (self->user_filename) + if (ls->source_filename) self->canonical->addr_string - = xstrprintf ("%s:%s", self->user_filename, saved_arg); + = xstrprintf ("%s:%s", ls->source_filename, saved_arg); else self->canonical->addr_string = xstrdup (saved_arg); } @@ -1596,241 +2450,8 @@ decode_objc (struct linespec_state *self, char **argptr) *argptr = new_argptr; do_cleanups (cleanup); - return info.result; -} -/* This handles C++ and Java compound data structures. P should point - at the first component separator, i.e. double-colon or period. As - an example, on entrance to this function we could have ARGPTR - pointing to "AAA::inA::fun" and P pointing to "::inA::fun". */ - -static struct symtabs_and_lines -decode_compound (struct linespec_state *self, - char **argptr, char *the_real_saved_arg, char *p) -{ - struct symtabs_and_lines values; - char *p2; - char *saved_arg2 = *argptr; - char *temp_end; - struct symbol *sym; - char *copy; - VEC (symbolp) *sym_classes; - char *saved_arg, *class_name; - struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); - - /* If the user specified any completer quote characters in the input, - strip them. They are superfluous. */ - saved_arg = alloca (strlen (the_real_saved_arg) + 1); - { - char *dst = saved_arg; - char *src = the_real_saved_arg; - char *quotes = get_gdb_completer_quote_characters (); - while (*src != '\0') - { - if (strchr (quotes, *src) == NULL) - *dst++ = *src; - ++src; - } - *dst = '\0'; - } - - /* First check for "global" namespace specification, of the form - "::foo". If found, skip over the colons and jump to normal - symbol processing. I.e. the whole line specification starts with - "::" (note the condition that *argptr == p). */ - if (p[0] == ':' - && ((*argptr == p) || (p[-1] == ' ') || (p[-1] == '\t'))) - saved_arg2 += 2; - - /* Given our example "AAA::inA::fun", we have two cases to consider: - - 1) AAA::inA is the name of a class. In that case, presumably it - has a method called "fun"; we then look up that method using - find_method. - - 2) AAA::inA isn't the name of a class. In that case, either the - user made a typo, AAA::inA is the name of a namespace, or it is - the name of a minimal symbol. - In this case we just delegate to decode_variable. - - Thus, our first task is to find everything before the last set of - double-colons and figure out if it's the name of a class. So we - first loop through all of the double-colons. */ - - p2 = p; /* Save for restart. */ - - /* This is very messy. Following the example above we have now the - following pointers: - p -> "::inA::fun" - argptr -> "AAA::inA::fun - saved_arg -> "AAA::inA::fun - saved_arg2 -> "AAA::inA::fun - p2 -> "::inA::fun". */ - - /* In the loop below, with these strings, we'll make 2 passes, each - is marked in comments. */ - - while (1) - { - static char *break_characters = " \t("; - - /* Move pointer up to next possible class/namespace token. */ - - p = p2 + 1; /* Restart with old value +1. */ - - /* PASS1: at this point p2->"::inA::fun", so p->":inA::fun", - i.e. if there is a double-colon, p will now point to the - second colon. */ - /* PASS2: p2->"::fun", p->":fun" */ - - /* Move pointer ahead to next double-colon. */ - while (*p - && strchr (break_characters, *p) == NULL - && strchr (get_gdb_completer_quote_characters (), *p) == NULL) - { - if (current_language->la_language == language_cplus) - p += cp_validate_operator (p); - - if (p[0] == '<') - { - temp_end = find_template_name_end (p); - if (!temp_end) - error (_("malformed template specification in command")); - p = temp_end; - } - /* Note that, since, at the start of this loop, p would be - pointing to the second colon in a double-colon, we only - satisfy the condition below if there is another - double-colon to the right (after). I.e. there is another - component that can be a class or a namespace. I.e, if at - the beginning of this loop (PASS1), we had - p->":inA::fun", we'll trigger this when p has been - advanced to point to "::fun". */ - /* PASS2: we will not trigger this. */ - else if ((p[0] == ':') && (p[1] == ':')) - break; /* Found double-colon. */ - else - { - /* PASS2: We'll keep getting here, until P points to one of the - break characters, at which point we exit this loop. */ - if (*p) - { - if (p[1] == '(' - && strncmp (&p[1], CP_ANONYMOUS_NAMESPACE_STR, - CP_ANONYMOUS_NAMESPACE_LEN) == 0) - p += CP_ANONYMOUS_NAMESPACE_LEN; - else if (strchr (break_characters, *p) == NULL) - ++p; - } - } - } - - if (*p != ':') - break; /* Out of the while (1). This would happen - for instance if we have looked up - unsuccessfully all the components of the - string, and p->""(PASS2). */ - - /* We get here if p points to one of the break characters or "" (i.e., - string ended). */ - /* Save restart for next time around. */ - p2 = p; - /* Restore argptr as it was on entry to this function. */ - *argptr = saved_arg2; - /* PASS1: at this point p->"::fun" argptr->"AAA::inA::fun", - p2->"::fun". */ - - /* All ready for next pass through the loop. */ - } /* while (1) */ - - - /* Start of lookup in the symbol tables. */ - - /* Lookup in the symbol table the substring between argptr and - p. Note, this call changes the value of argptr. */ - /* Before the call, argptr->"AAA::inA::fun", - p->"", p2->"::fun". After the call: argptr->"fun", p, p2 - unchanged. */ - sym_classes = lookup_prefix_sym (argptr, p2, self->file_symtabs, - &class_name); - make_cleanup (VEC_cleanup (symbolp), &sym_classes); - make_cleanup (xfree, class_name); - - /* If a class has been found, then we're in case 1 above. So we - look up "fun" as a method of those classes. */ - if (!VEC_empty (symbolp, sym_classes)) - { - /* Arg token is not digits => try it as a function name. - Find the next token (everything up to end or next - blank). */ - if (**argptr - && strchr (get_gdb_completer_quote_characters (), - **argptr) != NULL) - { - p = skip_quoted (*argptr); - *argptr = *argptr + 1; - } - else - { - /* At this point argptr->"fun". */ - char *a; - - p = *argptr; - while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p != ':' - && *p != '(') - p++; - /* At this point p->"". String ended. */ - /* Nope, C++ operators could have spaces in them - ("foo::operator <" or "foo::operator delete []"). - I apologize, this is a bit hacky... */ - if (current_language->la_language == language_cplus - && *p == ' ' && p - 8 - *argptr + 1 > 0) - { - /* The above loop has already swallowed "operator". */ - p += cp_validate_operator (p - 8) - 8; - } - - /* Keep any important naming information. */ - p = keep_name_info (p, 1); - } - - /* Allocate our own copy of the substring between argptr and - p. */ - copy = (char *) alloca (p - *argptr + 1); - memcpy (copy, *argptr, p - *argptr); - copy[p - *argptr] = '\0'; - if (p != *argptr - && copy[p - *argptr - 1] - && strchr (get_gdb_completer_quote_characters (), - copy[p - *argptr - 1]) != NULL) - copy[p - *argptr - 1] = '\0'; - - /* At this point copy->"fun", p->"". */ - - /* No line number may be specified. */ - *argptr = skip_spaces (p); - /* At this point arptr->"". */ - - /* Look for copy as a method of sym_class. */ - /* At this point copy->"fun", sym_class is "AAA:inA", - saved_arg->"AAA::inA::fun". This concludes the scanning of - the string for possible components matches. If we find it - here, we return. If not, and we are at the and of the string, - we'll lookup the whole string in the symbol tables. */ - - values = find_method (self, saved_arg, copy, class_name, sym_classes); - - do_cleanups (cleanup); - return values; - } /* End if symbol found. */ - - - /* We couldn't find a class, so we're in case 2 above. We check the - entire name as a symbol instead. The simplest way to do this is - to just throw an exception and let our caller fall through to - decode_variable. */ - - throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter")); + return values; } /* An instance of this type is used when collecting prefix symbols for @@ -1876,46 +2497,20 @@ collect_one_symbol (struct symbol *sym, void *d) return 1; /* Continue iterating. */ } -/* Return the symbol corresponding to the substring of *ARGPTR ending - at P, allowing whitespace. Also, advance *ARGPTR past the symbol - name in question, the compound object separator ("::" or "."), and - whitespace. Note that *ARGPTR is changed whether or not the - this call finds anything (i.e we return NULL). As an - example, say ARGPTR is "AAA::inA::fun" and P is "::inA::fun". */ +/* Return any symbols corresponding to CLASS_NAME in FILE_SYMTABS. */ static VEC (symbolp) * -lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs, - char **class_name) +lookup_prefix_sym (struct linespec_state *state, VEC (symtab_p) *file_symtabs, + const char *class_name) { - char *p1; - char *copy; int ix; struct symtab *elt; struct decode_compound_collector collector; struct cleanup *outer; struct cleanup *cleanup; - struct block *search_block; - - /* Extract the class name. */ - p1 = p; - while (p != *argptr && p[-1] == ' ') - --p; - copy = (char *) xmalloc (p - *argptr + 1); - memcpy (copy, *argptr, p - *argptr); - copy[p - *argptr] = 0; - *class_name = copy; - outer = make_cleanup (xfree, copy); - - /* Discard the class name from the argptr. */ - p = p1 + (p1[0] == ':' ? 2 : 1); - p = skip_spaces (p); - *argptr = p; - - /* At this point p1->"::inA::fun", p->"inA::fun" copy->"AAA", - argptr->"inA::fun". */ collector.symbols = NULL; - make_cleanup (VEC_cleanup (symbolp), &collector.symbols); + outer = make_cleanup (VEC_cleanup (symbolp), &collector.symbols); collector.unique_syms = htab_create_alloc (1, htab_hash_pointer, htab_eq_pointer, NULL, @@ -1926,10 +2521,10 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs, { if (elt == NULL) { - iterate_over_all_matching_symtabs (copy, STRUCT_DOMAIN, + iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN, collect_one_symbol, &collector, NULL, 0); - iterate_over_all_matching_symtabs (copy, VAR_DOMAIN, + iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN, collect_one_symbol, &collector, NULL, 0); } @@ -1942,9 +2537,9 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs, gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup); set_current_program_space (SYMTAB_PSPACE (elt)); search_block = get_search_block (elt); - LA_ITERATE_OVER_SYMBOLS (search_block, copy, STRUCT_DOMAIN, + LA_ITERATE_OVER_SYMBOLS (search_block, class_name, STRUCT_DOMAIN, collect_one_symbol, &collector); - LA_ITERATE_OVER_SYMBOLS (search_block, copy, VAR_DOMAIN, + LA_ITERATE_OVER_SYMBOLS (search_block, class_name, VAR_DOMAIN, collect_one_symbol, &collector); } } @@ -1984,6 +2579,34 @@ compare_symbols (const void *a, const void *b) return 0; } +/* Like compare_symbols but for minimal symbols. */ + +static int +compare_msymbols (const void *a, const void *b) +{ + struct minimal_symbol * const *sa = a; + struct minimal_symbol * const *sb = b; + uintptr_t uia, uib; + + uia = (uintptr_t) SYMBOL_OBJ_SECTION (*sa)->objfile->pspace; + uib = (uintptr_t) SYMBOL_OBJ_SECTION (*sb)->objfile->pspace; + + if (uia < uib) + return -1; + if (uia > uib) + return 1; + + uia = (uintptr_t) *sa; + uib = (uintptr_t) *sb; + + if (uia < uib) + return -1; + if (uia > uib) + return 1; + + return 0; +} + /* Look for all the matching instances of each symbol in NAMES. Only instances from PSPACE are considered; other program spaces are handled by our caller. If PSPACE is NULL, then all program spaces @@ -2031,14 +2654,16 @@ find_superclass_methods (VEC (typep) *superclasses, do_cleanups (cleanup); } -/* This finds the method COPY in the class whose type is given by one - of the symbols in SYM_CLASSES. */ +/* This finds the method METHOD_NAME in the class CLASS_NAME whose type is + given by one of the symbols in SYM_CLASSES. Matches are returned + in SYMBOLS (for debug symbols) and MINSYMS (for minimal symbols). */ -static struct symtabs_and_lines -find_method (struct linespec_state *self, char *saved_arg, - char *copy, const char *class_name, VEC (symbolp) *sym_classes) +static void +find_method (struct linespec_state *self, VEC (symtab_p) *file_symtabs, + const char *class_name, const char *method_name, + VEC (symbolp) *sym_classes, VEC (symbolp) **symbols, + VEC (minsym_and_objfile_d) **minsyms) { - char *canon; struct symbol *sym; struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); int ix; @@ -2048,15 +2673,6 @@ find_method (struct linespec_state *self, char *saved_arg, struct collect_info info; char *name_iter; - /* NAME is typed by the user: it needs to be canonicalized before - searching the symbol tables. */ - canon = cp_canonicalize_string_no_typedefs (copy); - if (canon != NULL) - { - copy = canon; - make_cleanup (xfree, copy); - } - /* Sort symbols so that symbols with the same program space are next to each other. */ qsort (VEC_address (symbolp, sym_classes), @@ -2065,11 +2681,12 @@ find_method (struct linespec_state *self, char *saved_arg, compare_symbols); info.state = self; - info.result.sals = NULL; - info.result.nelts = 0; + info.file_symtabs = file_symtabs; + info.result.symbols = NULL; + info.result.minimal_symbols = NULL; /* Iterate over all the types, looking for the names of existing - methods matching COPY. If we cannot find a direct method in a + methods matching METHOD_NAME. If we cannot find a direct method in a given program space, then we consider inherited methods; this is not ideal (ideal would be to respect C++ hiding rules), but it seems good enough and is what GDB has historically done. We only @@ -2093,7 +2710,7 @@ find_method (struct linespec_state *self, char *saved_arg, pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)); set_current_program_space (pspace); t = check_typedef (SYMBOL_TYPE (sym)); - find_methods (t, copy, &result_names, &superclass_vec); + find_methods (t, method_name, &result_names, &superclass_vec); /* Handle all items from a single program space at once; and be sure not to miss the last batch. */ @@ -2105,7 +2722,8 @@ find_method (struct linespec_state *self, char *saved_arg, /* If we did not find a direct implementation anywhere in this program space, consider superclasses. */ if (VEC_length (const_char_ptr, result_names) == last_result_len) - find_superclass_methods (superclass_vec, copy, &result_names); + find_superclass_methods (superclass_vec, method_name, + &result_names); /* We have a list of candidate symbol names, so now we iterate over the symbol tables looking for all @@ -2117,31 +2735,18 @@ find_method (struct linespec_state *self, char *saved_arg, } } - if (info.result.nelts > 0) + if (!VEC_empty (symbolp, info.result.symbols) + || !VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols)) { - if (self->canonical) - { - self->canonical->pre_expanded = 1; - if (self->user_filename) - self->canonical->addr_string - = xstrprintf ("%s:%s", self->user_filename, saved_arg); - else - self->canonical->addr_string = xstrdup (saved_arg); - } - + *symbols = info.result.symbols; + *minsyms = info.result.minimal_symbols; do_cleanups (cleanup); - - return info.result; + return; } - if (copy[0] == '~') - cplusplus_error (saved_arg, - "the class `%s' does not have destructor defined\n", - class_name); - else - cplusplus_error (saved_arg, - "the class %s does not have any method named %s\n", - class_name, copy); + /* Throw an NOT_FOUND_ERROR. This will be caught by the caller + and other attempts to locate the symbol will be made. */ + throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter")); } @@ -2203,35 +2808,14 @@ collect_symtabs_from_filename (const char *file) return collector.symtabs; } -/* Return all the symtabs associated to the filename given by the - substring of *ARGPTR ending at P, and advance ARGPTR past that - filename. */ +/* Return all the symtabs associated to the FILENAME. */ static VEC (symtab_p) * -symtabs_from_filename (char **argptr, char *p, int is_quote_enclosed, - char **user_filename) +symtabs_from_filename (const char *filename) { - char *p1; - char *copy; - struct cleanup *outer; VEC (symtab_p) *result; - p1 = p; - while (p != *argptr && p[-1] == ' ') - --p; - if ((*p == '"') && is_quote_enclosed) - --p; - copy = xmalloc (p - *argptr + 1); - outer = make_cleanup (xfree, copy); - memcpy (copy, *argptr, p - *argptr); - /* It may have the ending quote right after the file name. */ - if ((is_quote_enclosed && copy[p - *argptr - 1] == '"') - || copy[p - *argptr - 1] == '\'') - copy[p - *argptr - 1] = 0; - else - copy[p - *argptr] = 0; - - result = collect_symtabs_from_filename (copy); + result = collect_symtabs_from_filename (filename); if (VEC_empty (symtab_p, result)) { @@ -2239,70 +2823,211 @@ symtabs_from_filename (char **argptr, char *p, int is_quote_enclosed, throw_error (NOT_FOUND_ERROR, _("No symbol table is loaded. " "Use the \"file\" command.")); - throw_error (NOT_FOUND_ERROR, _("No source file named %s."), copy); + throw_error (NOT_FOUND_ERROR, _("No source file named %s."), filename); } - /* Discard the file name from the arg. */ - if (*p1 == '\0') - *argptr = p1; - else - *argptr = skip_spaces (p1 + 1); - - discard_cleanups (outer); - *user_filename = copy; return result; } -/* A callback used by iterate_over_all_matching_symtabs that collects - symbols for find_function_symbols. */ +/* Look up a function symbol named NAME in symtabs FILE_SYMTABS. Matching + debug symbols are returned in SYMBOLS. Matching minimal symbols are + returned in MINSYMS. */ -static int -collect_function_symbols (struct symbol *sym, void *arg) +static void +find_function_symbols (struct linespec_state *state, + VEC (symtab_p) *file_symtabs, const char *name, + VEC (symbolp) **symbols, + VEC (minsym_and_objfile_d) **minsyms) { - VEC (symbolp) **syms = arg; + struct collect_info info; + VEC (const_char_ptr) *symbol_names = NULL; + struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr), + &symbol_names); - if (SYMBOL_CLASS (sym) == LOC_BLOCK) - VEC_safe_push (symbolp, *syms, sym); + info.state = state; + info.result.symbols = NULL; + info.result.minimal_symbols = NULL; + info.file_symtabs = file_symtabs; - return 1; /* Continue iterating. */ + /* Try NAME as an Objective-C selector. */ + find_imps ((char *) name, &symbol_names); + if (!VEC_empty (const_char_ptr, symbol_names)) + add_all_symbol_names_from_pspace (&info, NULL, symbol_names); + else + add_matching_symbols_to_info (name, &info, NULL); + + do_cleanups (cleanup); + + if (VEC_empty (symbolp, info.result.symbols)) + { + VEC_free (symbolp, info.result.symbols); + *symbols = NULL; + } + else + *symbols = info.result.symbols; + + if (VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols)) + { + VEC_free (minsym_and_objfile_d, info.result.minimal_symbols); + *minsyms = NULL; + } + else + *minsyms = info.result.minimal_symbols; } -/* Look up a function symbol in *ARGPTR. If found, advance *ARGPTR - and return the symbol. If not found, return NULL. */ +/* Find all symbols named NAME in FILE_SYMTABS, returning debug symbols + in SYMBOLS and minimal symbols in MINSYMS. */ + +void +find_linespec_symbols (struct linespec_state *state, + VEC (symtab_p) *file_symtabs, + const char *name, + VEC (symbolp) **symbols, + VEC (minsym_and_objfile_d) **minsyms) +{ + char *klass, *method, *canon; + const char *lookup_name, *last, *p, *scope_op; + struct cleanup *cleanup; + VEC (symbolp) *classes; + volatile struct gdb_exception except; + + 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); + } + + canon = cp_canonicalize_string_no_typedefs (lookup_name); + if (canon != NULL) + { + lookup_name = canon; + cleanup = make_cleanup (xfree, canon); + } + + /* See if we can find a scope operator and break this symbol + 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) + { + last = p; + p = find_toplevel_string (p + strlen (scope_op), scope_op); + } + + /* If no scope operator was found, lookup the name as a symbol. */ + if (last == NULL) + { + find_function_symbols (state, file_symtabs, lookup_name, + symbols, minsyms); + do_cleanups (cleanup); + return; + } + + /* 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'; + + /* Skip past the scope operator. */ + last += strlen (scope_op); + method = xmalloc ((strlen (last) + 1) * sizeof (char)); + make_cleanup (xfree, method); + strcpy (method, last); + + /* Find a list of classes named KLASS. */ + classes = lookup_prefix_sym (state, file_symtabs, klass); + if (!VEC_empty (symbolp, classes)) + { + /* Now locate a list of suitable methods named METHOD. */ + TRY_CATCH (except, RETURN_MASK_ERROR) + { + find_method (state, file_symtabs, klass, method, classes, + symbols, minsyms); + } + + /* If successful, we're done. If NOT_FOUND_ERROR + was not thrown, rethrow the exception that we did get. + Otherwise, fall back to looking up the entire name as a symbol. + This can happen with namespace::function. */ + if (except.reason >= 0) + { + do_cleanups (cleanup); + return; + } + else if (except.error != NOT_FOUND_ERROR) + throw_exception (except); + } + + /* We couldn't find a class, so we check the entire name as a symbol + instead. */ + find_function_symbols (state, file_symtabs, lookup_name, symbols, minsyms); + do_cleanups (cleanup); +} + +/* Return all labels named NAME in FUNCTION_SYMBOLS. Return the + actual function symbol in which the label was found in LABEL_FUNC_RET. */ static VEC (symbolp) * -find_function_symbols (char **argptr, char *p, int is_quote_enclosed, - char **user_function) +find_label_symbols (struct linespec_state *self, + VEC (symbolp) *function_symbols, + VEC (symbolp) **label_funcs_ret, const char *name) { - char *p1; - char *copy; + int ix; + struct block *block; + struct symbol *sym; + struct symbol *fn_sym; VEC (symbolp) *result = NULL; - p1 = p; - while (p != *argptr && p[-1] == ' ') - --p; - if ((*p == '"') && is_quote_enclosed) - --p; - copy = (char *) xmalloc (p - *argptr + 1); - *user_function = copy; - memcpy (copy, *argptr, p - *argptr); - /* It may have the ending quote right after the file name. */ - if ((is_quote_enclosed && copy[p - *argptr - 1] == '"') - || copy[p - *argptr - 1] == '\'') - copy[p - *argptr - 1] = 0; - else - copy[p - *argptr] = 0; + if (function_symbols == NULL) + { + set_current_program_space (self->program_space); + block = get_search_block (NULL); + + for (; + block && !BLOCK_FUNCTION (block); + block = BLOCK_SUPERBLOCK (block)) + ; + if (!block) + return NULL; + fn_sym = BLOCK_FUNCTION (block); - iterate_over_all_matching_symtabs (copy, VAR_DOMAIN, - collect_function_symbols, &result, NULL, - 0); + sym = lookup_symbol (name, block, LABEL_DOMAIN, 0); - if (VEC_empty (symbolp, result)) - VEC_free (symbolp, result); + if (sym != NULL) + { + VEC_safe_push (symbolp, result, sym); + VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); + } + } else { - /* Discard the file name from the arg. */ - *argptr = skip_spaces (p1 + 1); + for (ix = 0; + VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix) + { + set_current_program_space (SYMTAB_PSPACE (SYMBOL_SYMTAB (fn_sym))); + block = SYMBOL_BLOCK_VALUE (fn_sym); + sym = lookup_symbol (name, block, LABEL_DOMAIN, 0); + + if (sym != NULL) + { + VEC_safe_push (symbolp, result, sym); + VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); + } + } } return result; @@ -2310,10 +3035,11 @@ find_function_symbols (char **argptr, char *p, int is_quote_enclosed, -/* A helper for decode_all_digits that handles the 'list_mode' case. */ +/* A helper for create_sals_line_offset that handles the 'list_mode' case. */ static void decode_digits_list_mode (struct linespec_state *self, + linespec_p ls, struct symtabs_and_lines *values, struct symtab_and_line val) { @@ -2322,7 +3048,8 @@ decode_digits_list_mode (struct linespec_state *self, gdb_assert (self->list_mode); - for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix) + for (ix = 0; VEC_iterate (symtab_p, ls->file_symtabs, ix, elt); + ++ix) { /* The logic above should ensure this. */ gdb_assert (elt != NULL); @@ -2341,11 +3068,12 @@ decode_digits_list_mode (struct linespec_state *self, } } -/* A helper for decode_all_digits that iterates over the symtabs, +/* A helper for create_sals_line_offset that iterates over the symtabs, adding lines to the VEC. */ static void decode_digits_ordinary (struct linespec_state *self, + linespec_p ls, int line, struct symtabs_and_lines *sals, struct linetable_entry **best_entry) @@ -2353,7 +3081,7 @@ decode_digits_ordinary (struct linespec_state *self, int ix; struct symtab *elt; - for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix) + for (ix = 0; VEC_iterate (symtab_p, ls->file_symtabs, ix, elt); ++ix) { int i; VEC (CORE_ADDR) *pcs; @@ -2381,381 +3109,64 @@ decode_digits_ordinary (struct linespec_state *self, } } -/* This decodes a line where the argument is all digits (possibly - preceded by a sign). Q should point to the end of those digits; - the other arguments are as usual. */ - -static struct symtabs_and_lines -decode_all_digits (struct linespec_state *self, - char **argptr, - char *q) -{ - struct symtabs_and_lines values; - struct symtab_and_line val; - int use_default = 0; - char *saved_arg = *argptr; - - enum sign - { - none, plus, minus - } - sign = none; - - init_sal (&val); - values.sals = NULL; - values.nelts = 0; - - /* This is where we need to make sure that 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 - set_default_source_symtab_and_line uses - select_source_symtab that calls us with such an argument. */ - - if (VEC_length (symtab_p, self->file_symtabs) == 1 - && VEC_index (symtab_p, self->file_symtabs, 0) == NULL) - { - set_current_program_space (self->program_space); - - /* Make sure we have at least a default source file. */ - set_default_source_symtab_and_line (); - initialize_defaults (&self->default_symtab, &self->default_line); - VEC_pop (symtab_p, self->file_symtabs); - VEC_free (symtab_p, self->file_symtabs); - self->file_symtabs - = collect_symtabs_from_filename (self->default_symtab->filename); - use_default = 1; - } - - if (**argptr == '+') - sign = plus, (*argptr)++; - else if (**argptr == '-') - sign = minus, (*argptr)++; - val.line = atoi (*argptr); - switch (sign) - { - case plus: - if (q == *argptr) - val.line = 5; - if (use_default) - val.line = self->default_line + val.line; - break; - case minus: - if (q == *argptr) - val.line = 15; - if (use_default) - val.line = self->default_line - val.line; - else - val.line = 1; - break; - case none: - break; /* No need to adjust val.line. */ - } - - *argptr = skip_spaces (q); - - if (self->list_mode) - decode_digits_list_mode (self, &values, val); - else - { - struct linetable_entry *best_entry = NULL; - int *filter; - 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, val.line, &intermediate_results, - &best_entry); - if (intermediate_results.nelts == 0 && best_entry != NULL) - decode_digits_ordinary (self, best_entry->line, &intermediate_results, - &best_entry); - - cleanup = make_cleanup (xfree, intermediate_results.sals); - - /* For optimized code, compiler can scatter one source line - accross disjoint ranges of PC values, even when no duplicate - functions or inline functions are involved. For example, - 'for (;;)' inside non-template non-inline non-ctor-or-dtor - function can result in two PC ranges. In this case, we don't - want to set breakpoint on first PC of each range. To filter - such cases, we use containing blocks -- for each PC found - above we see if there are other PCs that are in the same - block. If yes, the other PCs are filtered out. */ - - filter = xmalloc (intermediate_results.nelts * sizeof (int)); - make_cleanup (xfree, filter); - blocks = xmalloc (intermediate_results.nelts * sizeof (struct block *)); - make_cleanup (xfree, blocks); - - for (i = 0; i < intermediate_results.nelts; ++i) - { - set_current_program_space (intermediate_results.sals[i].pspace); - - filter[i] = 1; - blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc, - intermediate_results.sals[i].section); - } - - for (i = 0; i < intermediate_results.nelts; ++i) - { - if (blocks[i] != NULL) - for (j = i + 1; j < intermediate_results.nelts; ++j) - { - if (blocks[j] == blocks[i]) - { - filter[j] = 0; - break; - } - } - } - - for (i = 0; i < intermediate_results.nelts; ++i) - if (filter[i]) - { - struct symbol *sym = (blocks[i] - ? block_containing_function (blocks[i]) - : NULL); - - if (self->funfirstline) - skip_prologue_sal (&intermediate_results.sals[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], - sym ? SYMBOL_NATURAL_NAME (sym) : NULL); - } - - do_cleanups (cleanup); - } - - if (values.nelts == 0) - { - if (self->user_filename) - throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."), - val.line, self->user_filename); - else - throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."), - val.line); - } - - if (self->canonical) - { - char *copy = savestring (saved_arg, q - saved_arg); - - self->canonical->pre_expanded = 1; - gdb_assert (self->user_filename || use_default); - self->canonical->addr_string - = xstrprintf ("%s:%s", (self->user_filename - ? self->user_filename - : self->default_symtab->filename), - copy); - xfree (copy); - } - - return values; -} - -/* Decode a linespec starting with a dollar sign. */ +/* Return the line offset represented by VARIABLE. */ -static struct symtabs_and_lines -decode_dollar (struct linespec_state *self, char *copy) +static struct line_offset +linespec_parse_variable (struct linespec_state *self, const char *variable) { - LONGEST valx; int index = 0; - struct symtabs_and_lines values; - struct symtab_and_line val; - char *p; - struct symbol *sym; - struct minimal_symbol *msymbol; - int ix; - struct symtab *elt; + const char *p; + struct line_offset offset = {0, LINE_OFFSET_NONE}; - p = (copy[1] == '$') ? copy + 2 : copy + 1; + p = (variable[1] == '$') ? variable + 2 : variable + 1; + if (*p == '$') + ++p; while (*p >= '0' && *p <= '9') - p++; + ++p; if (!*p) /* Reached end of token without hitting non-digit. */ { /* We have a value history reference. */ struct value *val_history; - sscanf ((copy[1] == '$') ? copy + 2 : copy + 1, "%d", &index); - val_history = access_value_history ((copy[1] == '$') ? -index : index); + sscanf ((variable[1] == '$') ? variable + 2 : variable + 1, "%d", &index); + val_history + = access_value_history ((variable[1] == '$') ? -index : index); if (TYPE_CODE (value_type (val_history)) != TYPE_CODE_INT) error (_("History values used in line " "specs must have integer values.")); - valx = value_as_long (val_history); + offset.offset = value_as_long (val_history); } else { /* Not all digits -- may be user variable/function or a convenience variable. */ - - volatile struct gdb_exception exc; - - /* Avoid "may be used uninitialized" warning. */ - values.sals = NULL; - values.nelts = 0; - - TRY_CATCH (exc, RETURN_MASK_ERROR) - { - values = decode_variable (self, copy); - } - - if (exc.reason == 0) - return values; - - if (exc.error != NOT_FOUND_ERROR) - throw_exception (exc); - - /* Not a user variable or function -- must be convenience variable. */ - if (!get_internalvar_integer (lookup_internalvar (copy + 1), &valx)) - error (_("Convenience variables used in line " - "specs must have integer values.")); - } - - init_sal (&val); - - values.sals = NULL; - values.nelts = 0; - - for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix) - { - if (elt == NULL) - { - elt = self->default_symtab; - set_current_program_space (self->program_space); - } + LONGEST valx; + struct internalvar *ivar; + + /* Try it as a convenience variable. If it is not a convenience + variable, return and allow normal symbol lookup to occur. */ + ivar = lookup_only_internalvar (variable + 1); + if (ivar == NULL) + /* No internal variable with that name. Mark the offset + as unknown to allow the name to be looked up as a symbol. */ + offset.sign = LINE_OFFSET_UNKNOWN; else - set_current_program_space (SYMTAB_PSPACE (elt)); - - /* Either history value or convenience value from above, in valx. */ - val.symtab = elt; - val.line = valx; - val.pc = 0; - val.pspace = elt ? SYMTAB_PSPACE (elt) : current_program_space; - - add_sal_to_sals (self, &values, &val, NULL); - } - - if (self->canonical) - { - self->canonical->pre_expanded = 1; - if (self->user_filename) - self->canonical->addr_string = xstrprintf ("%s:%s", - self->user_filename, copy); - else - self->canonical->addr_string = xstrdup (copy); - } - - return values; -} - - - -/* A helper for decode_line_1 that tries to find a label. The label - is searched for in the current block. - FUNCTION_SYMBOLS is a list of the enclosing functions; or NULL if none - specified. - COPY is the name of the label to find. - CANONICAL is the same as the "canonical" argument to decode_line_1. - RESULT is a pointer to a symtabs_and_lines structure which will be - filled in on success. - This function returns 1 if a label was found, 0 otherwise. */ - -static int -decode_label (struct linespec_state *self, - VEC (symbolp) *function_symbols, char *copy, - struct symtabs_and_lines *result) -{ - struct symbol *fn_sym; - int ix; - - if (function_symbols == NULL) - { - struct block *block; - struct symbol *sym; - struct symtab_and_line sal; - struct symtabs_and_lines values; - - values.nelts = 0; - values.sals = NULL; - - set_current_program_space (self->program_space); - block = get_search_block (NULL); - - for (; - block && !BLOCK_FUNCTION (block); - block = BLOCK_SUPERBLOCK (block)) - ; - if (!block) - return 0; - fn_sym = BLOCK_FUNCTION (block); - - sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0); - - if (sym == NULL) - return 0; - - symbol_to_sal (&sal, self->funfirstline, sym); - add_sal_to_sals (self, &values, &sal, - SYMBOL_NATURAL_NAME (fn_sym)); - - if (self->canonical) { - self->canonical->special_display = 1; - self->canonical->addr_string - = xstrprintf ("%s:%s", SYMBOL_NATURAL_NAME (fn_sym), - copy); - } - - *result = values; - - return 1; - } - - result->sals = NULL; - result->nelts = 0; - - for (ix = 0; VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix) - { - struct block *block; - struct symbol *sym; - - set_current_program_space (SYMTAB_PSPACE (SYMBOL_SYMTAB (fn_sym))); - block = SYMBOL_BLOCK_VALUE (fn_sym); - sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0); - - if (sym != NULL) - { - struct symtab_and_line sal; - char *symname; - - symbol_to_sal (&sal, self->funfirstline, sym); - symname = xstrprintf ("%s:%s", - SYMBOL_NATURAL_NAME (fn_sym), - SYMBOL_NATURAL_NAME (sym)); - add_sal_to_sals (self, result, &sal, symname); - xfree (symname); + /* We found a valid variable name. If it is not an integer, + throw an error. */ + if (!get_internalvar_integer (ivar, &valx)) + error (_("Convenience variables used in line " + "specs must have integer values.")); + else + offset.offset = valx; } } - if (self->canonical && result->nelts > 0) - { - self->canonical->pre_expanded = 1; - self->canonical->special_display = 1; - - gdb_assert (self->user_function); - self->canonical->addr_string - = xstrprintf ("%s:%s", self->user_function, copy); - } - - return result->nelts > 0; + return offset; } + /* A callback used to possibly add a symbol to the results. */ @@ -2763,20 +3174,16 @@ static int collect_symbols (struct symbol *sym, void *data) { struct collect_info *info = data; - struct symtab_and_line sal; - - if (symbol_to_sal (&sal, info->state->funfirstline, sym) - && maybe_add_address (info->state->addr_set, - SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)), - sal.pc)) - add_sal_to_sals (info->state, &info->result, &sal, - SYMBOL_NATURAL_NAME (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 || info->state->list_mode) + VEC_safe_push (symbolp, info->result.symbols, sym); return 1; /* Continue iterating. */ } -/* We've found a minimal symbol MSYMBOL to associate with our - linespec; add it to the result symtabs_and_lines. */ +/* We've found a minimal symbol MSYMBOL in OBJFILE to associate with our + linespec; return the SAL in RESULT. */ static void minsym_found (struct linespec_state *self, struct objfile *objfile, @@ -2804,17 +3211,6 @@ minsym_found (struct linespec_state *self, struct objfile *objfile, add_sal_to_sals (self, result, &sal, SYMBOL_NATURAL_NAME (msymbol)); } -/* A helper struct which just holds a minimal symbol and the object - file from which it came. */ - -typedef struct minsym_and_objfile -{ - struct minimal_symbol *minsym; - struct objfile *objfile; -} minsym_and_objfile_d; - -DEF_VEC_O (minsym_and_objfile_d); - /* A helper struct to pass some data through iterate_over_minimal_symbols. */ @@ -2967,8 +3363,8 @@ search_minsyms_for_name (struct collect_info *info, const char *name, if (classify_mtype (MSYMBOL_TYPE (item->minsym)) != classification) break; - minsym_found (info->state, item->objfile, item->minsym, - &info->result); + VEC_safe_push (minsym_and_objfile_d, + info->result.minimal_symbols, item); } } @@ -2988,13 +3384,13 @@ add_matching_symbols_to_info (const char *name, int ix; struct symtab *elt; - for (ix = 0; VEC_iterate (symtab_p, info->state->file_symtabs, ix, elt); ++ix) + for (ix = 0; VEC_iterate (symtab_p, info->file_symtabs, ix, elt); ++ix) { struct symbol *sym; if (elt == NULL) { - iterate_over_all_matching_symtabs (name, VAR_DOMAIN, + iterate_over_all_matching_symtabs (info->state, name, VAR_DOMAIN, collect_symbols, info, pspace, 1); search_minsyms_for_name (info, name, pspace); @@ -3012,67 +3408,6 @@ add_matching_symbols_to_info (const char *name, } } -/* Decode a linespec that's a variable. If FILE_SYMTAB is non-NULL, - look in that symtab's static variables first. */ - -static struct symtabs_and_lines -decode_variable (struct linespec_state *self, char *copy) -{ - struct collect_info info; - const char *lookup_name; - char *canon; - struct cleanup *cleanup; - - info.state = self; - info.result.sals = NULL; - info.result.nelts = 0; - - cleanup = demangle_for_lookup (copy, current_language->la_language, - &lookup_name); - if (current_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 (copy); - make_cleanup (xfree, (void *) lookup_name); - } - - canon = cp_canonicalize_string_no_typedefs (lookup_name); - if (canon != NULL) - { - make_cleanup (xfree, canon); - lookup_name = canon; - } - - add_matching_symbols_to_info (lookup_name, &info, NULL); - - if (info.result.nelts > 0) - { - if (self->canonical) - { - self->canonical->pre_expanded = 1; - if (self->user_filename) - self->canonical->addr_string - = xstrprintf ("%s:%s", self->user_filename, copy); - else - self->canonical->addr_string = xstrdup (copy); - } - return info.result; - } - - if (!have_full_symbols () - && !have_partial_symbols () - && !have_minimal_symbols ()) - throw_error (NOT_FOUND_ERROR, - _("No symbol table is loaded. Use the \"file\" command.")); - if (self->user_filename) - throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined in \"%s\"."), - copy, self->user_filename); - else - throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined."), copy); -} - - /* Now come some functions that are called from multiple places within diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7fc5abe..8a7d4d8 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,23 @@ +2012-04-05 Keith Seitz <keiths@redhat.com> + + * gdb.base/advance.exp: Update error message for + "advance malformed" test. + * gdb.base/break.exp: Likewise for "breakpoint with + trailing garbage" test. + * gdb.base/hbreak2.exp: Likewise for "hardware breakpoint + with trailing garbage" test. + * gdb.base/sepdebug.exp: Likewise for "breakpoint with + trailng garbage" test. + * gdb.base/until.exp: Likewise for "malformed until" test. + * gdb.cp/ovldbreak.exp: Create the breakpoint table + for "breakpoint info (after setting on all)". + * gdb.cp/userdef.exp: Remove quoting for "break A2::operator+" + tests. + * gdb.cp/cplabel.cc: New file. + * gdb.cp/cplabel.exp: New test. + * gdb.linespec/ls-errs.c: New file. + * gdb.linespec/ls-errs.exp: New test. + 2012-04-03 Jan Kratochvil <jan.kratochvil@redhat.com> * gdb.trace/unavailable.exp diff --git a/gdb/testsuite/gdb.base/advance.exp b/gdb/testsuite/gdb.base/advance.exp index 456268d..617a7fb 100644 --- a/gdb/testsuite/gdb.base/advance.exp +++ b/gdb/testsuite/gdb.base/advance.exp @@ -45,7 +45,8 @@ gdb_test "advance [gdb_get_line_number "advance this location"]" \ # Verify that a malformed "advance" is gracefully caught. # gdb_test "advance [gdb_get_line_number "advance malformed"] then stop" \ - "Junk at end of arguments." "malformed advance" + "malformed linespec error: unexpected string, \"then stop\"" \ + "malformed advance" # Verify that "advance <funcname>" works. # diff --git a/gdb/testsuite/gdb.base/break.exp b/gdb/testsuite/gdb.base/break.exp index 71eb3a6..a203364 100644 --- a/gdb/testsuite/gdb.base/break.exp +++ b/gdb/testsuite/gdb.base/break.exp @@ -600,7 +600,7 @@ gdb_test "break $bp_location12 thread foo" \ # trailing garbage. # gdb_test "break $bp_location12 foo" \ - "Junk at end of arguments.*" \ + "malformed linespec error: unexpected string, \"foo\".*" \ "breakpoint with trailing garbage disallowed" # Verify that GDB responds gracefully to a "clear" command that has diff --git a/gdb/testsuite/gdb.base/hbreak2.exp b/gdb/testsuite/gdb.base/hbreak2.exp index 7f52cd3..a11d8b6 100644 --- a/gdb/testsuite/gdb.base/hbreak2.exp +++ b/gdb/testsuite/gdb.base/hbreak2.exp @@ -356,7 +356,7 @@ gdb_test "hbreak $bp_location12 thread foo" \ # trailing garbage. # gdb_test "hbreak $bp_location12 foo" \ - "Junk at end of arguments.*" \ + "malformed linespec error: unexpected string, \"foo\".*" \ "hardware breakpoint with trailing garbage disallowed" # Verify that GDB responds gracefully to a "clear" command that has diff --git a/gdb/testsuite/gdb.base/jump.exp b/gdb/testsuite/gdb.base/jump.exp index a5577e2..a7b2177 100644 --- a/gdb/testsuite/gdb.base/jump.exp +++ b/gdb/testsuite/gdb.base/jump.exp @@ -92,7 +92,7 @@ gdb_test "jump" "Argument required .starting address.*" \ # trailing junk. # gdb_test "jump 21 100" \ - "Junk at end of line specification: 100.*" \ + "malformed linespec error: unexpected number, \"100\"" \ "jump with trailing argument junk" diff --git a/gdb/testsuite/gdb.base/sepdebug.exp b/gdb/testsuite/gdb.base/sepdebug.exp index bd60c70..e6973fb 100644 --- a/gdb/testsuite/gdb.base/sepdebug.exp +++ b/gdb/testsuite/gdb.base/sepdebug.exp @@ -408,7 +408,7 @@ gdb_test "break $bp_location12 thread foo" \ # gdb_test "break $bp_location12 foo" \ - "Junk at end of arguments.*" \ + "malformed linespec error: unexpected string, \"foo\".*" \ "breakpoint with trailing garbage disallowed" # Verify that GDB responds gracefully to a "clear" command that has diff --git a/gdb/testsuite/gdb.base/until.exp b/gdb/testsuite/gdb.base/until.exp index 6b647cd..bdee21d 100644 --- a/gdb/testsuite/gdb.base/until.exp +++ b/gdb/testsuite/gdb.base/until.exp @@ -40,7 +40,8 @@ gdb_test "until $bp_location1" \ # Verify that a malformed "advance" is gracefully caught. # gdb_test "until 80 then stop" \ - "Junk at end of arguments." "malformed until" + "malformed linespec error: unexpected string, \"then stop\"." \ + "malformed until" # Rerun up to factorial, outer invocation if { ![runto factorial] } then { gdb_suppress_tests; } diff --git a/gdb/testsuite/gdb.cp/cplabel.cc b/gdb/testsuite/gdb.cp/cplabel.cc new file mode 100644 index 0000000..876f802 --- /dev/null +++ b/gdb/testsuite/gdb.cp/cplabel.cc @@ -0,0 +1,80 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +class foo +{ +public: + static int bar (void) + { + int i = 5; + bool first = true; + + to_the_top: /* bar:to_the_top */ + while (1) + { + if (i == 1) + { + if (first) + { + first = false; + goto to_the_top; + } + else + goto get_out_of_here; + } + + --i; + } + + get_out_of_here: /* bar:get_out_of_here */ + return i; + } + + int baz (int a) + { + int i = a; + bool first = true; + + to_the_top: /* baz:to_the_top */ + while (1) + { + if (i == 1) + { + if (first) + { + first = false; + goto to_the_top; + } + else + goto get_out_of_here; + } + + --i; + } + + get_out_of_here: /* baz:get_out_of_here */ + return i; + } +}; + +int +main (void) +{ + foo f; + return f.baz (foo::bar () + 3); +} + diff --git a/gdb/testsuite/gdb.cp/cplabel.exp b/gdb/testsuite/gdb.cp/cplabel.exp new file mode 100644 index 0000000..30dfa39 --- /dev/null +++ b/gdb/testsuite/gdb.cp/cplabel.exp @@ -0,0 +1,40 @@ +# Copyright 2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Tests for breakpoint on labels in methods. + +if {[skip_cplus_tests]} { continue } + +set testfile cplabel +set srcfile "$testfile.cc" +if {[prepare_for_testing "$testfile.exp" $testfile $srcfile {c++ debug}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +set methods {"bar" "baz"} +set labels {"to_the_top" "get_out_of_here"} + +foreach m $methods { + foreach l $labels { + set line [gdb_get_line_number "$m:$l"] + gdb_test "break foo::$m:$l" \ + "Breakpoint $decimal at $hex: file .*/$srcfile, line $line\." + } +} diff --git a/gdb/testsuite/gdb.cp/ovldbreak.exp b/gdb/testsuite/gdb.cp/ovldbreak.exp index 3e35b79..2c27f2d 100644 --- a/gdb/testsuite/gdb.cp/ovldbreak.exp +++ b/gdb/testsuite/gdb.cp/ovldbreak.exp @@ -320,23 +320,17 @@ gdb_expect { } } -gdb_test "info break" \ - "Num Type\[\t \]+Disp Enb Address\[\t \]+What.* -\[0-9\]+\[\t \]+breakpoint keep y\[\t \]+<MULTIPLE>\[\t \]*\r -\[0-9\]+.1\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(double\\) at.*$srcfile:140\r -\[0-9\]+.2\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(float\\) at.*$srcfile:137\r -\[0-9\]+.3\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((unsigned long|long unsigned)( int)?\\) at.*$srcfile:134\r -\[0-9\]+.4\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(long( int)?\\) at.*$srcfile:131\r -\[0-9\]+.5\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((unsigned|unsigned int)\\) at.*$srcfile:128\r -\[0-9\]+.6\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(int\\) at.*$srcfile:125\r -\[0-9\]+.7\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((unsigned short|short unsigned)( int)?\\) at.*$srcfile:122\r -\[0-9\]+.8\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(short( int)?\\) at.*$srcfile:119\r -\[0-9\]+.9\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(unsigned char\\) at.*$srcfile:116\r -\[0-9\]+.10\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(signed char\\) at.*$srcfile:113\r -\[0-9\]+.11\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(char\\) at.*$srcfile:110\r -\[0-9\]+.12\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((void|)\\) at.*$srcfile:107" \ - "breakpoint info (after setting on all)" +# Create the breakpoint table for "info breakpoint". +set bptable "Num Type\[\t \]+Disp Enb Address\[\t \]+What.*\[\r\n]+" +append bptable "\[0-9\]+\[\t \]+breakpoint\[\t \]+keep\[\t \]y\[\t \]+<MULTIPLE>.*\[\r\n\]+" +foreach ovld {void char signed_char unsigned_char short_int \ + unsigned_short_int int unsigned_int long_int \ + unsigned_long_int float double} { + append bptable [format "\[0-9\]+.\[0-9\]+\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(%s\\) at.*$srcfile:%d\[\r\n\]+" \ + $types($ovld) $line($ovld)] +} +gdb_test "info break" $bptable "breakpoint info (after setting on all)" # Run through each breakpoint. proc continue_to_bp_overloaded {bpnumber might_fail line argtype argument} { diff --git a/gdb/testsuite/gdb.cp/userdef.exp b/gdb/testsuite/gdb.cp/userdef.exp index 9c7cb57..90eb99d 100644 --- a/gdb/testsuite/gdb.cp/userdef.exp +++ b/gdb/testsuite/gdb.cp/userdef.exp @@ -136,8 +136,8 @@ gdb_test "print one += 7" "\\\$\[0-9\]* = {x = 9, y = 10}" gdb_test "print two = one" "\\\$\[0-9\]* = {x = 9, y = 10}" # Check that GDB tolerates whitespace in operator names. -gdb_test "break A2::'operator+'" ".*Breakpoint $decimal at.*" -gdb_test "break A2::'operator +'" ".*Breakpoint $decimal at.*" +gdb_test "break A2::operator+" ".*Breakpoint $decimal at.*" +gdb_test "break A2::operator +" ".*Breakpoint $decimal at.*" # Check that GDB handles operator* correctly. gdb_test "print c" "\\\$\[0-9\]* = {m = {z = .*}}" diff --git a/gdb/testsuite/gdb.linespec/ls-errs.c b/gdb/testsuite/gdb.linespec/ls-errs.c new file mode 100644 index 0000000..bb4c096 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/ls-errs.c @@ -0,0 +1,29 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +int myfunction (void) { return 0; } + +int +main (void) +{ + int a; + + a = myfunction (); + + here: + return a; +} diff --git a/gdb/testsuite/gdb.linespec/ls-errs.exp b/gdb/testsuite/gdb.linespec/ls-errs.exp new file mode 100644 index 0000000..21e01bf --- /dev/null +++ b/gdb/testsuite/gdb.linespec/ls-errs.exp @@ -0,0 +1,189 @@ +# Copyright 2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Tests for linespec error conditions + +set base ls-errs +set srcfile "$base.c" +set testfile "$base.exp" +set exefile $base + +if {[prepare_for_testing $testfile $exefile $srcfile \ + {debug nowarnings}]} { + return -1 +} + +# Turn off the pending breakpoint queries. +gdb_test_no_output "set breakpoint pending off" + +# We intentionally do not use gdb_breakpoint for these tests. + +# Add the (invalid) LINESPEC to the test array named in ARRAY_NAME. +# Use the index into ::error_messages MSG_ID and ARGS to create +# an error message which is the expect result of attempting to +# break on the given LINESPEC. +proc add {array_name linespec msg_id args} { + global error_messages + upvar $array_name tests + + lappend tests(linespecs) $linespec + set tests("$linespec") [string_to_regexp \ + [eval format \$error_messages($msg_id) $args]] +} + +# Common error message format strings. +array set error_messages { + invalid_file "No source file named %s." + invalid_function "Function \"%s\" not defined." + invalid_var_or_func "Undefined convenience variable or function \"%s\" not defined." + invalid_function_f "Function \"%s\" not defined in \"%s\"." + invalid_var_or_func_f \ + "Undefined convenience variable or function \"%s\" not defined in \"%s\"." + invalid_label "No label \"%s\" defined in function \"%s\"." + invalid_offset "No line %d in the current file." + invalid_offset_f "No line %d in file \"%s\"." + unexpected "malformed linespec error: unexpected %s" + unexpected_opt "malformed linespec error: unexpected %s, \"%s\"" + unmatched_quote "unmatched quote" +} + +# Some commonly used whitespace tests around ':'. +set spaces [list ":" ": " " :" " : " "\t: " " :\t" "\t:\t" " \t:\t " \ + "\t \t:\t \t \t"] + +# A list of invalid offsets. +set invalid_offsets [list -100 +500 1000] + +# THE_TESTS will hold all of our test information. Array index +# "linespecs" will contain the complete list of all linespecs +# to be tested. An array index of \"$linespec\" will contain +# the expected result. +set the_tests(linespecs) {} + +# Try some simple, invalid linespecs involving spaces. +foreach x $spaces { + add the_tests $x unexpected "colon" +} + +# Test invalid filespecs starting with offset. This is done +# first so that default offsets are tested. +foreach x $invalid_offsets { + set offset $x + + # Relative offsets are relative to line 16. Adjust + # expected offset from error message accordingly. + if {[string index $x 0] == "+" || + [string index $x 0] == "-"} { + incr offset 16 + } + add the_tests $x invalid_offset $offset +} + +# Test offsets with trailing tokens w/ and w/o spaces. +foreach x $spaces { + add the_tests "3$x" unexpected "colon" + add the_tests "+10$x" unexpected "colon" + add the_tests "-10$x" unexpected "colon" +} + +foreach x {1 +1 +100 -10} { + add the_tests "3 $x" unexpected_opt "number" $x + add the_tests "+10 $x" unexpected_opt "number" $x + add the_tests "-10 $x" unexpected_opt "number" $x +} + +add the_tests "3 foo" unexpected_opt "string" "foo" +add the_tests "+10 foo" unexpected_opt "string" "foo" +add the_tests "-10 foo" unexpected_opt "string" "foo" + +# Test invalid linespecs starting with filename. +foreach x [list "this_file_doesn't_exist.c" \ + "this file has spaces.c" \ + "\"file::colons.c\"" \ + "'file::colons.c'" \ + "\"this \"file\" has quotes.c\"" \ + "'this \"file\" has quotes.c'" \ + "'this 'file' has quotes.c'" \ + "\"this 'file' has quotes.c\""] { + # Remove any quoting from FILENAME for the error message. + add the_tests "$x:3" invalid_file [string trim $x \"'] +} + +# Test unmatched quotes. +foreach x {"\"src-file.c'" "'src-file.c"} { + add the_tests "$x:3" unmatched_quote +} + +add the_tests $srcfile invalid_function $srcfile +foreach x {"foo" " foo" " foo "} { + # Trim any leading/trailing whitespace for error messages. + add the_tests "$srcfile:$x" invalid_function_f [string trim $x] $srcfile + add the_tests "$srcfile:main:$x" invalid_label [string trim $x] "main" +} + +foreach x $spaces { + add the_tests "$srcfile$x" unexpected "end of input" + add the_tests "$srcfile:main$x" unexpected "end of input" +} + +add the_tests "${srcfile}::" invalid_function "${srcfile}::" +add the_tests "$srcfile:3 1" unexpected_opt "number" "1" +add the_tests "$srcfile:3 +100" unexpected_opt "number" "+100" +add the_tests "$srcfile:3 -100" unexpected_opt "number" "-100" +add the_tests "$srcfile:3 foo" unexpected_opt "string" "foo" + +foreach x $invalid_offsets { + add the_tests "$srcfile:$x" invalid_offset_f $x $srcfile +} + +# Test invalid filespecs starting with function. +foreach x {"foobar" "foo::bar" "foo.bar" "foo ." "foo bar" "foo 1" \ + "foo 0" "foo +10" "foo -10" "foo +100" "foo -100"} { + add the_tests $x invalid_function $x +} + +foreach x $spaces { + add the_tests "main${x}there" invalid_label "there" "main" + add the_tests "main:here${x}" unexpected "end of input" +} + +add the_tests "main 3" invalid_function "main 3" +add the_tests "main +100" invalid_function "main +100" +add the_tests "main -100" invalid_function "main -100" +add the_tests "main foo" invalid_function "main foo" + +foreach x {"3" "+100" "-100" "foo"} { + add the_tests "main:here $x" invalid_label "here $x" "main" +} + +foreach x {"if" "task" "thread"} { + add the_tests $x unexpected_opt "keyword" $x +} + +add the_tests "'main.c'flubber" unexpected_opt "string" "flubber" +add the_tests "'main.c',21" invalid_function "main.c" +add the_tests "'main.c' " invalid_function "main.c" +add the_tests "'main.c'3" unexpected_opt "number" "3" +add the_tests "'main.c'+3" unexpected_opt "number" "+3" + +# Test undefined convenience variables. +set x {$zippo} +add the_tests $x invalid_var_or_func $x +add the_tests "$srcfile:$x" invalid_var_or_func_f $x $srcfile + +# Run the tests +foreach linespec $the_tests(linespecs) { + gdb_test "break $linespec" $the_tests("$linespec") +} |