;; Fix comma/struct member parser collision
;; Tom Tromey and Leszek Swirski (RH BZ 18156900
commit b2f83c08372136fe9fe7d1df2feb5566c8b883fb
Author: Tom Tromey <tromey@redhat.com>
Date: Thu Mar 21 15:19:33 2013 +0000
PR exp/15109:
* c-exp.y (yylex): Rewrite to push all tokens onto the FIFO.
Handle FILENAME token.
gdb/testsuite
* gdb.cp/cpexprs.exp: Add test for FILENAME:: case.
* gdb.cp/misc.exp: Add test for FILENAME:: case.
commit 805e1f190887b3b7dea3fd157d58bc25effcf688
Author: Tom Tromey <tromey@redhat.com>
Date: Mon Nov 4 12:38:10 2013 -0700
fix PR c++/16117
This patch fixes PR c++/16117.
gdb has an extension so that users can use expressions like FILE::NAME
to choose a variable of the given name from the given file. The bug
is that this extension takes precedence over ordinary C++ expressions
of the same form. You might think this is merely hypothetical, but
now that C++ headers commonly do not use an extension, it is more
common.
This patch fixes the bug by making two related changes. First, it
changes gdb to prefer the ordinary C++ meaning of a symbol over the
extended meaning. Second, it arranges for single-quoting of the
symbol to indicate a preference for the extension.
Built and regtested on x86-64 Fedora 18.
New test case included.
2013-11-15 Tom Tromey <tromey@redhat.com>
PR c++/16117:
* c-exp.y (lex_one_token): Add "is_quoted_name" argument.
(classify_name): Likewise. Prefer a field of "this" over a
filename.
(classify_inner_name, yylex): Update.
2013-11-15 Tom Tromey <tromey@redhat.com>
* gdb.texinfo (Variables): Note gdb rules for ambiguous cases.
Add example.
2013-11-15 Tom Tromey <tromey@redhat.com>
* gdb.cp/includefile: New file.
* gdb.cp/filename.exp: New file.
* gdb.cp/filename.cc: New file.
commit 59498c305e6f1db2a1ed8d44cb58f0d24ec092fe
Author: Leszek Swirski <leszeks@google.com>
Date: Thu Jan 25 16:20:47 2018 +0000
Do not classify C struct members as a filename
There is existing logic in C/C++ expression parsing to avoid classifying
names as a filename when they are a field on the this object. This
change extends this logic to also avoid classifying names after a
struct-op (-> or .) as a filename, which otherwise causes a syntax
error.
Thus, it is now possible in the file
#include <map>
struct D {
void map();
}
D d;
to call
(gdb) print d.map()
where previously this would have been a syntax error.
Tested on gdb.cp/*.exp
gdb/ChangeLog:
* c-exp.y (lex_one_token, classify_name, yylex): Don't classify
names after a structop as a filename
gdb/testsuite/ChangeLog:
* gdb.cp/filename.cc, gdb.cp/filename.exp: Test that member
functions with the same name as an include file are parsed
correctly.
*** gdb-7.6.1-orig/gdb/c-exp.y 2020-04-21 16:59:20.636119459 -0400
--- gdb-7.6.1/gdb/c-exp.y 2020-04-22 16:41:04.560602687 -0400
*************** static int last_was_structop;
*** 2352,2358 ****
/* Read one token, getting characters through lexptr. */
static int
! lex_one_token (void)
{
int c;
int namelen;
--- 2352,2358 ----
/* Read one token, getting characters through lexptr. */
static int
! lex_one_token (int *is_quoted_name)
{
int c;
int namelen;
*************** lex_one_token (void)
*** 2362,2367 ****
--- 2362,2368 ----
char *copy;
last_was_structop = 0;
+ *is_quoted_name = 0;
retry:
*************** lex_one_token (void)
*** 2402,2408 ****
lexptr += 2;
yylval.opcode = tokentab2[i].opcode;
! if (parse_completion && tokentab2[i].token == ARROW)
last_was_structop = 1;
return tokentab2[i].token;
}
--- 2403,2409 ----
lexptr += 2;
yylval.opcode = tokentab2[i].opcode;
! if (tokentab2[i].token == ARROW)
last_was_structop = 1;
return tokentab2[i].token;
}
*************** lex_one_token (void)
*** 2426,2432 ****
saw_name_at_eof = 0;
return COMPLETE;
}
! else if (saw_structop)
return COMPLETE;
else
return 0;
--- 2427,2433 ----
saw_name_at_eof = 0;
return COMPLETE;
}
! else if (parse_completion && saw_structop)
return COMPLETE;
else
return 0;
*************** lex_one_token (void)
*** 2465,2472 ****
/* Might be a floating point number. */
if (lexptr[1] < '0' || lexptr[1] > '9')
{
! if (parse_completion)
! last_was_structop = 1;
goto symbol; /* Nope, must be a symbol. */
}
/* FALL THRU into number case. */
--- 2466,2472 ----
/* Might be a floating point number. */
if (lexptr[1] < '0' || lexptr[1] > '9')
{
! last_was_structop = 1;
goto symbol; /* Nope, must be a symbol. */
}
/* FALL THRU into number case. */
*************** lex_one_token (void)
*** 2606,2611 ****
--- 2606,2613 ----
{
++tokstart;
namelen = lexptr - tokstart - 1;
+ *is_quoted_name = 1;
+
goto tryname;
}
else if (host_len > 1)
*************** static struct obstack name_obstack;
*** 2748,2757 ****
/* Classify a NAME token. The contents of the token are in `yylval'.
Updates yylval and returns the new token type. BLOCK is the block
! in which lookups start; this can be NULL to mean the global
! scope. */
static int
! classify_name (const struct block *block)
{
struct symbol *sym;
char *copy;
--- 2750,2763 ----
/* Classify a NAME token. The contents of the token are in `yylval'.
Updates yylval and returns the new token type. BLOCK is the block
! in which lookups start; this can be NULL to mean the global scope.
! IS_QUOTED_NAME is non-zero if the name token was originally quoted
! in single quotes. IS_AFTER_STRUCTOP is true if this name follows
! a structure operator -- either '.' or ARROW. */
!
static int
! classify_name (const struct block *block, int is_quoted_name,
! int is_after_structop)
{
struct symbol *sym;
char *copy;
*************** classify_name (const struct block *block
*** 2775,2790 ****
}
else if (!sym)
{
- /* See if it's a file name. */
- struct symtab *symtab;
-
- symtab = lookup_symtab (copy);
- if (symtab)
- {
- yylval.bval = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
- return FILENAME;
- }
-
/* If we found a field of 'this', we might have erroneously
found a constructor where we wanted a type name. Handle this
case by noticing that we found a constructor and then look up
--- 2781,2786 ----
*************** classify_name (const struct block *block
*** 2804,2809 ****
--- 2800,2825 ----
return TYPENAME;
}
}
+
+ /* If we found a field on the "this" object, or we are looking
+ up a field on a struct, then we want to prefer it over a
+ filename. However, if the name was quoted, then it is better
+ to check for a filename or a block, since this is the only
+ way the user has of requiring the extension to be used. */
+ if ((is_a_field_of_this.type == NULL && !is_after_structop)
+ || is_quoted_name)
+ {
+ /* See if it's a file name. */
+ struct symtab *symtab;
+
+ symtab = lookup_symtab (copy);
+ if (symtab)
+ {
+ yylval.bval = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab),
+ STATIC_BLOCK);
+ return FILENAME;
+ }
+ }
}
if (sym && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
*************** classify_inner_name (const struct block
*** 2873,2879 ****
char *copy;
if (context == NULL)
! return classify_name (block);
type = check_typedef (context);
if (TYPE_CODE (type) != TYPE_CODE_STRUCT
--- 2889,2895 ----
char *copy;
if (context == NULL)
! return classify_name (block, 0, 0);
type = check_typedef (context);
if (TYPE_CODE (type) != TYPE_CODE_STRUCT
*************** static int
*** 2941,2998 ****
yylex (void)
{
token_and_value current;
! int first_was_coloncolon, last_was_coloncolon, first_iter;
struct type *context_type = NULL;
if (popping && !VEC_empty (token_and_value, token_fifo))
! {
! token_and_value tv = *VEC_index (token_and_value, token_fifo, 0);
! VEC_ordered_remove (token_and_value, token_fifo, 0);
! yylval = tv.value;
! return tv.token;
! }
popping = 0;
! current.token = lex_one_token ();
if (current.token == NAME)
! current.token = classify_name (expression_context_block);
if (parse_language->la_language != language_cplus
! || (current.token != TYPENAME && current.token != COLONCOLON))
return current.token;
! first_was_coloncolon = current.token == COLONCOLON;
! last_was_coloncolon = first_was_coloncolon;
! obstack_free (&name_obstack, obstack_base (&name_obstack));
! if (!last_was_coloncolon)
! {
! obstack_grow (&name_obstack, yylval.sval.ptr, yylval.sval.length);
! context_type = yylval.tsym.type;
! }
current.value = yylval;
! first_iter = 1;
while (1)
{
! token_and_value next;
! next.token = lex_one_token ();
! next.value = yylval;
! if (next.token == NAME && last_was_coloncolon)
{
int classification;
! classification = classify_inner_name (first_was_coloncolon
! ? NULL
! : expression_context_block,
! context_type);
/* We keep going until we either run out of names, or until
we have a qualified name which is not a type. */
if (classification != TYPENAME && classification != NAME)
! {
! /* Push the final component and leave the loop. */
! VEC_safe_push (token_and_value, token_fifo, &next);
! break;
! }
/* Update the partial name we are constructing. */
if (context_type != NULL)
--- 2957,3055 ----
yylex (void)
{
token_and_value current;
! int first_was_coloncolon, last_was_coloncolon;
struct type *context_type = NULL;
+ int last_to_examine, next_to_examine, checkpoint;
+ const struct block *search_block;
+ int is_quoted_name, last_lex_was_structop;
if (popping && !VEC_empty (token_and_value, token_fifo))
! goto do_pop;
popping = 0;
! last_lex_was_structop = last_was_structop;
!
! /* Read the first token and decide what to do. Most of the
! subsequent code is C++-only; but also depends on seeing a "::" or
! name-like token. */
! current.token = lex_one_token (&is_quoted_name);
if (current.token == NAME)
! current.token = classify_name (expression_context_block, is_quoted_name,
! last_lex_was_structop);
if (parse_language->la_language != language_cplus
! || (current.token != TYPENAME && current.token != COLONCOLON
! && current.token != FILENAME))
return current.token;
! /* Read any sequence of alternating "::" and name-like tokens into
! the token FIFO. */
current.value = yylval;
! VEC_safe_push (token_and_value, token_fifo, ¤t);
! last_was_coloncolon = current.token == COLONCOLON;
while (1)
{
! int ignore;
!
! /* We ignore quoted names other than the very first one.
! Subsequent ones do not have any special meaning. */
! current.token = lex_one_token (&ignore);
! current.value = yylval;
! VEC_safe_push (token_and_value, token_fifo, ¤t);
! if ((last_was_coloncolon && current.token != NAME)
! || (!last_was_coloncolon && current.token != COLONCOLON))
! break;
! last_was_coloncolon = !last_was_coloncolon;
! }
! popping = 1;
! /* We always read one extra token, so compute the number of tokens
! to examine accordingly. */
! last_to_examine = VEC_length (token_and_value, token_fifo) - 2;
! next_to_examine = 0;
!
! current = *VEC_index (token_and_value, token_fifo, next_to_examine);
! ++next_to_examine;
!
! obstack_free (&name_obstack, obstack_base (&name_obstack));
! checkpoint = 0;
! if (current.token == FILENAME)
! search_block = current.value.bval;
! else if (current.token == COLONCOLON)
! search_block = NULL;
! else
! {
! gdb_assert (current.token == TYPENAME);
! search_block = expression_context_block;
! obstack_grow (&name_obstack, current.value.sval.ptr,
! current.value.sval.length);
! context_type = current.value.tsym.type;
! checkpoint = 1;
! }
!
! first_was_coloncolon = current.token == COLONCOLON;
! last_was_coloncolon = first_was_coloncolon;
!
! while (next_to_examine <= last_to_examine)
! {
! token_and_value *next;
!
! next = VEC_index (token_and_value, token_fifo, next_to_examine);
! ++next_to_examine;
!
! if (next->token == NAME && last_was_coloncolon)
{
int classification;
! yylval = next->value;
! classification = classify_inner_name (search_block, context_type);
/* We keep going until we either run out of names, or until
we have a qualified name which is not a type. */
if (classification != TYPENAME && classification != NAME)
! break;
!
! /* Accept up to this token. */
! checkpoint = next_to_examine;
/* Update the partial name we are constructing. */
if (context_type != NULL)
*************** yylex (void)
*** 3000,3007 ****
/* We don't want to put a leading "::" into the name. */
obstack_grow_str (&name_obstack, "::");
}
! obstack_grow (&name_obstack, next.value.sval.ptr,
! next.value.sval.length);
yylval.sval.ptr = obstack_base (&name_obstack);
yylval.sval.length = obstack_object_size (&name_obstack);
--- 3057,3064 ----
/* We don't want to put a leading "::" into the name. */
obstack_grow_str (&name_obstack, "::");
}
! obstack_grow (&name_obstack, next->value.sval.ptr,
! next->value.sval.length);
yylval.sval.ptr = obstack_base (&name_obstack);
yylval.sval.length = obstack_object_size (&name_obstack);
*************** yylex (void)
*** 3015,3052 ****
context_type = yylval.tsym.type;
}
! else if (next.token == COLONCOLON && !last_was_coloncolon)
last_was_coloncolon = 1;
else
{
/* We've reached the end of the name. */
- VEC_safe_push (token_and_value, token_fifo, &next);
break;
}
-
- first_iter = 0;
- }
-
- popping = 1;
-
- /* If we ended with a "::", insert it too. */
- if (last_was_coloncolon)
- {
- token_and_value cc;
- memset (&cc, 0, sizeof (token_and_value));
- if (first_was_coloncolon && first_iter)
- {
- yylval = cc.value;
- return COLONCOLON;
- }
- cc.token = COLONCOLON;
- VEC_safe_insert (token_and_value, token_fifo, 0, &cc);
}
yylval = current.value;
- yylval.sval.ptr = obstack_copy0 (&expansion_obstack,
- yylval.sval.ptr,
- yylval.sval.length);
return current.token;
}
--- 3072,3103 ----
context_type = yylval.tsym.type;
}
! else if (next->token == COLONCOLON && !last_was_coloncolon)
last_was_coloncolon = 1;
else
{
/* We've reached the end of the name. */
break;
}
}
+ /* If we have a replacement token, install it as the first token in
+ the FIFO, and delete the other constituent tokens. */
+ if (checkpoint > 0)
+ {
+ current.value.sval.ptr = obstack_copy0 (&expansion_obstack,
+ current.value.sval.ptr,
+ current.value.sval.length);
+
+ VEC_replace (token_and_value, token_fifo, 0, ¤t);
+ if (checkpoint > 1)
+ VEC_block_remove (token_and_value, token_fifo, 1, checkpoint - 1);
+ }
+
+ do_pop:
+ current = *VEC_index (token_and_value, token_fifo, 0);
+ VEC_ordered_remove (token_and_value, token_fifo, 0);
yylval = current.value;
return current.token;
}
*** gdb-7.6.1-orig/gdb/doc/gdb.texinfo 2020-04-21 16:59:20.581119544 -0400
--- gdb-7.6.1/gdb/doc/gdb.texinfo 2020-04-22 16:26:14.350165928 -0400
*************** $4 = 0
*** 8219,8229 ****
@end smallexample
@cindex C@t{++} scope resolution
! These uses of @samp{::} are very rarely in conflict with the very similar
! use of the same notation in C@t{++}. @value{GDBN} also supports use of the C@t{++}
! scope resolution operator in @value{GDBN} expressions.
! @c FIXME: Um, so what happens in one of those rare cases where it's in
! @c conflict?? --mew
@cindex wrong values
@cindex variable values, wrong
--- 8219,8242 ----
@end smallexample
@cindex C@t{++} scope resolution
! These uses of @samp{::} are very rarely in conflict with the very
! similar use of the same notation in C@t{++}. When they are in
! conflict, the C@t{++} meaning takes precedence; however, this can be
! overridden by quoting the file or function name with single quotes.
!
! For example, suppose the program is stopped in a method of a class
! that has a field named @code{includefile}, and there is also an
! include file named @file{includefile} that defines a variable,
! @code{some_global}.
!
! @smallexample
! (@value{GDBP}) p includefile
! $1 = 23
! (@value{GDBP}) p includefile::some_global
! A syntax error in expression, near `'.
! (@value{GDBP}) p 'includefile'::some_global
! $2 = 27
! @end smallexample
@cindex wrong values
@cindex variable values, wrong
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/cpexprs.exp 2020-04-21 16:59:20.413119804 -0400
--- gdb-7.6.1/gdb/testsuite/gdb.cp/cpexprs.exp 2020-04-22 16:18:30.938538563 -0400
*************** gdb_test "p CV_f(int)" { = {int \(int\
*** 731,735 ****
--- 731,738 ----
gdb_test "p CV_f(CV::t)" { = {int \(int\)} 0x[0-9a-f]+ <CV_f\(int\)>}
gdb_test "p CV_f(CV::i)" " = 43"
+ gdb_test "p CV_f('cpexprs.cc'::CV::t)" \
+ { = {int \(int\)} 0x[0-9a-f]+ <CV_f\(int\)>}
+
gdb_exit
return 0
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/misc.exp 2020-04-21 16:59:20.411119807 -0400
--- gdb-7.6.1/gdb/testsuite/gdb.cp/misc.exp 2020-04-22 16:18:30.939538558 -0400
*************** gdb_test "print (bool)17.93" "\\$\[0-9\]
*** 107,109 ****
--- 107,112 ----
gdb_test "print (bool)0.0" "\\$\[0-9\]* = false" "(bool)0.0"
gdb_test "print (int)true" "\\$\[0-9\]* = 1" "(int)true"
gdb_test "print (int)false" "\\$\[0-9\]* = 0" "(int)false"
+
+ gdb_test "print 'misc.cc'::v_bool" " = true" \
+ "expression using block qualifier"
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/filename.cc 1969-12-31 19:00:00.000000000 -0500
--- gdb-7.6.1/gdb/testsuite/gdb.cp/filename.cc 2020-04-22 16:54:00.973626918 -0400
***************
*** 0 ****
--- 1,57 ----
+ /* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 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/>. */
+
+ #include "includefile"
+
+ class C {
+ public:
+ int includefile[1];
+
+ C() {
+ includefile[0] = 23;
+ }
+
+ void m() {
+ /* stop inside C */
+ }
+ };
+
+ class D {
+ public:
+ int includefile();
+
+ void m() {
+ /* stop inside D */
+ }
+ };
+
+ int D::includefile() {
+ return 24;
+ }
+
+ int main() {
+ C c;
+ C* pc = &c;
+ c.m();
+
+ D d;
+ D* pd = &d;
+ d.m();
+
+ /* stop outside */
+ return 0;
+ }
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/filename.exp 1969-12-31 19:00:00.000000000 -0500
--- gdb-7.6.1/gdb/testsuite/gdb.cp/filename.exp 2020-04-22 16:34:25.685647330 -0400
***************
*** 0 ****
--- 1,49 ----
+ # Copyright 2013 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/>.
+
+ if { [skip_cplus_tests] } { continue }
+
+ standard_testfile .cc
+
+ if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+ return -1
+ }
+
+ if ![runto_main] then {
+ perror "couldn't run to main"
+ continue
+ }
+
+ gdb_breakpoint [gdb_get_line_number "stop inside C"]
+ gdb_continue_to_breakpoint "stop inside C"
+
+ gdb_test "print includefile\[0\]" " = 23"
+ gdb_test "print this->includefile\[0\]" " = 23"
+ gdb_test "print 'includefile'::some_global" " = 27"
+
+ gdb_breakpoint [gdb_get_line_number "stop inside D"]
+ gdb_continue_to_breakpoint "stop inside D"
+
+ gdb_test "print includefile()" " = 24"
+ gdb_test "print this->includefile()" " = 24"
+ gdb_test "print 'includefile'::some_global" " = 27"
+
+ gdb_breakpoint [gdb_get_line_number "stop outside"]
+ gdb_continue_to_breakpoint "stop outside"
+
+ gdb_test "print c.includefile\[0\]" " = 23"
+ gdb_test "print pc->includefile\[0\]" " = 23"
+ gdb_test "print d.includefile()" " = 24"
+ gdb_test "print pd->includefile()" " = 24"
*** gdb-7.6.1-orig/gdb/testsuite/gdb.cp/includefile 1969-12-31 19:00:00.000000000 -0500
--- gdb-7.6.1/gdb/testsuite/gdb.cp/includefile 2020-04-22 16:26:14.352165918 -0400
***************
*** 0 ****
--- 1,18 ----
+ /* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 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 some_global = 27;