Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v14 0/2] add annotation in 'info locals' command for variables shadowing case
@ 2026-07-14 12:40 Abdul Basit Ijaz
  2026-07-14 12:40 ` [PATCH v14 1/2] gdb: " Abdul Basit Ijaz
  2026-07-14 12:40 ` [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands Abdul Basit Ijaz
  0 siblings, 2 replies; 7+ messages in thread
From: Abdul Basit Ijaz @ 2026-07-14 12:40 UTC (permalink / raw)
  To: gdb-patches
  Cc: pedro, philippe.waroquiers, aburgess, christina.schimpe, lsix,
	eliz, abdul.b.ijaz, guinevere

Hi All,

For the variable shadowing case, these patches add the annotation "shadowed"
and location information to the shadowed variables so that it is easier for
the end user to identify which variable was shadowed.

Patch #1 updates "info locals" command
Path #2 is for the mi commands '-stack-list-locals' and '-stack-list-variables'.

Last V13 Series:
https://sourceware.org/pipermail/gdb-patches/2026-July/228523.html

V12 Series:
https://sourceware.org/pipermail/gdb-patches/2025-December/223219.html

V11 Series:
https://sourceware.org/pipermail/gdb-patches/2025-November/223002.html

V10 Series:
https://sourceware.org/pipermail/gdb-patches/2025-April/217019.html

V9 Series:
https://sourceware.org/pipermail/gdb-patches/2025-March/216590.html

V8 Series:
https://sourceware.org/pipermail/gdb-patches/2024-December/214263.html

Documentation in V7 Series was already approved by Eli:
https://sourceware.org/pipermail/gdb-patches/2024-October/212228.html
https://sourceware.org/pipermail/gdb-patches/2024-October/212229.html

Patch#1 V6 was Reviewed-by Larsen:
https://sourceware.org/pipermail/gdb-patches/2024-January/205923.html
Patch#2 V2 was Tested-by Larsen:
https://sourceware.org/pipermail/gdb-patches/2024-January/205922.html

Changes since V13:
* Fixes following Andrew feedback on V13 patch series:
  Patch#1: https://sourceware.org/pipermail/gdb-patches/2026-July/228604.html
  Patch#2: https://sourceware.org/pipermail/gdb-patches/2026-July/228627.html

Testing is done for unix/m32/native-gdbserver configurations and no issue is seen.

Thanks & Best Regards
Abdul Basit Ijaz

Ijaz, Abdul B (2):
  gdb: add annotation in 'info locals' command for variables shadowing
    case
  gdb: add shadowed field in '-stack-list-locals/variables' mi commands

 gdb/NEWS                                      |   9 ++
 gdb/c-typeprint.c                             |   3 +-
 gdb/doc/gdb.texinfo                           |  48 ++++++
 gdb/language.c                                |  20 +++
 gdb/language.h                                |  18 +++
 gdb/mi/mi-cmd-stack.c                         | 144 +++++++++++++-----
 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.mi/mi-var-shadowing.c       |  50 ++++++
 gdb/testsuite/gdb.mi/mi-var-shadowing.exp     | 141 +++++++++++++++++
 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 +++-
 20 files changed, 885 insertions(+), 60 deletions(-)
 create mode 100644 gdb/testsuite/gdb.ada/var_shadowing.exp
 create mode 100644 gdb/testsuite/gdb.ada/var_shadowing/var_shadowing.adb
 create mode 100755 gdb/testsuite/gdb.base/var-shadowing.c
 create mode 100755 gdb/testsuite/gdb.base/var-shadowing.exp
 create mode 100644 gdb/testsuite/gdb.base/var-shadowing2.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.exp
 create mode 100755 gdb/testsuite/gdb.rust/var_reuse.exp
 create mode 100755 gdb/testsuite/gdb.rust/var_reuse.rs

-- 
2.34.1

Intel Deutschland GmbH

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v14 1/2] gdb: add annotation in 'info locals' command for variables shadowing case
  2026-07-14 12:40 [PATCH v14 0/2] add annotation in 'info locals' command for variables shadowing case Abdul Basit Ijaz
@ 2026-07-14 12:40 ` Abdul Basit Ijaz
  2026-07-17 12:56   ` Andrew Burgess
  2026-07-14 12:40 ` [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands Abdul Basit Ijaz
  1 sibling, 1 reply; 7+ messages in thread
From: Abdul Basit Ijaz @ 2026-07-14 12:40 UTC (permalink / raw)
  To: gdb-patches
  Cc: pedro, philippe.waroquiers, aburgess, christina.schimpe, lsix,
	eliz, abdul.b.ijaz, guinevere

From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>

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 identify
which variable belongs to the current scope.  So for such cases add location
info to the innermost listed variables and for super block variables add
"shadowed" annotation in the form of "<file.c:line, shadowed>".

Suppose we have

1:int x = 3;
2:  {
3:    int x = 4;
4:    int y = 52;
5:    x = 99; /* break here */
6:  }

Currently:

(gdb) info locals
x = 4
y = 52
x = 3

After applying this patch, we obtain:

(gdb) info locals
x = 4  <file.c:3>
y = 52
x = 3  <file.c:1, shadowed>

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.

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 >= 1.73 so XFAIL is added accordingly.

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.

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.

Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Co-Authored-By: Andrew Burgess <aburgess@redhat.com>
---
 gdb/NEWS                                      |   5 +
 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, 561 insertions(+), 23 deletions(-)
 create mode 100644 gdb/testsuite/gdb.ada/var_shadowing.exp
 create mode 100644 gdb/testsuite/gdb.ada/var_shadowing/var_shadowing.adb
 create mode 100755 gdb/testsuite/gdb.base/var-shadowing.c
 create mode 100755 gdb/testsuite/gdb.base/var-shadowing.exp
 create mode 100644 gdb/testsuite/gdb.base/var-shadowing2.c
 create mode 100755 gdb/testsuite/gdb.rust/var_reuse.exp
 create mode 100755 gdb/testsuite/gdb.rust/var_reuse.rs

diff --git a/gdb/NEWS b/gdb/NEWS
index ec9b5a33787..2c8adf05b43 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -201,6 +201,11 @@ 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.
 
+info locals
+  GDB now shows the "shadowed" annotation and the location information
+  for variables that are shadowed, or which are shadowing.
+
+
 * Removed commands
 
 target ctf
diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index e418aca56a4..9397160d29b 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -818,7 +818,8 @@ c_type_print_template_args (const struct type_print_options *flags,
       if (sym->loc_class () == 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);
     }
 
   gdb_puts (_("] "), stream);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a698b2b8451..ee33bfdba13 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.
 
+@smallexample
+@group
+1: int x = 3;
+2: @{
+3:       int x = 4;
+4:       int y = 52;
+5:       x = 99; // breakpoint-line
+6: @}
+@end group
+@group
+(gdb) info locals
+x = 4	<file.c:3>
+y = 52
+x = 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).  For such cases, its location for the
+outermost scope is followed by @samp{shadowed}.  The location can
+help to locate the instances of shadowed variables.  So,
+location information is only added for shadowed variables.
+
 @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 struct language_defn *la,
   return sym;
 }
 
+/* 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.  */
 
 INIT_GDB_FILE (language)
diff --git a/gdb/language.h b/gdb/language.h
index 75154d9c591..4172773307a 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -83,6 +83,20 @@ enum macro_expansion
     macro_expansion_no, macro_expansion_c
   };
 
+/* 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,
+  };
+
 \f
 /* Per architecture (OS/ABI) language information.  */
 
@@ -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);
 
+/* Returns the shadowing option supported for the input language.  */
+
+extern lang_vars_shadowing get_lang_vars_shadowing_option (enum language lang);
+
 /* Save the current language and restore it upon destruction.  */
 
 class scoped_restore_current_language
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index a337a6b7db9..e16af63189e 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2377,7 +2377,8 @@ clear_dangling_display_expressions (struct objfile *objfile)
 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 = 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 != 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 = (var->symtab () != nullptr
+				   && var->symtab ()->filename () != nullptr)
+				   ? lbasename (var->symtab ()->filename ())
+				   : "??";
+	  bool printed = (shadow_status == 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_ptr &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 == nullptr)
     name = var->print_name ();
@@ -2414,7 +2451,8 @@ print_variable_and_value (const char *name, symbol *var,
   gdb_printf (stream, "%*s%ps = ", 2 * indent, "",
 	      styled_string (variable_name_style.style (), name));
 
-  print_variable_value (var, frame, stream, indent, current_language);
+  print_variable_value (var, frame, stream, indent, current_language,
+			shadow_status);
 
   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"
 
 /* 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.  */
 
 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 *block,
     }
 }
 
+/* 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_vars;
+
+  /* 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 = !printed_vars.insert (print_name).second;
+      bool shadowed = shadowed_vars.find (print_name) != shadowed_vars.end ();
+
+      enum var_shadowing shadowing_status;
+      if (already_printed && shadowed)
+	shadowing_status = var_shadowing::SHADOWED;
+      else if (!already_printed && shadowed)
+	shadowing_status = var_shadowing::SHADOWING;
+      else
+	shadowing_status = 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)
+	  == lang_vars_shadowing::PRINT
+	  && shadowing_status != 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_language)
+	       == lang_vars_shadowing::HIDE
+	       && shadowing_status == 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.  */
 
@@ -2192,14 +2259,16 @@ struct print_variable_and_value_data
   struct ui_file *stream;
   int values_printed;
 
-  void operator() (const char *print_name, struct symbol *sym);
+  void operator() (const char *print_name, struct symbol *sym,
+		   var_shadowing shadow_status);
 };
 
 /* The callback for the locals and args iterators.  */
 
 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;
 
@@ -2219,7 +2288,8 @@ print_variable_and_value_data::operator() (const char *print_name,
       return;
     }
 
-  print_variable_and_value (print_name, sym, frame, stream, num_tabs);
+  print_variable_and_value (print_name, sym, frame, stream, num_tabs,
+			    shadow_status);
 
   values_printed = 1;
 }
@@ -2288,7 +2358,7 @@ print_frame_local_vars (const frame_info_ptr &frame,
   scoped_restore_selected_frame restore_selected_frame;
   select_frame (frame);
 
-  iterate_over_block_local_vars (block, cb_data);
+  iterate_over_block_local_vars_printing (block, cb_data);
 
   if (!cb_data.values_printed && !quiet)
     {
@@ -2386,6 +2456,37 @@ iterate_over_block_arg_vars (const struct block *b,
     }
 }
 
+/* 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
+	    = 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 = stream;
   cb_data.values_printed = 0;
 
-  iterate_over_block_arg_vars (func->value_block (), cb_data);
+  iterate_over_block_arg_vars_printing (func->value_block (), cb_data);
 
   if (!cb_data.values_printed && !quiet)
     {
diff --git a/gdb/stack.h b/gdb/stack.h
index ad2700b59a7..a03103b9847 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -20,6 +20,8 @@
 #ifndef GDB_STACK_H
 #define GDB_STACK_H
 
+enum class var_shadowing;
+
 gdb::unique_xmalloc_ptr<char> find_frame_funname (const frame_info_ptr &frame,
 						  enum language *funlang,
 						  struct symbol **funcp);
@@ -27,12 +29,32 @@ gdb::unique_xmalloc_ptr<char> find_frame_funname (const frame_info_ptr &frame,
 typedef gdb::function_view<void (const char *print_name, struct symbol *sym)>
      iterate_over_block_arg_local_vars_cb;
 
+typedef gdb::function_view<void (const char *print_name, struct symbol *sym,
+				 var_shadowing shadow_status)>
+    iterate_over_block_arg_local_vars_cb_printing;
+
 void iterate_over_block_arg_vars (const struct block *block,
 				  iterate_over_block_arg_local_vars_cb cb);
 
 void iterate_over_block_local_vars (const struct block *block,
 				    iterate_over_block_arg_local_vars_cb cb);
 
+/* 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.ada/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]] != "" } {
+    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 = 111\t<$testfile.adb:$i_level3>"  \
+    "i = 11\t<$testfile.adb:$i_level2, shadowed>"  \
+    "i = 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/testsuite/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 := 1;  -- I-Level1
+begin
+  declare
+    I : Integer := 11; -- I-Level2
+  begin
+    declare
+      I : Integer := 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.base/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 = 100;  /* bp for entry */
+  unsigned int val1 = 1;		/* val1-d1 */
+  unsigned int val2 = 2;		/* val2-d1 */
+  a = 101;  /* bp for locals 1 */
+  {
+    unsigned int val2 = 3;		/* val2-d2 */
+    unsigned int val3 = 4;		/* val3-d1 */
+    a = 102;  /* bp for locals 2 */
+    {
+      unsigned int val1 = 5;		/* val1-d2 */
+      a = 103;  /* bp for locals 3 */
+      {
+	#include "var-shadowing2.c"
+	unsigned int val1 = 6;	/* val1-d3 */
+	unsigned int val2 = 7;	/* val2-d3 */
+	unsigned int val3 = 8;	/* val3-d2 */
+	a = 104;  /* bp for locals 4 */
+      }
+    }
+  }
+  a = 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.base/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 = 1"  \
+    "val2 = 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 = 3\t<$srcfile:$val2_d2>"  \
+    "val3 = 4"  \
+    "a = 101"   \
+    "val1 = 1"  \
+    "val2 = 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_line3.*"
+gdb_test "info locals" [multi_line \
+    "val1 = 5\t<$srcfile:$val1_d2>"  \
+    "val2 = 3\t<$srcfile:$val2_d2>"  \
+    "val3 = 4"  \
+    "a = 102"   \
+    "val1 = 1\t<$srcfile:$val1_d1, shadowed>"  \
+    "val2 = 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_line4.*"
+gdb_test "info locals" [multi_line \
+    "a = 999\t<${testfile}2.c:16>" \
+    "val1 = 6\t<$srcfile:$val1_d3>"  \
+    "val2 = 7\t<$srcfile:$val2_d3>"  \
+    "val3 = 8\t<$srcfile:$val3_d2>"  \
+    "val1 = 5\t<$srcfile:$val1_d2, shadowed>" \
+    "val2 = 3\t<$srcfile:$val2_d2, shadowed>" \
+    "val3 = 4\t<$srcfile:$val3_d1, shadowed>" \
+    "a = 103\t<$srcfile:$a_line, shadowed>"   \
+    "val1 = 1\t<$srcfile:$val1_d1, shadowed>" \
+    "val2 = 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 = 105" \
+    "val1 = 1"  \
+    "val2 = 2"  \
+    ] "info locals at outermost level last"
diff --git a/gdb/testsuite/gdb.base/var-shadowing2.c b/gdb/testsuite/gdb.base/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 = 999;
diff --git a/gdb/testsuite/gdb.opt/inline-locals.exp b/gdb/testsuite/gdb.opt/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 = \\{0 <repeats 64 times>\\}"
+    set shadowed_pass_re "\t<$srcfile:$decimal>"
+    set shadowed_fail_re "\t<$srcfile:$decimal, shadowed>"
+    set pass_re "array = \\{0 <repeats 64 times>\\}($shadowed_pass_re)?"
     set kfail_re [multi_line $pass_re \
-		      "array = <optimized out>"]
+		      "array = <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 = {$decimal, \[^\r\n\]*}"
+    set pass_re "array = {$decimal, \[^\r\n\]*}($shadowed_pass_re)?"
     set kfail_re [multi_line $pass_re \
-		      "array = <optimized out>"]
+		      "array = <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
 
     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} {
 }
 
 if {! $no_frames } {
-    check_scoped_locals "bp for locals 1" "loc2 = 20\r\nloc1 = 10"
-    check_scoped_locals "bp for locals 2" "loc3 = 30\r\nloc2 = 20\r\nloc1 = 10"
-    check_scoped_locals "bp for locals 3" "loc1 = 10"
+    check_scoped_locals "bp for locals 1" \
+	"loc2 = 20(\t<$srcfile:$decimal>)?\r\nloc1 = 10(\t<$srcfile:$decimal>)?"
+    check_scoped_locals "bp for locals 2" \
+	"loc3 = 30(\t<$srcfile:$decimal>)?\r\nloc2 = 20(\t<$srcfile:$decimal>)?\r\nloc1 = 10(\t<$srcfile:$decimal>)?"
+    check_scoped_locals "bp for locals 3" "loc1 = 10(\t<$srcfile:$decimal>)?"
 }
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 >= 1.73.
+if {[rust_at_least 1.73]} {
+    setup_xfail "*-*-*" "gdb/31079"
+}
+gdb_test "info local _x" "_x = 12" "print local _x variable"
diff --git a/gdb/testsuite/gdb.rust/var_reuse.rs b/gdb/testsuite/gdb.rust/var_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 = 5;
+    let _x = _x + 7;
+    let _y = 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);
 
+/* 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.  */
 
 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);
 
 /* 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.  */
 
 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);
 
 extern void typedef_print (struct type *type, struct symbol *news,
 			   struct ui_file *stream);
-- 
2.34.1

Intel Deutschland GmbH

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands
  2026-07-14 12:40 [PATCH v14 0/2] add annotation in 'info locals' command for variables shadowing case Abdul Basit Ijaz
  2026-07-14 12:40 ` [PATCH v14 1/2] gdb: " Abdul Basit Ijaz
@ 2026-07-14 12:40 ` Abdul Basit Ijaz
  2026-07-17 14:04   ` Andrew Burgess
  1 sibling, 1 reply; 7+ messages in thread
From: Abdul Basit Ijaz @ 2026-07-14 12:40 UTC (permalink / raw)
  To: gdb-patches
  Cc: pedro, philippe.waroquiers, aburgess, christina.schimpe, lsix,
	eliz, abdul.b.ijaz, guinevere

From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>

For C/C++/Fortran languages GDB prints same name variable multiple times in
case of variable shadowing and it is confusing for user to identify which
variable belongs to the current scope.  So GDB now prints location information
for shadowed variables and add 'shadowed' field also in '-stack-list-locals'
and '-stack-list-variables' mi commands for super-block shadowed variable.

Suppose we have test.c file

1:int x = 3;
2:  {
3:    int x = 4;
4:    int y = 5;
5:    x = 99; /* break here */
6:  }

The "-stack-list-locals" and "-stack-list-variables" mi commands at the
"break here" line gives the following output:

Before the change:

~~~
(gdb)
-stack-list-locals 0
^done,locals=[name="x",name="y",name="x"]
(gdb)
-stack-list-locals 1
^done,locals=[{name="x",value="4"},{name="y",value="5"},{name="x",value="3"}]
(gdb)
-stack-list-locals 2
^done,locals=[{name="x",type="int",value="4"},{name="y",type="int",value="5"},{name="x",type="int",value="3"}]
(gdb)
-stack-list-variables 0
^done,variables=[{name="x"},{name="y"},{name="x"}]
(gdb)
-stack-list-variables 1
^done,variables=[{name="x",value="4"},{name="y",value="5"},{name="x",value="3"}]
(gdb)
-stack-list-variables 2
^done,variables=[{name="x",type="int",value="4"},{name="y",type="int",value="5"},{name="x",type="int",value="3"}]
~~~

With this patch we obtain:

~~~
(gdb)
-stack-list-locals 0
^done,locals=[name="x",name="y",name="x"]
(gdb)
-stack-list-locals 1
^done,locals=[{name="x",value="4",filename="test.c",fullname="/home/src/test.c",line="5"},{name="y",value="5"},{name="x",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
(gdb)
-stack-list-locals 2
^done,locals=[{name="x",type="int",value="4",filename="test.c",fullname="/home/src/test.c",line="5"},{name="y",type="int",value="5"},{name="x",type="int",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
(gdb)
-stack-list-variables 0
^done,variables=[{name="x",filename="test.c",fullname="/home/src/test.c",line="5"},{name="y"},{name="x",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
(gdb)
-stack-list-variables 1
^done,variables=[{name="x",value="4",filename="test.c",fullname="/home/src/test.c",line="4"},{name="y",value="5"},{name="x",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
(gdb)
-stack-list-variables 2
^done,variables=[{name="x",type="int",value="4",filename="test.c",fullname="/home/src/test.c",line="4"},{name="y",type="int",value="5"},{name="x",type="int",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
~~~
Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
---
 gdb/NEWS                                  |   4 +
 gdb/doc/gdb.texinfo                       |  22 ++++
 gdb/mi/mi-cmd-stack.c                     | 144 ++++++++++++++++------
 gdb/testsuite/gdb.mi/mi-var-shadowing.c   |  50 ++++++++
 gdb/testsuite/gdb.mi/mi-var-shadowing.exp | 141 +++++++++++++++++++++
 5 files changed, 324 insertions(+), 37 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 2c8adf05b43..a3d0ad4ca11 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -594,6 +594,10 @@ multi-wp-addr in qSupported
    multiple times, and the same mapping was being reused.  In all
    other cases, this field will have the value 'false'.
 
+** GDB now shows the "shadowed", "filename", "fullname" and "line"
+   fields in the output of '-stack-list-locals/variables' mi
+   commands for variables that are shadowed, or which are shadowing.
+
 * Support for stabs debugging format and the a.out/dbx object format is
   deprecated, and will be removed in GDB 18.
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ee33bfdba13..883859e69c4 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35585,6 +35585,28 @@ If the @code{--skip-unavailable} option is specified, local variables
 and arguments that are not available are not listed.  Partially
 available arguments and local variables are still displayed, however.
 
+@smallexample
+@group
+1: int x = 3;
+2: @{
+3:       int x = 4;
+4:       int y = 5;
+5:       x = 99; // breakpoint-line
+6: @}
+@end group
+@group
+(gdb) -stack-list-variables 2
+^done,variables=[@{name="x",type="int",value="4",
+filename="name.c",fullname="/home/src/name.c",line="3"@},
+@{name="y",type="int",value="5"@},@{name="x",type="int",
+value="3",filename="name.c",fullname="/home/src/name.c",
+line="1",shadowed="true"@}]
+@end group
+@end smallexample
+
+The location information and @samp{shadowed} attribute is only added
+for shadowed variables (@pxref{shadowed variables}).
+
 @subsubheading Example
 
 @smallexample
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 280a08d121e..d2e814750d5 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -32,6 +32,8 @@
 #include "mi-parse.h"
 #include <optional>
 #include "inferior.h"
+#include "source.h"
+#include "gdbsupport/unordered_map.h"
 
 enum what_to_list { locals, arguments, all };
 
@@ -490,7 +492,9 @@ mi_cmd_stack_list_variables (const char *command, const char *const *argv,
 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;
 
@@ -514,12 +518,25 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
 					     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");
@@ -560,6 +577,69 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
 	}
       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 ();
+
+	  /* Use lbasename instead of symtab_to_filename_for_display as the
+	     latter defaults to relative path while basename is preferred
+	     here.  */
+	  uiout->field_string ("filename", lbasename (symtab->filename ()));
+	  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,
@@ -573,9 +653,10 @@ list_args_or_locals (const frame_print_options &fp_opts,
 		     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);
 
@@ -596,42 +677,31 @@ list_args_or_locals (const frame_print_options &fp_opts,
 
   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;
 
@@ -657,10 +727,10 @@ list_args_or_locals (const frame_print_options &fp_opts,
 
 	      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);
 	    }
 	}
 
diff --git a/gdb/testsuite/gdb.mi/mi-var-shadowing.c b/gdb/testsuite/gdb.mi/mi-var-shadowing.c
new file mode 100644
index 00000000000..875e753b485
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.c
@@ -0,0 +1,50 @@
+/* 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 = 100;	/* entry bp */
+  unsigned int val1 = 1;		/* val1-d1 */
+  unsigned int val2 = 2;		/* val2-d1 */
+  a = 101;  /* bp for locals 1 */
+  {
+    unsigned int val2 = 3;		/* val2-d2 */
+    unsigned int val3 = 4;		/* val3-d1 */
+    a = 102;  /* bp for locals 2 */
+    {
+      unsigned int val1 = 5;		/* val1-d2 */
+      a = 103;  /* bp for locals 3 */
+      {
+	unsigned int val1 = 6;		/* val1-d3 */
+	unsigned int val2 = 7;		/* val2-d3 */
+	unsigned int val3 = 8;		/* val3-d2 */
+	a = 104;  /* bp for locals 4 */
+      }
+    }
+  }
+  a = 105;
+
+  return 0; /* bp for locals 5 */
+}
+
+int
+main (void)
+{
+  shadowing ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.mi/mi-var-shadowing.exp b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
new file mode 100644
index 00000000000..27326864083
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
@@ -0,0 +1,141 @@
+# 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 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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
+set stack_test3_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\"\},\{name=\"a\",type=\"int\",value=\"102\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
+set stack_test4_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"6\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d3\"\},\{name=\"val2\",type=\"unsigned int\",value=\"7\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d3\"\},\{name=\"val3\",type=\"unsigned int\",value=\"8\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d2\"\},\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d1\",shadowed=\"true\"\},\{name=\"a\",type=\"int\",value=\"103\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\"},{name=\"val2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\"},{name=\"val2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},{name=\"val2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d3\"},{name=\"val2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d3\"},{name=\"val3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d2\"},{name=\"val1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d1\",shadowed=\"true\"\},{name=\"a\"},{name=\"val1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},{name=\"val2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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"
-- 
2.34.1

Intel Deutschland GmbH

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v14 1/2] gdb: add annotation in 'info locals' command for variables shadowing case
  2026-07-14 12:40 ` [PATCH v14 1/2] gdb: " Abdul Basit Ijaz
@ 2026-07-17 12:56   ` Andrew Burgess
  2026-07-17 14:14     ` Ijaz, Abdul B
  0 siblings, 1 reply; 7+ messages in thread
From: Andrew Burgess @ 2026-07-17 12:56 UTC (permalink / raw)
  To: Abdul Basit Ijaz, gdb-patches
  Cc: pedro, philippe.waroquiers, christina.schimpe, lsix, eliz,
	abdul.b.ijaz, guinevere


Hi, I have a last couple of super minor nits, see inline below.  If
you're happy to fix these as I suggest then I don't think you need to
post another version of the patch:

Approved-By: Andrew Burgess <aburgess@redhat.com>

But please wait for approval on patch #2 before merging both together.

Thanks,
Andrew

Abdul Basit Ijaz <abdul.b.ijaz@intel.com> writes:

> From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>
>
> 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 identify
> which variable belongs to the current scope.  So for such cases add location
> info to the innermost listed variables and for super block variables add
> "shadowed" annotation in the form of "<file.c:line, shadowed>".
>
> Suppose we have
>
> 1:int x = 3;
> 2:  {
> 3:    int x = 4;
> 4:    int y = 52;
> 5:    x = 99; /* break here */
> 6:  }
>
> Currently:
>
> (gdb) info locals
> x = 4
> y = 52
> x = 3
>
> After applying this patch, we obtain:
>
> (gdb) info locals
> x = 4  <file.c:3>
> y = 52
> x = 3  <file.c:1, shadowed>
>
> 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.
>
> 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 >= 1.73 so XFAIL is added accordingly.
>
> 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.
>
> 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.
>
> Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Co-Authored-By: Andrew Burgess <aburgess@redhat.com>
> ---
>  gdb/NEWS                                      |   5 +
>  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, 561 insertions(+), 23 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.ada/var_shadowing.exp
>  create mode 100644 gdb/testsuite/gdb.ada/var_shadowing/var_shadowing.adb
>  create mode 100755 gdb/testsuite/gdb.base/var-shadowing.c
>  create mode 100755 gdb/testsuite/gdb.base/var-shadowing.exp
>  create mode 100644 gdb/testsuite/gdb.base/var-shadowing2.c
>  create mode 100755 gdb/testsuite/gdb.rust/var_reuse.exp
>  create mode 100755 gdb/testsuite/gdb.rust/var_reuse.rs
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index ec9b5a33787..2c8adf05b43 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -201,6 +201,11 @@ 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.
>  
> +info locals
> +  GDB now shows the "shadowed" annotation and the location information
> +  for variables that are shadowed, or which are shadowing.
> +
> +

Double blank line here, there should be only one.

>  * Removed commands
>  
>  target ctf

> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index a698b2b8451..ee33bfdba13 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.
>  
> +@smallexample
> +@group
> +1: int x = 3;
> +2: @{
> +3:       int x = 4;
> +4:       int y = 52;
> +5:       x = 99; // breakpoint-line
> +6: @}
> +@end group
> +@group
> +(gdb) info locals
> +x = 4	<file.c:3>
> +y = 52
> +x = 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).  For such cases, its location for the
> +outermost scope is followed by @samp{shadowed}.  The location can
> +help to locate the instances of shadowed variables.  So,
> +location information is only added for shadowed variables.

I found the second sentence of this paragraph a little hard to parse.
Also the justification for the location seems unnecessary (to me).  How
about this instead:

  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/stack.h b/gdb/stack.h
> index ad2700b59a7..a03103b9847 100644
> --- a/gdb/stack.h
> +++ b/gdb/stack.h
> @@ -20,6 +20,8 @@
>  #ifndef GDB_STACK_H
>  #define GDB_STACK_H
>  
> +enum class var_shadowing;
> +
>  gdb::unique_xmalloc_ptr<char> find_frame_funname (const frame_info_ptr &frame,
>  						  enum language *funlang,
>  						  struct symbol **funcp);
> @@ -27,12 +29,32 @@ gdb::unique_xmalloc_ptr<char> find_frame_funname (const frame_info_ptr &frame,
>  typedef gdb::function_view<void (const char *print_name, struct symbol *sym)>
>       iterate_over_block_arg_local_vars_cb;
>  
> +typedef gdb::function_view<void (const char *print_name, struct symbol *sym,
> +				 var_shadowing shadow_status)>
> +    iterate_over_block_arg_local_vars_cb_printing;
> +

After the recent commit:

  commit ef5907287aec1dc7f5323c66042a4a26f275c5cf
  Date:   Mon Jul 13 15:06:35 2026 +0200

    [gdb] Convert template typedefs to using

the previous 'typedef' has been updated to 'using'.  Could you update
this to match please.

>  void iterate_over_block_arg_vars (const struct block *block,
>  				  iterate_over_block_arg_local_vars_cb cb);
>  
>  void iterate_over_block_local_vars (const struct block *block,
>  				    iterate_over_block_arg_local_vars_cb cb);
>  


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands
  2026-07-14 12:40 ` [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands Abdul Basit Ijaz
@ 2026-07-17 14:04   ` Andrew Burgess
  2026-07-17 14:34     ` Ijaz, Abdul B
  0 siblings, 1 reply; 7+ messages in thread
From: Andrew Burgess @ 2026-07-17 14:04 UTC (permalink / raw)
  To: Abdul Basit Ijaz, gdb-patches
  Cc: pedro, philippe.waroquiers, christina.schimpe, lsix, eliz,
	abdul.b.ijaz, guinevere


Hi!

Thanks for the update.  I have some feedback below.  There's one,
relating the use of lbasename which might need further discussion, I've
laid out my position inline below.

There's also a few other minor points, but nothing serious.

Abdul Basit Ijaz <abdul.b.ijaz@intel.com> writes:

> From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>
>
> For C/C++/Fortran languages GDB prints same name variable multiple times in
> case of variable shadowing and it is confusing for user to identify which
> variable belongs to the current scope.  So GDB now prints location information
> for shadowed variables and add 'shadowed' field also in '-stack-list-locals'
> and '-stack-list-variables' mi commands for super-block shadowed variable.
>
> Suppose we have test.c file
>
> 1:int x = 3;
> 2:  {
> 3:    int x = 4;
> 4:    int y = 5;
> 5:    x = 99; /* break here */
> 6:  }
>
> The "-stack-list-locals" and "-stack-list-variables" mi commands at the
> "break here" line gives the following output:
>
> Before the change:
>
> ~~~
> (gdb)
> -stack-list-locals 0
> ^done,locals=[name="x",name="y",name="x"]
> (gdb)
> -stack-list-locals 1
> ^done,locals=[{name="x",value="4"},{name="y",value="5"},{name="x",value="3"}]
> (gdb)
> -stack-list-locals 2
> ^done,locals=[{name="x",type="int",value="4"},{name="y",type="int",value="5"},{name="x",type="int",value="3"}]
> (gdb)
> -stack-list-variables 0
> ^done,variables=[{name="x"},{name="y"},{name="x"}]
> (gdb)
> -stack-list-variables 1
> ^done,variables=[{name="x",value="4"},{name="y",value="5"},{name="x",value="3"}]
> (gdb)
> -stack-list-variables 2
> ^done,variables=[{name="x",type="int",value="4"},{name="y",type="int",value="5"},{name="x",type="int",value="3"}]
> ~~~
>
> With this patch we obtain:
>
> ~~~
> (gdb)
> -stack-list-locals 0
> ^done,locals=[name="x",name="y",name="x"]
> (gdb)
> -stack-list-locals 1
> ^done,locals=[{name="x",value="4",filename="test.c",fullname="/home/src/test.c",line="5"},{name="y",value="5"},{name="x",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]

Line numbers are wrong, they should be "3" and "1".  I think many of the
line numbers in the below might be off too.

> (gdb)
> -stack-list-locals 2
> ^done,locals=[{name="x",type="int",value="4",filename="test.c",fullname="/home/src/test.c",line="5"},{name="y",type="int",value="5"},{name="x",type="int",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
> (gdb)
> -stack-list-variables 0
> ^done,variables=[{name="x",filename="test.c",fullname="/home/src/test.c",line="5"},{name="y"},{name="x",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
> (gdb)
> -stack-list-variables 1
> ^done,variables=[{name="x",value="4",filename="test.c",fullname="/home/src/test.c",line="4"},{name="y",value="5"},{name="x",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
> (gdb)
> -stack-list-variables 2
> ^done,variables=[{name="x",type="int",value="4",filename="test.c",fullname="/home/src/test.c",line="4"},{name="y",type="int",value="5"},{name="x",type="int",value="3",filename="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
> ~~~
> Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
> ---
>  gdb/NEWS                                  |   4 +
>  gdb/doc/gdb.texinfo                       |  22 ++++
>  gdb/mi/mi-cmd-stack.c                     | 144 ++++++++++++++++------
>  gdb/testsuite/gdb.mi/mi-var-shadowing.c   |  50 ++++++++
>  gdb/testsuite/gdb.mi/mi-var-shadowing.exp | 141 +++++++++++++++++++++
>  5 files changed, 324 insertions(+), 37 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.c
>  create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 2c8adf05b43..a3d0ad4ca11 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -594,6 +594,10 @@ multi-wp-addr in qSupported
>     multiple times, and the same mapping was being reused.  In all
>     other cases, this field will have the value 'false'.
>  
> +** GDB now shows the "shadowed", "filename", "fullname" and "line"
> +   fields in the output of '-stack-list-locals/variables' mi
> +   commands for variables that are shadowed, or which are shadowing.
> +
>  * Support for stabs debugging format and the a.out/dbx object format is
>    deprecated, and will be removed in GDB 18.
>  
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index ee33bfdba13..883859e69c4 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -35585,6 +35585,28 @@ If the @code{--skip-unavailable} option is specified, local variables
>  and arguments that are not available are not listed.  Partially
>  available arguments and local variables are still displayed, however.
>  
> +@smallexample
> +@group
> +1: int x = 3;
> +2: @{
> +3:       int x = 4;
> +4:       int y = 5;
> +5:       x = 99; // breakpoint-line
> +6: @}
> +@end group
> +@group
> +(gdb) -stack-list-variables 2
> +^done,variables=[@{name="x",type="int",value="4",
> +filename="name.c",fullname="/home/src/name.c",line="3"@},
> +@{name="y",type="int",value="5"@},@{name="x",type="int",
> +value="3",filename="name.c",fullname="/home/src/name.c",
> +line="1",shadowed="true"@}]
> +@end group
> +@end smallexample
> +
> +The location information and @samp{shadowed} attribute is only added
> +for shadowed variables (@pxref{shadowed variables}).

How about updating this paragraph to:

  The location information and @samp{shadowed} attribute are only added
  when variable shadowing is detected (@pxref{shadowed variables}).

the original text says that information is only added for shadowed
variables, but this isn't the full story, the shadowing copy also gets
information.  My updated text avoids this (I think).

> +
>  @subsubheading Example
>  
>  @smallexample
> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
> index 280a08d121e..d2e814750d5 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -560,6 +577,69 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
>  	}
>        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 ();
> +
> +	  /* Use lbasename instead of symtab_to_filename_for_display as the
> +	     latter defaults to relative path while basename is preferred
> +	     here.  */
> +	  uiout->field_string ("filename", lbasename (symtab->filename ()));

I strongly feel this is the wrong choice in this case.

I saw your comment about lbasename on the previous patch iteration, so I
didn't push about the issue on patch #1, the CLI output (though I do
think symtab_to_filename_for_display would be better).  While for the
CLI we do need to balance between full information and screen space use,
and avoid making the output too busy, in the MI we don't have that
concern.  The MI output is only intended for things like IDEs to parse,
not humans, so I think consistency with other similar fields is far more
important.  If an IDE want to only show the basename then it can always
do that, but the IDE cannot (reliably) recreate the relative path if
that's what it wants to display.  So for that reason, for me, I'd really
like you to consider using symtab_to_filename_for_display for the MI case.

> +	  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;

I think the return line needs less indentation.

>  }
>  
>  /* Print a list of the objects for the frame FI in a certain form,

> diff --git a/gdb/testsuite/gdb.mi/mi-var-shadowing.exp b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
> new file mode 100644
> index 00000000000..27326864083
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
> @@ -0,0 +1,141 @@
> +# 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 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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
> +set stack_test3_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\"\},\{name=\"a\",type=\"int\",value=\"102\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
> +set stack_test4_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"6\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d3\"\},\{name=\"val2\",type=\"unsigned int\",value=\"7\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d3\"\},\{name=\"val3\",type=\"unsigned int\",value=\"8\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d2\"\},\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d1\",shadowed=\"true\"\},\{name=\"a\",type=\"int\",value=\"103\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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\"\}\\\]"

There's a bunch of tests that fail for me here due to the use of:

  fullname=\"$srcdir/$subdir/$srcfile\"

I guess you must configure GDB with an absolute path to the configure
script, while I use a relative path.  As a result the expected pattern
for me becomes:

  fullname="/tmp/binutils-gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.mi/mi-var-shadowing.c"

which doesn't matter the absolute path that is actually emitted.
Looking around this seems to be a solved problem though, other tests
use:

  fullname=\"${fullname_syntax}$srcfile\"

and if I change all of the original fullname patterns to the updated
version, then the tests all pass for me.

If you update to use symtab_to_filename_for_display as I request then
you will also need to update the 'filename' patterns, but these can just
become:

  filename=\"[^\r\n\]+$srcfile\"

which will be fine.


If you're happy to make these changes, including the lbasename change,
then I think this would be fine:

Approved-By: Andrew Burgess <aburgess@redhat.com>

Otherwise, happy to discuss the lbasename stuff more as needed.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [PATCH v14 1/2] gdb: add annotation in 'info locals' command for variables shadowing case
  2026-07-17 12:56   ` Andrew Burgess
@ 2026-07-17 14:14     ` Ijaz, Abdul B
  0 siblings, 0 replies; 7+ messages in thread
From: Ijaz, Abdul B @ 2026-07-17 14:14 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches
  Cc: pedro, philippe.waroquiers, Schimpe, Christina, lsix, eliz, guinevere

Thanks Andrew for the feedback. I will fix them as per the feedback before mering.

>> +
>> +

Andrew>Double blank line here, there should be only one.
Abdul>Will remove one blank line.

>> +method, or inner class).  For such cases, its location for the 
>> +outermost scope is followed by @samp{shadowed}.  The location can 
>> +help to locate the instances of shadowed variables.  So, location 
>> +information is only added for shadowed variables.

Andrew>I found the second sentence of this paragraph a little hard to parse.
Andrew>Also the justification for the location seems unnecessary (to me).  How about this instead:

Abdul>Will use the description from your suggestion.

>> +typedef gdb::function_view<void (const char *print_name, struct symbol *sym,
>> +				 var_shadowing shadow_status)>
>> +    iterate_over_block_arg_local_vars_cb_printing;
>> +

Abdrew>After the recent commit:

Abdrew>  commit ef5907287aec1dc7f5323c66042a4a26f275c5cf
Abdrew>  Date:   Mon Jul 13 15:06:35 2026 +0200

Abdrew>    [gdb] Convert template typedefs to using

Abdrew>the previous 'typedef' has been updated to 'using'.  Could you update this to match please.

Abdul> Will fix it before merging.

Best Regards,
Abdul Basit


-----Original Message-----
From: Andrew Burgess <aburgess@redhat.com> 
Sent: Friday, July 17, 2026 2:57 PM
To: Ijaz, Abdul B <abdul.b.ijaz@intel.com>; gdb-patches@sourceware.org
Cc: pedro@palves.net; philippe.waroquiers@skynet.be; Schimpe, Christina <christina.schimpe@intel.com>; lsix@lancelotsix.com; eliz@gnu.org; Ijaz, Abdul B <abdul.b.ijaz@intel.com>; guinevere@redhat.com
Subject: Re: [PATCH v14 1/2] gdb: add annotation in 'info locals' command for variables shadowing case


Hi, I have a last couple of super minor nits, see inline below.  If you're happy to fix these as I suggest then I don't think you need to post another version of the patch:

Approved-By: Andrew Burgess <aburgess@redhat.com>

But please wait for approval on patch #2 before merging both together.

Thanks,
Andrew

Abdul Basit Ijaz <abdul.b.ijaz@intel.com> writes:

> From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>
>
> 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 
> identify which variable belongs to the current scope.  So for such 
> cases add location info to the innermost listed variables and for 
> super block variables add "shadowed" annotation in the form of "<file.c:line, shadowed>".
>
> Suppose we have
>
> 1:int x = 3;
> 2:  {
> 3:    int x = 4;
> 4:    int y = 52;
> 5:    x = 99; /* break here */
> 6:  }
>
> Currently:
>
> (gdb) info locals
> x = 4
> y = 52
> x = 3
>
> After applying this patch, we obtain:
>
> (gdb) info locals
> x = 4  <file.c:3>
> y = 52
> x = 3  <file.c:1, shadowed>
>
> 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.
>
> 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 >= 1.73 so XFAIL is added accordingly.
>
> 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.
>
> 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.
>
> Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Co-Authored-By: Andrew Burgess <aburgess@redhat.com>
> ---
>  gdb/NEWS                                      |   5 +
>  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, 561 insertions(+), 23 deletions(-)  create mode 
> 100644 gdb/testsuite/gdb.ada/var_shadowing.exp
>  create mode 100644 
> gdb/testsuite/gdb.ada/var_shadowing/var_shadowing.adb
>  create mode 100755 gdb/testsuite/gdb.base/var-shadowing.c
>  create mode 100755 gdb/testsuite/gdb.base/var-shadowing.exp
>  create mode 100644 gdb/testsuite/gdb.base/var-shadowing2.c
>  create mode 100755 gdb/testsuite/gdb.rust/var_reuse.exp
>  create mode 100755 gdb/testsuite/gdb.rust/var_reuse.rs
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index ec9b5a33787..2c8adf05b43 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -201,6 +201,11 @@ 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.
>  
> +info locals
> +  GDB now shows the "shadowed" annotation and the location 
> +information
> +  for variables that are shadowed, or which are shadowing.
> +
> +

Double blank line here, there should be only one.

>  * Removed commands
>  
>  target ctf

> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 
> a698b2b8451..ee33bfdba13 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.
>  
> +@smallexample
> +@group
> +1: int x = 3;
> +2: @{
> +3:       int x = 4;
> +4:       int y = 52;
> +5:       x = 99; // breakpoint-line
> +6: @}
> +@end group
> +@group
> +(gdb) info locals
> +x = 4	<file.c:3>
> +y = 52
> +x = 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).  For such cases, its location for the 
> +outermost scope is followed by @samp{shadowed}.  The location can 
> +help to locate the instances of shadowed variables.  So, location 
> +information is only added for shadowed variables.

I found the second sentence of this paragraph a little hard to parse.
Also the justification for the location seems unnecessary (to me).  How about this instead:

  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/stack.h b/gdb/stack.h index ad2700b59a7..a03103b9847 
> 100644
> --- a/gdb/stack.h
> +++ b/gdb/stack.h
> @@ -20,6 +20,8 @@
>  #ifndef GDB_STACK_H
>  #define GDB_STACK_H
>  
> +enum class var_shadowing;
> +
>  gdb::unique_xmalloc_ptr<char> find_frame_funname (const frame_info_ptr &frame,
>  						  enum language *funlang,
>  						  struct symbol **funcp);
> @@ -27,12 +29,32 @@ gdb::unique_xmalloc_ptr<char> find_frame_funname 
> (const frame_info_ptr &frame,  typedef gdb::function_view<void (const char *print_name, struct symbol *sym)>
>       iterate_over_block_arg_local_vars_cb;
>  
> +typedef gdb::function_view<void (const char *print_name, struct symbol *sym,
> +				 var_shadowing shadow_status)>
> +    iterate_over_block_arg_local_vars_cb_printing;
> +

After the recent commit:

  commit ef5907287aec1dc7f5323c66042a4a26f275c5cf
  Date:   Mon Jul 13 15:06:35 2026 +0200

    [gdb] Convert template typedefs to using

the previous 'typedef' has been updated to 'using'.  Could you update this to match please.

>  void iterate_over_block_arg_vars (const struct block *block,
>  				  iterate_over_block_arg_local_vars_cb cb);
>  
>  void iterate_over_block_local_vars (const struct block *block,
>  				    iterate_over_block_arg_local_vars_cb cb);
>  

Intel Deutschland GmbH

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands
  2026-07-17 14:04   ` Andrew Burgess
@ 2026-07-17 14:34     ` Ijaz, Abdul B
  0 siblings, 0 replies; 7+ messages in thread
From: Ijaz, Abdul B @ 2026-07-17 14:34 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches
  Cc: pedro, philippe.waroquiers, Schimpe, Christina, lsix, eliz, guinevere

Hi Andrew,

Thanks for the feedback and approval. I will update the changes you suggested including replacing lbasename with symtab_to_filename_for_display before merging.

>> -stack-list-locals 1
>> ^done,locals=[{name="x",value="4",filename="test.c",fullname="/home/sr
>> c/test.c",line="5"},{name="y",value="5"},{name="x",value="3",filename=
>> "test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]

Andrew>Line numbers are wrong, they should be "3" and "1".  I think many of the line numbers in the below might be off too.

Abdul> Right, will fix this and the below before mering.


>> +The location information and @samp{shadowed} attribute is only added 
>> +for shadowed variables (@pxref{shadowed variables}).

Andrew>How about updating this paragraph to:
Andrew>The location information and @samp{shadowed} attribute are only added
Andew> when variable shadowing is detected (@pxref{shadowed variables}).

Abdul> Will fix it before merging.

>> +	  uiout->field_string ("filename", lbasename (symtab->filename ()));

Andrew>I strongly feel this is the wrong choice in this case.

Andrew> The MI output is only intended for things like IDEs to parse, not humans, so I think consistency with other similar fields is far more important.  If an IDE want to only show the basename then it can always do that, but the IDE cannot (reliably) recreate the relative path if that's what it wants to display.  So for that reason, for me, I'd really like you to consider using symtab_to_filename_for_display for the MI case.

Abdul> Fine with me. I will replace lbasename to symtab_to_filename_for_display. As there was already complete path info available with the "fullname" so decided to use lbasename instead of it in the "filename".  But you are right its up to IDEs to parse and keep the MI output consistent.


>> +    return print_me;

Andrew> I think the return line needs less indentation

Abdul> Will fix before merging.


Andrew>There's a bunch of tests that fail for me here due to the use of:

Andrew>  fullname=\"$srcdir/$subdir/$srcfile\"

Andrew>I guess you must configure GDB with an absolute path to the configure script, while I use a relative path.  As a result the expected pattern for me becomes:

Abdul> Yes my testsuite folder is configured with absolute Path. Will test it with relative path also before merging.

Andrew>If you update to use symtab_to_filename_for_display as I request then you will also need to update the 'filename' patterns, but these can just
become:

Andrew>  filename=\"[^\r\n\]+$srcfile\"

Abdul> Thanks, will update it accordingly before merging.

Thanks & Best Regards
Abdul Basit

-----Original Message-----
From: Andrew Burgess <aburgess@redhat.com> 
Sent: Friday, July 17, 2026 4:05 PM
To: Ijaz, Abdul B <abdul.b.ijaz@intel.com>; gdb-patches@sourceware.org
Cc: pedro@palves.net; philippe.waroquiers@skynet.be; Schimpe, Christina <christina.schimpe@intel.com>; lsix@lancelotsix.com; eliz@gnu.org; Ijaz, Abdul B <abdul.b.ijaz@intel.com>; guinevere@redhat.com
Subject: Re: [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands


Hi!

Thanks for the update.  I have some feedback below.  There's one, relating the use of lbasename which might need further discussion, I've laid out my position inline below.

There's also a few other minor points, but nothing serious.

Abdul Basit Ijaz <abdul.b.ijaz@intel.com> writes:

> From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>
>
> For C/C++/Fortran languages GDB prints same name variable multiple 
> times in case of variable shadowing and it is confusing for user to 
> identify which variable belongs to the current scope.  So GDB now 
> prints location information for shadowed variables and add 'shadowed' field also in '-stack-list-locals'
> and '-stack-list-variables' mi commands for super-block shadowed variable.
>
> Suppose we have test.c file
>
> 1:int x = 3;
> 2:  {
> 3:    int x = 4;
> 4:    int y = 5;
> 5:    x = 99; /* break here */
> 6:  }
>
> The "-stack-list-locals" and "-stack-list-variables" mi commands at 
> the "break here" line gives the following output:
>
> Before the change:
>
> ~~~
> (gdb)
> -stack-list-locals 0
> ^done,locals=[name="x",name="y",name="x"]
> (gdb)
> -stack-list-locals 1
> ^done,locals=[{name="x",value="4"},{name="y",value="5"},{name="x",valu
> e="3"}]
> (gdb)
> -stack-list-locals 2
> ^done,locals=[{name="x",type="int",value="4"},{name="y",type="int",val
> ue="5"},{name="x",type="int",value="3"}]
> (gdb)
> -stack-list-variables 0
> ^done,variables=[{name="x"},{name="y"},{name="x"}]
> (gdb)
> -stack-list-variables 1
> ^done,variables=[{name="x",value="4"},{name="y",value="5"},{name="x",v
> alue="3"}]
> (gdb)
> -stack-list-variables 2
> ^done,variables=[{name="x",type="int",value="4"},{name="y",type="int",
> value="5"},{name="x",type="int",value="3"}]
> ~~~
>
> With this patch we obtain:
>
> ~~~
> (gdb)
> -stack-list-locals 0
> ^done,locals=[name="x",name="y",name="x"]
> (gdb)
> -stack-list-locals 1
> ^done,locals=[{name="x",value="4",filename="test.c",fullname="/home/sr
> c/test.c",line="5"},{name="y",value="5"},{name="x",value="3",filename=
> "test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]

Line numbers are wrong, they should be "3" and "1".  I think many of the line numbers in the below might be off too.

> (gdb)
> -stack-list-locals 2
> ^done,locals=[{name="x",type="int",value="4",filename="test.c",fullnam
> e="/home/src/test.c",line="5"},{name="y",type="int",value="5"},{name="
> x",type="int",value="3",filename="test.c",fullname="/home/src/test.c",
> line="2",shadowed="true"}]
> (gdb)
> -stack-list-variables 0
> ^done,variables=[{name="x",filename="test.c",fullname="/home/src/test.
> c",line="5"},{name="y"},{name="x",filename="test.c",fullname="/home/sr
> c/test.c",line="2",shadowed="true"}]
> (gdb)
> -stack-list-variables 1
> ^done,variables=[{name="x",value="4",filename="test.c",fullname="/home
> /src/test.c",line="4"},{name="y",value="5"},{name="x",value="3",filena
> me="test.c",fullname="/home/src/test.c",line="2",shadowed="true"}]
> (gdb)
> -stack-list-variables 2
> ^done,variables=[{name="x",type="int",value="4",filename="test.c",full
> name="/home/src/test.c",line="4"},{name="y",type="int",value="5"},{nam
> e="x",type="int",value="3",filename="test.c",fullname="/home/src/test.
> c",line="2",shadowed="true"}]
> ~~~
> Reviewed-By: Guinevere Larsen <guinevere@redhat.com>
> ---
>  gdb/NEWS                                  |   4 +
>  gdb/doc/gdb.texinfo                       |  22 ++++
>  gdb/mi/mi-cmd-stack.c                     | 144 ++++++++++++++++------
>  gdb/testsuite/gdb.mi/mi-var-shadowing.c   |  50 ++++++++
>  gdb/testsuite/gdb.mi/mi-var-shadowing.exp | 141 +++++++++++++++++++++
>  5 files changed, 324 insertions(+), 37 deletions(-)  create mode 
> 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.c
>  create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 2c8adf05b43..a3d0ad4ca11 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -594,6 +594,10 @@ multi-wp-addr in qSupported
>     multiple times, and the same mapping was being reused.  In all
>     other cases, this field will have the value 'false'.
>  
> +** GDB now shows the "shadowed", "filename", "fullname" and "line"
> +   fields in the output of '-stack-list-locals/variables' mi
> +   commands for variables that are shadowed, or which are shadowing.
> +
>  * Support for stabs debugging format and the a.out/dbx object format is
>    deprecated, and will be removed in GDB 18.
>  
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 
> ee33bfdba13..883859e69c4 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -35585,6 +35585,28 @@ If the @code{--skip-unavailable} option is 
> specified, local variables  and arguments that are not available are 
> not listed.  Partially  available arguments and local variables are still displayed, however.
>  
> +@smallexample
> +@group
> +1: int x = 3;
> +2: @{
> +3:       int x = 4;
> +4:       int y = 5;
> +5:       x = 99; // breakpoint-line
> +6: @}
> +@end group
> +@group
> +(gdb) -stack-list-variables 2
> +^done,variables=[@{name="x",type="int",value="4",
> +filename="name.c",fullname="/home/src/name.c",line="3"@},
> +@{name="y",type="int",value="5"@},@{name="x",type="int",
> +value="3",filename="name.c",fullname="/home/src/name.c",
> +line="1",shadowed="true"@}]
> +@end group
> +@end smallexample
> +
> +The location information and @samp{shadowed} attribute is only added 
> +for shadowed variables (@pxref{shadowed variables}).

How about updating this paragraph to:

  The location information and @samp{shadowed} attribute are only added
  when variable shadowing is detected (@pxref{shadowed variables}).

the original text says that information is only added for shadowed variables, but this isn't the full story, the shadowing copy also gets information.  My updated text avoids this (I think).

> +
>  @subsubheading Example
>  
>  @smallexample
> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c index 
> 280a08d121e..d2e814750d5 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -560,6 +577,69 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
>  	}
>        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 ();
> +
> +	  /* Use lbasename instead of symtab_to_filename_for_display as the
> +	     latter defaults to relative path while basename is preferred
> +	     here.  */
> +	  uiout->field_string ("filename", lbasename (symtab->filename ()));

I strongly feel this is the wrong choice in this case.

I saw your comment about lbasename on the previous patch iteration, so I didn't push about the issue on patch #1, the CLI output (though I do think symtab_to_filename_for_display would be better).  While for the CLI we do need to balance between full information and screen space use, and avoid making the output too busy, in the MI we don't have that concern.  The MI output is only intended for things like IDEs to parse, not humans, so I think consistency with other similar fields is far more important.  If an IDE want to only show the basename then it can always do that, but the IDE cannot (reliably) recreate the relative path if that's what it wants to display.  So for that reason, for me, I'd really like you to consider using symtab_to_filename_for_display for the MI case.

> +	  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;

I think the return line needs less indentation.

>  }
>  
>  /* Print a list of the objects for the frame FI in a certain form,

> diff --git a/gdb/testsuite/gdb.mi/mi-var-shadowing.exp 
> b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
> new file mode 100644
> index 00000000000..27326864083
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
> @@ -0,0 +1,141 @@
> +# 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 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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
> +set stack_test3_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\"\},\{name=\"a\",type=\"int\",value=\"102\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
> +set stack_test4_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"6\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d3\"\},\{name=\"val2\",type=\"unsigned int\",value=\"7\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d3\"\},\{name=\"val3\",type=\"unsigned int\",value=\"8\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d2\"\},\{name=\"val1\",type=\"unsigned int\",value=\"5\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d1\",shadowed=\"true\"\},\{name=\"a\",type=\"int\",value=\"103\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",filename=\"$srcfile\",fullname=\"$srcdir/$subdir/$srcfile\",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\"\}\\\]"

There's a bunch of tests that fail for me here due to the use of:

  fullname=\"$srcdir/$subdir/$srcfile\"

I guess you must configure GDB with an absolute path to the configure script, while I use a relative path.  As a result the expected pattern for me becomes:

  fullname="/tmp/binutils-gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.mi/mi-var-shadowing.c"

which doesn't matter the absolute path that is actually emitted.
Looking around this seems to be a solved problem though, other tests
use:

  fullname=\"${fullname_syntax}$srcfile\"

and if I change all of the original fullname patterns to the updated version, then the tests all pass for me.

If you update to use symtab_to_filename_for_display as I request then you will also need to update the 'filename' patterns, but these can just
become:

  filename=\"[^\r\n\]+$srcfile\"

which will be fine.


If you're happy to make these changes, including the lbasename change, then I think this would be fine:

Approved-By: Andrew Burgess <aburgess@redhat.com>

Otherwise, happy to discuss the lbasename stuff more as needed.

Thanks,
Andrew

Intel Deutschland GmbH

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-17 14:35 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-14 12:40 [PATCH v14 0/2] add annotation in 'info locals' command for variables shadowing case Abdul Basit Ijaz
2026-07-14 12:40 ` [PATCH v14 1/2] gdb: " Abdul Basit Ijaz
2026-07-17 12:56   ` Andrew Burgess
2026-07-17 14:14     ` Ijaz, Abdul B
2026-07-14 12:40 ` [PATCH v14 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands Abdul Basit Ijaz
2026-07-17 14:04   ` Andrew Burgess
2026-07-17 14:34     ` Ijaz, Abdul B

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox