diff options
-rw-r--r-- | gdb/breakpoint.c | 3 | ||||
-rw-r--r-- | gdb/linespec.c | 55 | ||||
-rw-r--r-- | gdb/linespec.h | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.linespec/keywords.c | 36 | ||||
-rw-r--r-- | gdb/testsuite/gdb.linespec/keywords.exp | 77 |
5 files changed, 153 insertions, 22 deletions
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 13c8949..8a252bf 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -9335,8 +9335,7 @@ parse_breakpoint_sals (char **address, { /* If no arg given, or if first arg is 'if ', use the default breakpoint. */ - if ((*address) == NULL - || (startswith ((*address), "if") && isspace ((*address)[2]))) + if ((*address) == NULL || linespec_lexer_lex_keyword (*address)) { /* The last displayed codepoint, if it's valid, is our default breakpoint address. */ diff --git a/gdb/linespec.c b/gdb/linespec.c index 9ec4a5e..90c07a2 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -246,6 +246,7 @@ typedef enum ls_token_type linespec_token_type; /* List of keywords */ static const char * const linespec_keywords[] = { "if", "thread", "task" }; +#define IF_KEYWORD_INDEX 0 /* A token of the linespec lexer */ @@ -290,11 +291,6 @@ struct ls_parser /* Is the entire linespec quote-enclosed? */ int is_quote_enclosed; - /* Is a keyword syntactically valid at this point? - In, e.g., "break thread thread 1", the leading "keyword" must not - be interpreted as such. */ - int keyword_ok; - /* The state of the parse. */ struct linespec_state state; #define PARSER_STATE(PPTR) (&(PPTR)->state) @@ -421,7 +417,7 @@ linespec_lexer_lex_number (linespec_parser *parser, linespec_token *tokenp) /* Does P represent one of the keywords? If so, return the keyword. If not, return NULL. */ -static const char * +const char * linespec_lexer_lex_keyword (const char *p) { int i; @@ -433,11 +429,34 @@ linespec_lexer_lex_keyword (const char *p) 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. */ + character is whitespace, we may have found a keyword. + It is only a keyword if it is not followed by another + keyword. */ if (strncmp (p, linespec_keywords[i], len) == 0 - && !(isalnum (p[len]) || p[len] == '_')) - return linespec_keywords[i]; + && isspace (p[len])) + { + int j; + + /* Special case: "if" ALWAYS stops the lexer, since it + is not possible to predict what is going to appear in + the condition, which can only be parsed after SaLs have + been found. */ + if (i != IF_KEYWORD_INDEX) + { + p += len; + p = skip_spaces_const (p); + for (j = 0; j < ARRAY_SIZE (linespec_keywords); ++j) + { + int nextlen = strlen (linespec_keywords[j]); + + if (strncmp (p, linespec_keywords[j], nextlen) == 0 + && isspace (p[nextlen])) + return NULL; + } + } + + return linespec_keywords[i]; + } } } @@ -734,13 +753,16 @@ linespec_lexer_lex_one (linespec_parser *parser) PARSER_STREAM (parser) = skip_spaces_const (PARSER_STREAM (parser)); /* Check for a keyword, they end the linespec. */ - keyword = NULL; - if (parser->keyword_ok) - keyword = linespec_lexer_lex_keyword (PARSER_STREAM (parser)); + keyword = linespec_lexer_lex_keyword (PARSER_STREAM (parser)); if (keyword != NULL) { parser->lexer.current.type = LSTOKEN_KEYWORD; LS_TOKEN_KEYWORD (parser->lexer.current) = keyword; + /* We do not advance the stream here intentionally: + we would like lexing to stop when a keyword is seen. + + PARSER_STREAM (parser) += strlen (keyword); */ + return parser->lexer.current; } @@ -2175,10 +2197,6 @@ parse_linespec (linespec_parser *parser, const char **argptr) } } - /* A keyword at the start cannot be interpreted as such. - Consider "b thread thread 42". */ - parser->keyword_ok = 0; - parser->lexer.saved_arg = *argptr; parser->lexer.stream = argptr; @@ -2250,9 +2268,6 @@ parse_linespec (linespec_parser *parser, const char **argptr) else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER) unexpected_linespec_error (parser); - /* Now we can recognize keywords. */ - parser->keyword_ok = 1; - /* Shortcut: If the next token is not LSTOKEN_COLON, we know that this token cannot represent a filename. */ token = linespec_lexer_peek_token (parser); diff --git a/gdb/linespec.h b/gdb/linespec.h index 5520afa..7e66024 100644 --- a/gdb/linespec.h +++ b/gdb/linespec.h @@ -152,4 +152,8 @@ extern struct symtabs_and_lines decode_line_with_current_source (char *, int); extern struct symtabs_and_lines decode_line_with_last_displayed (char *, int); +/* Does P represent one of the keywords? If so, return + the keyword. If not, return NULL. */ + +extern const char *linespec_lexer_lex_keyword (const char *p); #endif /* defined (LINESPEC_H) */ diff --git a/gdb/testsuite/gdb.linespec/keywords.c b/gdb/testsuite/gdb.linespec/keywords.c new file mode 100644 index 0000000..de2db63 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/keywords.c @@ -0,0 +1,36 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2015 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/>. */ + +static int +task (int task) +{ + return task - 1; +} + +static int +thread (int thread) +{ + return task (thread) + 1; +} + +int +main (void) +{ + int x = 0; + x += thread (0); + return x; +} diff --git a/gdb/testsuite/gdb.linespec/keywords.exp b/gdb/testsuite/gdb.linespec/keywords.exp new file mode 100644 index 0000000..fcb745f --- /dev/null +++ b/gdb/testsuite/gdb.linespec/keywords.exp @@ -0,0 +1,77 @@ +# Copyright 2015 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/>. + +# Test keyword parsing in the linespec parser. + +standard_testfile +set exefile $testfile + +if {[prepare_for_testing $testfile $exefile $srcfile {debug}]} { + return -1 +} + +if ![runto_main] { + fail "Can't run to main" + return 0 +} + +# Turn off pending breakpoints to facilitate testing errors. +gdb_test_no_output "set breakpoint pending off" + +# The linespec lexer ignores the language setting when lexing +# keywords. +gdb_test "break if" "Function \"if\" not defined." +gdb_breakpoint "thread" "message" +gdb_breakpoint "task" "message" + +# The lexer should prune any trailing whitesapce, so the expected +# outcome of the following tests should be the same as the previous +# tests. +with_test_prefix "trailing whitespace" { + gdb_test "break if " "Function \"if\" not defined." + gdb_breakpoint "thread " "message" + gdb_breakpoint "task " "message" +} + +# With a single keyword specified first in the location, +# we assume we have a NULL location, i.e., the actual location +# of the event is the current default location. +# +# break if XX --> okay if XX is a valid expression +# (the lexer cannot know whether the expression is valid or not) +# break {thread,task} NUMBER --> invalid thread/task +# break {thread,task} STUFF --> "junk" after keyword (STUFF is not numeric) +gdb_test "break thread 123" "Unknown thread 123\\." +gdb_test "break thread foo" "Junk after thread keyword\\." +gdb_test "break task 123" "Unknown task 123\\." +gdb_test "break task foo" "Junk after task keyword\\." +gdb_breakpoint "thread if 0" "message" + +# These are also NULL locations, but using a subsequent keyword +# as the "junk". +gdb_test "break thread thread" "Junk after thread keyword\\." +gdb_test "break thread task" "Junk after thread keyword\\." +gdb_test "break thread if" "Junk after thread keyword\\." +gdb_test "break task task" "Junk after task keyword\\." +gdb_test "break task thread" "Junk after task keyword\\." +gdb_test "break task if" "Junk after task keyword\\." + +# Test locations containing keyword followed by keyword. +gdb_test "break thread thread 123" "Unknown thread 123\\." +gdb_test "break task task 123" "Unknown task 123\\." + +# Test NULL location with valid conditional containing a keyword. +gdb_breakpoint "thread if thread == 0" +gdb_breakpoint "task if task == 0" |