[binutils-gdb] gdb: add annotation in 'info locals' command for variables shadowing case
Abdul Basit Ijaz via Gdb-cvs <[email protected]> Fri, 17 Jul 2026 17:39:31 +0000 (GMT)
| Newsgroups | gmane.comp.gdb.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3D2465b9e41315= 14bdb42365076ef65ef6ad0e3f6c commit 2465b9e4131514bdb42365076ef65ef6ad0e3f6c Author: Ijaz, Abdul B <[email protected]> Date: Fri Sep 15 15:12:10 2023 +0200 gdb: add annotation in 'info locals' command for variables shadowing ca= se =20 For C/C++/Fortran/Ada languages GDB prints same name variable multiple times in case of variable shadowing and it is confusing for user to ide= ntify which variable belongs to the current scope. So for such cases add loc= ation info to the innermost listed variables and for super block variables add "shadowed" annotation in the form of "<file.c:line, shadowed>". =20 Suppose we have =20 1:int x =3D 3; 2: { 3: int x =3D 4; 4: int y =3D 52; 5: x =3D 99; /* break here */ 6: } =20 Currently: =20 (gdb) info locals x =3D 4 y =3D 52 x =3D 3 =20 After applying this patch, we obtain: =20 (gdb) info locals x =3D 4 <file.c:3> y =3D 52 x =3D 3 <file.c:1, shadowed> =20 The patch adds the location annotations by keeping track of inner block and already printed variables to identify shadowing. So, GDB now prints "<file.c:line, shadowed>" for shadowed super-block variables and "<file.c:line>" for innermost declarations of such variables only. =20 The location annotations are printed for shadowed variables in case of C/C++/Fortran/Ada languages. In Rust, it is possible to declare a variable with the same name many times. So in this case, just the first instance of the variable is printed. RUST language test "var_reuse.exp" fails with rustc compiler version >=3D 1.73 so XFAIL is added according= ly. =20 Fix regex expression in the gdb.opt/inline-locals.exp test according to this change. The test update is only required due to the existing gdb known ticket gdb/25695 where this issue is seen with 7.5.0 version on sles15sp6 but it is not seen anymore on the newer gcc versions e.g. gcc-11.4.0. =20 The symtab()/filename() nullptr check was added specifically to avoid the crash seen in gdb.dwarf2/missing-type-name-for-templates.exp where template symbols may have no associated source file. =20 Reviewed-By: Guinevere Larsen <[email protected]> Reviewed-By: Eli Zaretskii <[email protected]> Co-Authored-By: Andrew Burgess <[email protected]> Approved-By: Andrew Burgess <[email protected]> Diff: --- gdb/NEWS | 4 + gdb/c-typeprint.c | 3 +- gdb/doc/gdb.texinfo | 26 +++++ gdb/language.c | 20 ++++ gdb/language.h | 18 ++++ gdb/printcmd.c | 44 +++++++- gdb/stack.c | 115 +++++++++++++++++= ++-- gdb/stack.h | 22 ++++ gdb/testsuite/gdb.ada/var_shadowing.exp | 39 +++++++ .../gdb.ada/var_shadowing/var_shadowing.adb | 30 ++++++ gdb/testsuite/gdb.base/var-shadowing.c | 51 +++++++++ gdb/testsuite/gdb.base/var-shadowing.exp | 92 +++++++++++++++++ gdb/testsuite/gdb.base/var-shadowing2.c | 16 +++ gdb/testsuite/gdb.opt/inline-locals.exp | 21 ++-- gdb/testsuite/gdb.rust/var_reuse.exp | 36 +++++++ gdb/testsuite/gdb.rust/var_reuse.rs | 20 ++++ gdb/value.h | 26 ++++- 17 files changed, 560 insertions(+), 23 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index ec9b5a33787..5410acc8303 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -201,6 +201,10 @@ New command class for help commands that we, as developers, believe would be close to a minimal set of commands for a new user of GDB. =20 +info locals + GDB now shows the "shadowed" annotation and the location information + for variables that are shadowed, or which are shadowing. + * Removed commands =20 target ctf diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index aaace85aa29..1168464a9f9 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -818,7 +818,8 @@ c_type_print_template_args (const struct type_print_opt= ions *flags, if (sym->loc_class () =3D=3D LOC_TYPEDEF) c_print_type (sym->type (), "", stream, -1, 0, language, flags); else - print_variable_value (sym, {}, stream, 0, language_def (language)); + print_variable_value (sym, {}, stream, 0, language_def (language), + var_shadowing::NONE); } =20 gdb_puts (_("] "), stream); diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 04d761c904c..d2fbe61d8fc 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -9188,6 +9188,32 @@ The optional flag @samp{-q}, which stands for @samp{= quiet}, disables printing header information and messages explaining why no local variables have been printed. =20 +@smallexample +@group +1: int x =3D 3; +2: @{ +3: int x =3D 4; +4: int y =3D 52; +5: x =3D 99; // breakpoint-line +6: @} +@end group +@group +(gdb) info locals +x =3D 4 <file.c:3> +y =3D 52 +x =3D 3 <file.c:1, shadowed> +@end group +@end smallexample + +@anchor{shadowed variables} +@cindex shadowed variables +A variable is @dfn{shadowed} when there's another variable with the +same name which is declared within an inner scope (decision block, +method, or inner class). When shadowing is detected, location +information is added to all instances of the shadowed variable name. +The outermost instances are additionally followed by @samp{shadowed} +to indicate that they are not the active variable. + @item info locals [-q] [-t @var{type_regexp}] [@var{regexp}] Like @kbd{info locals}, but only print the local variables selected with the provided regexp(s). diff --git a/gdb/language.c b/gdb/language.c index 3e3be66a676..972018e8f7b 100644 --- a/gdb/language.c +++ b/gdb/language.c @@ -1090,6 +1090,26 @@ language_lookup_primitive_type_as_symbol (const stru= ct language_defn *la, return sym; } =20 +/* See language.h. */ + +lang_vars_shadowing get_lang_vars_shadowing_option (enum language lang) +{ + switch (lang) + { + case language_c: + case language_cplus: + case language_fortran: + case language_ada: + return lang_vars_shadowing::PRINT; + + case language_rust: + return lang_vars_shadowing::HIDE; + + default: + return lang_vars_shadowing::NONE; + } +} + /* Initialize the language routines. */ =20 INIT_GDB_FILE (language) diff --git a/gdb/language.h b/gdb/language.h index 62827ffe02c..fb070edaa54 100644 --- a/gdb/language.h +++ b/gdb/language.h @@ -83,6 +83,20 @@ enum macro_expansion macro_expansion_no, macro_expansion_c }; =20 +/* How should shadowed, or shadowing, variables be printed. */ + +enum class lang_vars_shadowing + { + /* Adds shadowed information for such variables. */ + PRINT, + + /* Does not print shadowed variables. */ + HIDE, + + /* Print variables without shadow information. */ + NONE, + }; + =0C /* Per architecture (OS/ABI) language information. */ =20 @@ -801,6 +815,10 @@ void c_get_string (struct value *value, symbol_name_matcher_ftype *get_symbol_name_matcher (const language_defn *lang, const lookup_name_info &lookup_name); =20 +/* Returns the shadowing option supported for the input language. */ + +extern lang_vars_shadowing get_lang_vars_shadowing_option (enum language l= ang); + /* Save the current language and restore it upon destruction. */ =20 class scoped_restore_current_language diff --git a/gdb/printcmd.c b/gdb/printcmd.c index 662f43fbef8..15c086a9284 100644 --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -2377,7 +2377,8 @@ clear_dangling_display_expressions (struct objfile *o= bjfile) void print_variable_value (symbol *var, const frame_info_ptr &frame, ui_file *stream, int indent, - const language_defn *language) + const language_defn *language, + var_shadowing shadow_status) { try { @@ -2392,6 +2393,41 @@ print_variable_value (symbol *var, const frame_info_= ptr &frame, get_user_print_options (&opts); opts.deref_ref =3D true; common_val_print_checked (val, stream, indent, &opts, language); + + /* Print <%line, shadowed> after the variable value only when it is + variable shadowing case. */ + if (shadow_status !=3D var_shadowing::NONE) + { + /* Use lbasename instead of symtab_to_filename_for_display as the + latter defaults to relative path while basename is preferred + here. */ + const char *file_name =3D (var->symtab () !=3D nullptr + && var->symtab ()->filename () !=3D nullptr) + ? lbasename (var->symtab ()->filename ()) + : "??"; + bool printed =3D (shadow_status =3D=3D var_shadowing::SHADOWED); + string_file out (current_uiout->can_emit_style_escape ()); + + gdb_printf (&out, "\t<%ps:", + styled_string (file_name_style.style (), file_name)); + + if (var->line () > 0) + gdb_printf (&out, "%ps", + styled_string (line_number_style.style (), + pulongest (var->line ()))); + else + gdb_puts ("No line number information available", &out); + + if (printed) + { + gdb_puts (", ", &out); + fputs_styled ("shadowed", metadata_style.style (), &out); + } + + gdb_puts (">", &out); + + gdb_puts (out.c_str (), stream); + } } catch (const gdb_exception_error &except) { @@ -2406,7 +2442,8 @@ print_variable_value (symbol *var, const frame_info_p= tr &frame, void print_variable_and_value (const char *name, symbol *var, const frame_info_ptr &frame, - ui_file *stream, int indent) + ui_file *stream, int indent, + var_shadowing shadow_status) { if (name =3D=3D nullptr) name =3D var->print_name (); @@ -2414,7 +2451,8 @@ print_variable_and_value (const char *name, symbol *v= ar, gdb_printf (stream, "%*s%ps =3D ", 2 * indent, "", styled_string (variable_name_style.style (), name)); =20 - print_variable_value (var, frame, stream, indent, current_language); + print_variable_value (var, frame, stream, indent, current_language, + shadow_status); =20 gdb_printf (stream, "\n"); } diff --git a/gdb/stack.c b/gdb/stack.c index e084976eabf..f41f818cebb 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -59,6 +59,7 @@ #include "cli/cli-option.h" #include "cli/cli-style.h" #include "gdbsupport/buildargv.h" +#include "gdbsupport/unordered_set.h" =20 /* The possible choices of "set print frame-arguments", and the value of this setting. */ @@ -2133,8 +2134,9 @@ backtrace_command_completer (struct cmd_list_element = *ignore, /* Iterate over the local variables of a block B, calling CB. */ =20 static void -iterate_over_block_locals (const struct block *b, - iterate_over_block_arg_local_vars_cb cb) +iterate_over_block_locals + (const struct block *b, + gdb::function_view <void (const char *, struct symbol *)> cb) { for (struct symbol *sym : block_iterator_range (b)) { @@ -2180,6 +2182,71 @@ iterate_over_block_local_vars (const struct block *b= lock, } } =20 +/* See stack.h. */ + +void +iterate_over_block_local_vars_printing + (const struct block *block, + iterate_over_block_arg_local_vars_cb_printing cb) +{ + gdb::unordered_set<std::string> collected_vars, shadowed_vars, printed_v= ars; + + /* Phase one: iterate over all locals within the block, and every parent + block up to the enclosing function block. Record all of the locals + seen, this allows us to know which locals are shadowing locals from a + more outer scope. */ + iterate_over_block_local_vars + (block, [&] (const char *print_name, struct symbol *sym) + { + if (!sym->is_argument ()) + { + if (!collected_vars.insert (print_name).second) + shadowed_vars.insert (print_name); + } + }); + + /* Phase two: iterate over all locals within the block, and every parent + block up to the enclosing function block. Print all the locals seen + by calling CB. Depending on the current language we vary the + arguments to CB to indicate shadowing. Or in some cases, we don't + print the local at all. */ + iterate_over_block_local_vars + (block, [&] (const char *print_name, struct symbol *sym) + { + bool already_printed =3D !printed_vars.insert (print_name).second; + bool shadowed =3D shadowed_vars.find (print_name) !=3D shadowed_vars= .end (); + + enum var_shadowing shadowing_status; + if (already_printed && shadowed) + shadowing_status =3D var_shadowing::SHADOWED; + else if (!already_printed && shadowed) + shadowing_status =3D var_shadowing::SHADOWING; + else + shadowing_status =3D var_shadowing::NONE; + + /* Only for C/C++/Fortran/Ada languages, in case of variables + shadowing print <file:line, shadowed> annotation after + the superblock variable. Iteration of block starts from inner + block which is printed only with location information. */ + if (get_lang_vars_shadowing_option (current_language->la_language) + =3D=3D lang_vars_shadowing::PRINT + && shadowing_status !=3D var_shadowing::NONE) + cb (print_name, sym, shadowing_status); + /* In case of Rust language it is possible to declare variable with + same name multiple times and only innermost instance of variable + is accessible. So print only the innermost instance and there is + no need of printing duplicates. */ + else if (get_lang_vars_shadowing_option (current_language->la_langua= ge) + =3D=3D lang_vars_shadowing::HIDE + && shadowing_status =3D=3D var_shadowing::SHADOWED) + { + /* Nothing. */ + } + else + cb (print_name, sym, var_shadowing::NONE); + }); +} + /* Data to be passed around in the calls to the locals and args iterators. */ =20 @@ -2192,14 +2259,16 @@ struct print_variable_and_value_data struct ui_file *stream; int values_printed; =20 - void operator() (const char *print_name, struct symbol *sym); + void operator() (const char *print_name, struct symbol *sym, + var_shadowing shadow_status); }; =20 /* The callback for the locals and args iterators. */ =20 void print_variable_and_value_data::operator() (const char *print_name, - struct symbol *sym) + struct symbol *sym, + var_shadowing shadow_status) { frame_info_ptr frame; =20 @@ -2219,7 +2288,8 @@ print_variable_and_value_data::operator() (const char= *print_name, return; } =20 - print_variable_and_value (print_name, sym, frame, stream, num_tabs); + print_variable_and_value (print_name, sym, frame, stream, num_tabs, + shadow_status); =20 values_printed =3D 1; } @@ -2288,7 +2358,7 @@ print_frame_local_vars (const frame_info_ptr &frame, scoped_restore_selected_frame restore_selected_frame; select_frame (frame); =20 - iterate_over_block_local_vars (block, cb_data); + iterate_over_block_local_vars_printing (block, cb_data); =20 if (!cb_data.values_printed && !quiet) { @@ -2386,6 +2456,37 @@ iterate_over_block_arg_vars (const struct block *b, } } =20 +/* See stack.h. */ + +void +iterate_over_block_arg_vars_printing + (const struct block *b, + iterate_over_block_arg_local_vars_cb_printing cb) +{ + for (struct symbol *sym : block_iterator_range (b)) + { + /* Don't worry about things which aren't arguments. */ + if (sym->is_argument ()) + { + /* We have to look up the symbol because arguments can have + two entries (one a parameter, one a local) and the one we + want is the local, which lookup_symbol will find for us. + This includes gcc1 (not gcc2) on the sparc when passing a + small structure and gcc2 when the argument type is float + and it is passed as a double and converted to float by + the prologue (in the latter case the type of the LOC_ARG + symbol is double and the type of the LOC_LOCAL symbol is + float). There are also LOC_ARG/LOC_REGISTER pairs which + are not combined in symbol-reading. */ + + struct symbol *sym2 + =3D lookup_symbol_search_name (sym->search_name (), + b, SEARCH_VAR_DOMAIN).symbol; + cb (sym->print_name (), sym2, var_shadowing::NONE); + } + } +} + /* Print all argument variables of the function of FRAME. Print them with values to STREAM. If REGEXP is not NULL, only print argument variables whose name @@ -2428,7 +2529,7 @@ print_frame_arg_vars (const frame_info_ptr &frame, cb_data.stream =3D stream; cb_data.values_printed =3D 0; =20 - iterate_over_block_arg_vars (func->value_block (), cb_data); + iterate_over_block_arg_vars_printing (func->value_block (), cb_data); =20 if (!cb_data.values_printed && !quiet) { diff --git a/gdb/stack.h b/gdb/stack.h index 48096a29286..4f7c223f47f 100644 --- a/gdb/stack.h +++ b/gdb/stack.h @@ -20,6 +20,8 @@ #ifndef GDB_STACK_H #define GDB_STACK_H =20 +enum class var_shadowing; + gdb::unique_xmalloc_ptr<char> find_frame_funname (const frame_info_ptr &fr= ame, enum language *funlang, struct symbol **funcp); @@ -27,12 +29,32 @@ gdb::unique_xmalloc_ptr<char> find_frame_funname (const= frame_info_ptr &frame, using iterate_over_block_arg_local_vars_cb =3D gdb::function_view<void (const char *print_name, struct symbol *sym)= >; =20 +using iterate_over_block_arg_local_vars_cb_printing + =3D gdb::function_view<void (const char *print_name, struct symbol *sym, + var_shadowing shadow_status)>; + void iterate_over_block_arg_vars (const struct block *block, iterate_over_block_arg_local_vars_cb cb); =20 void iterate_over_block_local_vars (const struct block *block, iterate_over_block_arg_local_vars_cb cb); =20 +/* Iterate over all the argument variables in block B, call CB for + each variable with its print name, symbol, and shadowing status. */ + +void iterate_over_block_arg_vars_printing + (const struct block *block, + iterate_over_block_arg_local_vars_cb_printing cb); + +/* Iterate over all the local variables in block B, including all its + superblocks, stopping when the top-level block is reached. Call CB + for each variable with its print name, symbol, and shadowing status + relative to inner blocks already visited. */ + +void iterate_over_block_local_vars_printing + (const struct block *block, + iterate_over_block_arg_local_vars_cb_printing cb); + /* Initialize *WHAT to be a copy of the user desired print what frame info. If !WHAT.has_value (), the printing function chooses a default set of information to print, otherwise the printing function should print diff --git a/gdb/testsuite/gdb.ada/var_shadowing.exp b/gdb/testsuite/gdb.ad= a/var_shadowing.exp new file mode 100644 index 00000000000..ffa96b049cf --- /dev/null +++ b/gdb/testsuite/gdb.ada/var_shadowing.exp @@ -0,0 +1,39 @@ +# Copyright 2023-2026 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/>. + +load_lib "ada.exp" + +require allow_ada_tests + +standard_ada_testfile var_shadowing + +if {[gdb_compile_ada "${srcfile}" "${binfile}" \ + executable [list debug]] !=3D "" } { + return +} + +clean_restart ${testfile} + +set i_level1 [gdb_get_line_number "I-Level1"] +set i_level2 [gdb_get_line_number "I-Level2"] +set i_level3 [gdb_get_line_number "I-Level3"] +set bp_location [gdb_get_line_number "BREAK"] +runto "var_shadowing.adb:$bp_location" + +gdb_test "info locals" [multi_line \ + "i =3D 111\t<$testfile.adb:$i_level3>" \ + "i =3D 11\t<$testfile.adb:$i_level2, shadowed>" \ + "i =3D 1\t<$testfile.adb:$i_level1, shadowed>" \ +] "info locals at innermost level" diff --git a/gdb/testsuite/gdb.ada/var_shadowing/var_shadowing.adb b/gdb/te= stsuite/gdb.ada/var_shadowing/var_shadowing.adb new file mode 100644 index 00000000000..27983388555 --- /dev/null +++ b/gdb/testsuite/gdb.ada/var_shadowing/var_shadowing.adb @@ -0,0 +1,30 @@ +-- Copyright 2023-2026 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/>. + +with Ada.Text_IO; use Ada.Text_IO; + +procedure Varshadow is + I : Integer :=3D 1; -- I-Level1 +begin + declare + I : Integer :=3D 11; -- I-Level2 + begin + declare + I : Integer :=3D 111; -- I-Level3 + begin + Put_Line ("hello"); -- BREAK + end; + end; +end; diff --git a/gdb/testsuite/gdb.base/var-shadowing.c b/gdb/testsuite/gdb.bas= e/var-shadowing.c new file mode 100755 index 00000000000..7886ea31aec --- /dev/null +++ b/gdb/testsuite/gdb.base/var-shadowing.c @@ -0,0 +1,51 @@ +/* Copyright (C) 2023-2026 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 <stdlib.h> + +int +shadowing (void) +{ + int a =3D 100; /* bp for entry */ + unsigned int val1 =3D 1; /* val1-d1 */ + unsigned int val2 =3D 2; /* val2-d1 */ + a =3D 101; /* bp for locals 1 */ + { + unsigned int val2 =3D 3; /* val2-d2 */ + unsigned int val3 =3D 4; /* val3-d1 */ + a =3D 102; /* bp for locals 2 */ + { + unsigned int val1 =3D 5; /* val1-d2 */ + a =3D 103; /* bp for locals 3 */ + { + #include "var-shadowing2.c" + unsigned int val1 =3D 6; /* val1-d3 */ + unsigned int val2 =3D 7; /* val2-d3 */ + unsigned int val3 =3D 8; /* val3-d2 */ + a =3D 104; /* bp for locals 4 */ + } + } + } + a =3D 105; + + return 0; /* bp for locals 5 */ +} + +int +main (void) +{ + shadowing (); + return 0; +} diff --git a/gdb/testsuite/gdb.base/var-shadowing.exp b/gdb/testsuite/gdb.b= ase/var-shadowing.exp new file mode 100755 index 00000000000..119b8c39d4a --- /dev/null +++ b/gdb/testsuite/gdb.base/var-shadowing.exp @@ -0,0 +1,92 @@ +# Copyright 2023-2026 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/>. + +standard_testfile +if [prepare_for_testing "failed to prepare" $testfile $srcfile] { + return +} + +if ![runto_main] { + return +} + +set bp_line1 [gdb_get_line_number "bp for locals 1"] +set bp_line2 [gdb_get_line_number "bp for locals 2"] +set bp_line3 [gdb_get_line_number "bp for locals 3"] +set bp_line4 [gdb_get_line_number "bp for locals 4"] +set bp_line5 [gdb_get_line_number "bp for locals 5"] + +set val1_d1 [gdb_get_line_number "val1-d1"] +set val1_d2 [gdb_get_line_number "val1-d2"] +set val1_d3 [gdb_get_line_number "val1-d3"] +set val2_d1 [gdb_get_line_number "val2-d1"] +set val2_d2 [gdb_get_line_number "val2-d2"] +set val2_d3 [gdb_get_line_number "val2-d3"] +set val3_d1 [gdb_get_line_number "val3-d1"] +set val3_d2 [gdb_get_line_number "val3-d2"] +set a_line [gdb_get_line_number "bp for entry"] + +gdb_breakpoint $srcfile:$bp_line1 +gdb_continue_to_breakpoint "continue to outermost level" \ + ".*$srcfile:$bp_line1.*" +gdb_test "info locals" [multi_line \ + "val1 =3D 1" \ + "val2 =3D 2" \ + ] "info locals at outermost level" + +gdb_breakpoint $srcfile:$bp_line2 +gdb_continue_to_breakpoint "continue to first level" ".*$srcfile:$bp_line2= .*" +gdb_test "info locals" [multi_line \ + "val2 =3D 3\t<$srcfile:$val2_d2>" \ + "val3 =3D 4" \ + "a =3D 101" \ + "val1 =3D 1" \ + "val2 =3D 2\t<$srcfile:$val2_d1, shadowed>" \ + ] "info locals first level" + +gdb_breakpoint $srcfile:$bp_line3 +gdb_continue_to_breakpoint "continue to second level" ".*$srcfile:$bp_line= 3.*" +gdb_test "info locals" [multi_line \ + "val1 =3D 5\t<$srcfile:$val1_d2>" \ + "val2 =3D 3\t<$srcfile:$val2_d2>" \ + "val3 =3D 4" \ + "a =3D 102" \ + "val1 =3D 1\t<$srcfile:$val1_d1, shadowed>" \ + "val2 =3D 2\t<$srcfile:$val2_d1, shadowed>" \ + ] "info locals second level" + +gdb_breakpoint $srcfile:$bp_line4 +gdb_continue_to_breakpoint "continue to innermost level" ".*$srcfile:$bp_l= ine4.*" +gdb_test "info locals" [multi_line \ + "a =3D 999\t<${testfile}2.c:16>" \ + "val1 =3D 6\t<$srcfile:$val1_d3>" \ + "val2 =3D 7\t<$srcfile:$val2_d3>" \ + "val3 =3D 8\t<$srcfile:$val3_d2>" \ + "val1 =3D 5\t<$srcfile:$val1_d2, shadowed>" \ + "val2 =3D 3\t<$srcfile:$val2_d2, shadowed>" \ + "val3 =3D 4\t<$srcfile:$val3_d1, shadowed>" \ + "a =3D 103\t<$srcfile:$a_line, shadowed>" \ + "val1 =3D 1\t<$srcfile:$val1_d1, shadowed>" \ + "val2 =3D 2\t<$srcfile:$val2_d1, shadowed>" \ + ] "info locals at innermost level" + +gdb_breakpoint $srcfile:$bp_line5 +gdb_continue_to_breakpoint "continue to outermost level last" \ + ".*$srcfile:$bp_line5.*" +gdb_test "info locals" [multi_line \ + "a =3D 105" \ + "val1 =3D 1" \ + "val2 =3D 2" \ + ] "info locals at outermost level last" diff --git a/gdb/testsuite/gdb.base/var-shadowing2.c b/gdb/testsuite/gdb.ba= se/var-shadowing2.c new file mode 100644 index 00000000000..d70fcd3bdd5 --- /dev/null +++ b/gdb/testsuite/gdb.base/var-shadowing2.c @@ -0,0 +1,16 @@ +/* Copyright (C) 2023-2026 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 a =3D 999; diff --git a/gdb/testsuite/gdb.opt/inline-locals.exp b/gdb/testsuite/gdb.op= t/inline-locals.exp index 2c3a9f71c2f..6af398e5cf4 100644 --- a/gdb/testsuite/gdb.opt/inline-locals.exp +++ b/gdb/testsuite/gdb.opt/inline-locals.exp @@ -42,9 +42,11 @@ if { ! $no_frames } { "backtrace from bar 2" gdb_test "up" "#1 .*func1 .* at .*" "up from bar 2" gdb_test "info frame" ".*inlined into frame.*" "func1 inlined 2" - set pass_re "array =3D \\{0 <repeats 64 times>\\}" + set shadowed_pass_re "\t<$srcfile:$decimal>" + set shadowed_fail_re "\t<$srcfile:$decimal, shadowed>" + set pass_re "array =3D \\{0 <repeats 64 times>\\}($shadowed_pass_re)?" set kfail_re [multi_line $pass_re \ - "array =3D <optimized out>"] + "array =3D <optimized out>$shadowed_fail_re"] gdb_test_multiple "info locals" "info locals above bar 2" { -re -wrap $pass_re { pass $gdb_test_name @@ -91,9 +93,9 @@ if { ! $no_frames } { "backtrace from bar 3" gdb_test "up" "#1 .*func1 .* at .*" "up from bar 3" gdb_test "info frame" ".*inlined into frame.*" "func1 inlined 3" - set pass_re "array =3D {$decimal, \[^\r\n\]*}" + set pass_re "array =3D {$decimal, \[^\r\n\]*}($shadowed_pass_re)?" set kfail_re [multi_line $pass_re \ - "array =3D <optimized out>"] + "array =3D <optimized out>$shadowed_fail_re"] gdb_test_multiple "info locals" "info locals above bar 3" { -re -wrap $pass_re { pass $gdb_test_name @@ -133,7 +135,8 @@ proc check_scoped_locals {bp_label pass_re} { gdb_breakpoint $srcfile:$locals_bp =20 gdb_continue_to_breakpoint "$bp_label" ".*$srcfile:$locals_bp.*" - set kfail_re [multi_line $pass_re ".*<optimized out>"] + set kfail_re [multi_line $pass_re \ + ".*<optimized out>(.*<$srcfile:$::decimal, shadowed>)?"] gdb_test_multiple "info locals" "scoped info locals at $bp_label" { -re -wrap $pass_re { pass $gdb_test_name @@ -149,7 +152,9 @@ proc check_scoped_locals {bp_label pass_re} { } =20 if {! $no_frames } { - check_scoped_locals "bp for locals 1" "loc2 =3D 20\r\nloc1 =3D 10" - check_scoped_locals "bp for locals 2" "loc3 =3D 30\r\nloc2 =3D 20\r\nl= oc1 =3D 10" - check_scoped_locals "bp for locals 3" "loc1 =3D 10" + check_scoped_locals "bp for locals 1" \ + "loc2 =3D 20(\t<$srcfile:$decimal>)?\r\nloc1 =3D 10(\t<$srcfile:$decimal>= )?" + check_scoped_locals "bp for locals 2" \ + "loc3 =3D 30(\t<$srcfile:$decimal>)?\r\nloc2 =3D 20(\t<$srcfile:$decimal>= )?\r\nloc1 =3D 10(\t<$srcfile:$decimal>)?" + check_scoped_locals "bp for locals 3" "loc1 =3D 10(\t<$srcfile:$decima= l>)?" } diff --git a/gdb/testsuite/gdb.rust/var_reuse.exp b/gdb/testsuite/gdb.rust/= var_reuse.exp new file mode 100755 index 00000000000..dcba42b554d --- /dev/null +++ b/gdb/testsuite/gdb.rust/var_reuse.exp @@ -0,0 +1,36 @@ +# Copyright 2023-2026 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/>. + +load_lib rust-support.exp +require allow_rust_tests +require {can_compile rust} + +standard_testfile .rs +if {[prepare_for_testing "failed to prepare" \ + $testfile $srcfile {debug rust}]} { + return +} + +set line [gdb_get_line_number "set breakpoint here"] +if {![runto ${srcfile}:$line]} { + untested "could not run to breakpoint" + return +} + +# Wrong local values are shown for rustc version >=3D 1.73. +if {[rust_at_least 1.73]} { + setup_xfail "*-*-*" "gdb/31079" +} +gdb_test "info local _x" "_x =3D 12" "print local _x variable" diff --git a/gdb/testsuite/gdb.rust/var_reuse.rs b/gdb/testsuite/gdb.rust/v= ar_reuse.rs new file mode 100755 index 00000000000..70101a043c5 --- /dev/null +++ b/gdb/testsuite/gdb.rust/var_reuse.rs @@ -0,0 +1,20 @@ +// Copyright (C) 2023-2026 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/>. + +fn main() { + let _x =3D 5; + let _x =3D _x + 7; + let _y =3D 8; // set breakpoint here +} diff --git a/gdb/value.h b/gdb/value.h index e94c69e49d3..4cc0d7d0902 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -1578,28 +1578,46 @@ extern int val_print_string (struct type *elttype, = const char *encoding, struct ui_file *stream, const struct value_print_options *options); =20 +/* Track the shadowing status of a variable. */ +enum class var_shadowing +{ + /* This variable is not shadowed, and not shadowing. */ + NONE, + + /* This variable is shadowed by a later one. */ + SHADOWED, + + /* This variable is shadowing an earlier one, and is not itself + shadowed. */ + SHADOWING +}; + /* Print the value in stack frame FRAME of a variable specified by a struct symbol. STREAM is the ui_file on which to print the value. INDENT specifies the number of indent levels to print before printing the variable name. LANGUAGE is the language to use for - printing. */ + printing. SHADOW_STATUS specifies the variable shadowing + information. */ =20 extern void print_variable_value (symbol *var, const frame_info_ptr &frame, ui_file *stream, int indent, - const language_defn *language); + const language_defn *language, + var_shadowing shadow_status); =20 /* Print the value in stack frame FRAME of a variable specified by a struct symbol. NAME is the name to print; if NULL then VAR's print name will be used. STREAM is the ui_file on which to print the value. INDENT specifies the number of indent levels to print - before printing the variable name. */ + before printing the variable name. SHADOW_STATUS specifies the + variable shadowing information. */ =20 extern void print_variable_and_value (const char *name, symbol *var, const frame_info_ptr &frame, ui_file *stream, - int indent); + int indent, + var_shadowing shadow_status); =20 extern void typedef_print (struct type *type, struct symbol *news, struct ui_file *stream);