#include "mi-parse.h"
#include <optional>
#include "inferior.h"
+#include "source.h"
+#include "gdbsupport/unordered_map.h"
enum what_to_list { locals, arguments, all };
static void
list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
enum print_values values, int skip_unavailable,
- const frame_print_options &fp_opts)
+ const frame_print_options &fp_opts,
+ const gdb::unordered_set<std::string> *shadowed_vars,
+ gdb::unordered_set<std::string> &printed_vars)
{
struct ui_out *uiout = current_uiout;
arg->val->type ()->length ()))))
return;
+ string_file stb;
+ const char *name = arg->sym->print_name ();
+ /* To distinguish innermost variable from the rest in the shadowed_vars
+ this boolean is needed. */
+ bool already_printed = !printed_vars.insert (name).second;
+ bool shadowed = shadowed_vars->find (name) != shadowed_vars->end ();
+
+ /* In case of Rust language it is possible to declare variable with
+ same name multiple times and only latest declaration of variable
+ is accessible. So print only the first instance and there is no
+ need of printing duplicates. */
+ if (get_lang_vars_shadowing_option (current_language->la_language)
+ == lang_vars_shadowing::HIDE && shadowed && already_printed)
+ return;
+
std::optional<ui_out_emit_tuple> tuple_emitter;
if (values != PRINT_NO_VALUES || what == all)
tuple_emitter.emplace (uiout, nullptr);
- string_file stb;
-
stb.puts (arg->sym->print_name ());
if (arg->entry_kind == print_entry_values_only)
stb.puts ("@entry");
}
uiout->field_stream ("value", stb);
}
+
+ /* Only for C/C++/Fortran/Ada languages, in case of variables shadowing
+ print shadowed field after the superblock variable and only location
+ of the variables in the innerblock. */
+ if (get_lang_vars_shadowing_option (current_language->la_language)
+ == lang_vars_shadowing::PRINT && shadowed
+ && !(values == PRINT_NO_VALUES && what == locals))
+ {
+ if (arg->sym->symtab () != nullptr)
+ {
+ symtab *symtab = arg->sym->symtab ();
+
+ uiout->field_string ("filename",
+ symtab_to_filename_for_display (symtab));
+ uiout->field_string ("fullname", symtab_to_fullname (symtab));
+ uiout->field_unsigned ("line", arg->sym->line ());
+ }
+
+ if (already_printed)
+ uiout->field_string ("shadowed", "true");
+ }
+}
+
+/* Returns true if address_class can be printed, otherwise returns false. */
+
+static bool
+can_print_aclass (struct symbol *sym, enum what_to_list what)
+{
+ bool print_me = false;
+
+ switch (sym->loc_class ())
+ {
+ default:
+ case LOC_UNDEF: /* catches errors */
+ case LOC_CONST: /* constant */
+ case LOC_TYPEDEF: /* local typedef */
+ case LOC_LABEL: /* local label */
+ case LOC_BLOCK: /* local function */
+ case LOC_CONST_BYTES: /* loc. byte seq. */
+ case LOC_UNRESOLVED: /* unresolved static */
+ case LOC_OPTIMIZED_OUT: /* optimized out */
+ print_me = false;
+ break;
+
+ case LOC_ARG: /* argument */
+ case LOC_REF_ARG: /* reference arg */
+ case LOC_REGPARM_ADDR: /* indirect register arg */
+ case LOC_LOCAL: /* stack local */
+ case LOC_STATIC: /* static */
+ case LOC_REGISTER: /* register */
+ case LOC_COMPUTED: /* computed location */
+ if (what == all)
+ print_me = true;
+ else if (what == locals)
+ print_me = !sym->is_argument ();
+ else
+ print_me = sym->is_argument ();
+ break;
+ }
+
+ return print_me;
}
/* Print a list of the objects for the frame FI in a certain form,
enum what_to_list what, enum print_values values,
const frame_info_ptr &fi, int skip_unavailable)
{
- const struct block *block;
+ const struct block *block, *orig_block;
const char *name_of_result;
struct ui_out *uiout = current_uiout;
+ gdb::unordered_set<std::string> collected_vars, shadowed_vars, printed_vars;
block = get_frame_block (fi, 0);
ui_out_emit_list list_emitter (uiout, name_of_result);
- while (block != 0)
+ orig_block = block;
+ /* Stored list of shadowed variables later help in identifying them
+ from the rest. */
+ while (block != nullptr)
{
for (struct symbol *sym : block_iterator_range (block))
{
- int print_me = 0;
-
- switch (sym->loc_class ())
+ if (can_print_aclass (sym, what))
{
- default:
- case LOC_UNDEF: /* catches errors */
- case LOC_CONST: /* constant */
- case LOC_TYPEDEF: /* local typedef */
- case LOC_LABEL: /* local label */
- case LOC_BLOCK: /* local function */
- case LOC_CONST_BYTES: /* loc. byte seq. */
- case LOC_UNRESOLVED: /* unresolved static */
- case LOC_OPTIMIZED_OUT: /* optimized out */
- print_me = 0;
- break;
-
- case LOC_ARG: /* argument */
- case LOC_REF_ARG: /* reference arg */
- case LOC_REGPARM_ADDR: /* indirect register arg */
- case LOC_LOCAL: /* stack local */
- case LOC_STATIC: /* static */
- case LOC_REGISTER: /* register */
- case LOC_COMPUTED: /* computed location */
- if (what == all)
- print_me = 1;
- else if (what == locals)
- print_me = !sym->is_argument ();
- else
- print_me = sym->is_argument ();
- break;
+ const char *name = sym->print_name ();
+ if (!collected_vars.insert (name).second)
+ shadowed_vars.insert (name);
}
- if (print_me)
+ }
+ if (block->function ())
+ break;
+ block = block->superblock ();
+ }
+
+ block = orig_block;
+ while (block != 0)
+ {
+ for (struct symbol *sym : block_iterator_range (block))
+ {
+ if (can_print_aclass (sym, what))
{
struct frame_arg arg, entryarg;
if (arg.entry_kind != print_entry_values_only)
list_arg_or_local (&arg, what, values, skip_unavailable,
- fp_opts);
+ fp_opts, &shadowed_vars, printed_vars);
if (entryarg.entry_kind != print_entry_values_no)
list_arg_or_local (&entryarg, what, values, skip_unavailable,
- fp_opts);
+ fp_opts, &shadowed_vars, printed_vars);
}
}
--- /dev/null
+# 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 mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+ continue
+}
+
+standard_testfile
+
+set opts {debug}
+if [build_executable ${testfile}.exp ${testfile} ${srcfile} $opts] {
+ return
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+mi_runto main
+
+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 "entry bp"]
+
+set fullname "${fullname_syntax}$srcfile"
+set filename "\[^\r\n\]+$srcfile"
+
+set stack_test1_regx "\\^done,(locals|variables)=\\\[\{name=\"a\",type=\"int\",value=\"$decimal\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\"\},{name=\"val2\",type=\"unsigned int\",value=\"2\"\}\\\]"
+set stack_test2_regx "\\^done,(locals|variables)=\\\[\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d2\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\"\},\{name=\"a\",type=\"int\",value=\"101\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
+set stack_test3_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d2\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d2\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\"\},\{name=\"a\",type=\"int\",value=\"102\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
+set stack_test4_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"6\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d3\"\},\{name=\"val2\",type=\"unsigned int\",value=\"7\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d3\"\},\{name=\"val3\",type=\"unsigned int\",value=\"8\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val3_d2\"\},\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val3_d1\",shadowed=\"true\"\},\{name=\"a\",type=\"int\",value=\"103\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
+set stack_test5_regx "\\^done,(locals|variables)=\\\[\{name=\"a\",type=\"int\",value=\"105\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\"\}\\\]"
+
+mi_gdb_test \
+ "-break-insert --source ${srcfile} --line ${bp_line1}" \
+ "\\^done.*source ${srcfile} \\-line ${bp_line1}.*" \
+ "bp at outermost level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line1}" \
+ { "" "disp=\"keep\"" } "continue to outermost level"
+mi_gdb_test "-stack-list-locals 0" \
+ "\\^done,locals=\\\[name=\"a\",name=\"val1\",name=\"val2\"\\\]" \
+ "-stack-list-locals 0 at outermost level"
+mi_gdb_test "-stack-list-variables 0" \
+ "\\^done,variables=\\\[{name=\"a\"},{name=\"val1\"},{name=\"val2\"}\\\]" \
+ "-stack-list-variables 0 at outermost level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test1_regx}" \
+ "-stack-list-locals 2 at outermost level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test1_regx}" \
+ "-stack-list-variables 2 at outermost level"
+
+mi_gdb_test \
+ "-break-insert --source ${srcfile} --line ${bp_line2}" \
+ "\\^done.*source ${srcfile} \\-line ${bp_line2}.*" \
+ "bp at first level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line2}" \
+ { "" "disp=\"keep\"" } "continue to first level"
+mi_gdb_test "-stack-list-locals 0" \
+ "\\^done,locals=\\\[name=\"val2\",name=\"val3\",name=\"a\",name=\"val1\",name=\"val2\"\\\]" \
+ "-stack-list-locals 0 at first level"
+mi_gdb_test "-stack-list-variables 0" \
+ "\\^done,variables=\\\[{name=\"val2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\"},{name=\"val2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]" \
+ "-stack-list-variables 0 at first level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test2_regx}" \
+ "-stack-list-locals 2 at first level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test2_regx}" \
+ "-stack-list-variables 2 at first level"
+
+mi_gdb_test \
+ "-break-insert --source ${srcfile} --line ${bp_line3}" \
+ "\\^done.*source ${srcfile} \\-line ${bp_line3}.*" \
+ "bp at second level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line3}" \
+ { "" "disp=\"keep\"" } "continue to second level"
+mi_gdb_test "-stack-list-locals 0" \
+ "\\^done,locals=\\\[name=\"val1\",name=\"val2\",name=\"val3\",name=\"a\",name=\"val1\",name=\"val2\"\\\]" \
+ "-stack-list-locals 0 at second level"
+mi_gdb_test "-stack-list-variables 0" \
+ "\\^done,variables=\\\[{name=\"val1\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d2\"},{name=\"val2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d1\",shadowed=\"true\"\},{name=\"val2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]" \
+ "-stack-list-variables 0 at second level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test3_regx}" \
+ "-stack-list-locals 2 at second level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test3_regx}" \
+ "-stack-list-variables 2 at second level"
+
+mi_gdb_test \
+ "-break-insert --source ${srcfile} --line ${bp_line4}" \
+ "\\^done.*source ${srcfile} \\-line ${bp_line4}.*" \
+ "bp at third level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line4}" \
+ { "" "disp=\"keep\"" } "continue to third level"
+mi_gdb_test "-stack-list-locals 0" \
+ "\\^done,locals=\\\[name=\"val1\",name=\"val2\",name=\"val3\",name=\"val1\",name=\"val2\",name=\"val3\",name=\"a\",name=\"val1\",name=\"val2\"\\\]" \
+ "-stack-list-locals 0 at third level"
+mi_gdb_test "-stack-list-variables 0" \
+ "\\^done,variables=\\\[{name=\"val1\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d3\"},{name=\"val2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d3\"},{name=\"val3\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val3_d2\"},{name=\"val1\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val3_d1\",shadowed=\"true\"\},{name=\"a\"},{name=\"val1\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val1_d1\",shadowed=\"true\"\},{name=\"val2\",filename=\"$filename\",fullname=\"$fullname\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]" \
+ "-stack-list-variables 0 at third level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test4_regx}" \
+ "-stack-list-locals 2 at third level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test4_regx}" \
+ "-stack-list-variables 2 at third level"
+
+mi_gdb_test \
+ "-break-insert --source ${srcfile} --line ${bp_line5}" \
+ "\\^done.*source ${srcfile} \\-line ${bp_line5}.*" \
+ "bp at outermost level last"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line5}" \
+ { "" "disp=\"keep\"" } "continue to outermost level last"
+mi_gdb_test "-stack-list-locals 0" \
+ "\\^done,locals=\\\[name=\"a\",name=\"val1\",name=\"val2\"\\\]" \
+ "-stack-list-locals 0 at outermost level last"
+mi_gdb_test "-stack-list-variables 0" \
+ "\\^done,variables=\\\[{name=\"a\"},{name=\"val1\"},{name=\"val2\"}\\\]" \
+ "-stack-list-variables at outermost level last"
+mi_gdb_test "-stack-list-locals 2" "${stack_test5_regx}" \
+ "-stack-list-locals 2 at outermost level last"
+mi_gdb_test "-stack-list-variables 2" "${stack_test5_regx}" \
+ "-stack-list-variables 2 at outermost level last"