;; Fix comma/struct member parser collision ;; Tom Tromey and Leszek Swirski (RH BZ 18156900 commit b2f83c08372136fe9fe7d1df2feb5566c8b883fb Author: Tom Tromey 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 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 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 * gdb.texinfo (Variables): Note gdb rules for ambiguous cases. Add example. 2013-11-15 Tom Tromey * gdb.cp/includefile: New file. * gdb.cp/filename.exp: New file. * gdb.cp/filename.cc: New file. commit 59498c305e6f1db2a1ed8d44cb58f0d24ec092fe Author: Leszek Swirski 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 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]+ } gdb_test "p CV_f(CV::i)" " = 43" + gdb_test "p CV_f('cpexprs.cc'::CV::t)" \ + { = {int \(int\)} 0x[0-9a-f]+ } + 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 . */ + + #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 . + + 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 . */ + + int some_global = 27;