Hi Abdul,
Thanks for continuing to work on this. Sorry that it has taken so many
iterations to move this forward.
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 = 42;
> 2: {
> 3: int x = 99;
> 4: int y = 52;
> 5: x = 99; /* break here */
> 6: }
>
> Currently:
>
> (gdb) info locals
> x = 99
> x = 42
> y = 52
>
> After applying this patch, we obtain:
>
> (gdb) info locals
> x = 99 <file.c:3>
> y = 52
> x = 42 <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.
>
> Co-Authored-By: Andrew Burgess <aburgess@redhat.com>
> ---
> gdb/NEWS | 4 +
> gdb/doc/gdb.texinfo | 20 ++++
> gdb/language.c | 20 ++++
> gdb/language.h | 19 ++++
> gdb/printcmd.c | 28 +++++-
> gdb/stack.c | 83 ++++++++++++++---
> gdb/stack.h | 3 +-
> gdb/testsuite/gdb.ada/var_shadowing.exp | 39 ++++++++
> .../gdb.ada/var_shadowing/var_shadowing.adb | 30 ++++++
> gdb/testsuite/gdb.base/var-shadowing.c | 49 ++++++++++
> 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/tracepoint.c | 3 +-
> gdb/value.h | 4 +-
> 17 files changed, 464 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 6a557bb4af9..730d68b5dcd 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -56,6 +56,10 @@ info sharedlibrary
> command are now for the full memory range allocated to the shared
> library.
>
> +info locals
> + GDB now shows the "shadowed" annotation and the location information in
> + the output of this command for variables shadowing case.
> +
> * Python API
>
> ** New class gdb.Color for dealing with colors.
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index e034ac53295..129baf93f77 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -8942,6 +8942,26 @@ 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
> +1: int x = 3;
> +2: @{
> +3: int x = 4;
> +4: int y = 52;
> +5: x = 99; // breakpoint-line
> +6: @}
> +(gdb) info locals
> +x = 4 <file.c:3>
> +y = 52
> +x = 3 <file.c:1, shadowed>
> +@end smallexample
> +
> +A variable is 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 4208c23b723..fd204acb29c 100644
> --- a/gdb/language.c
> +++ b/gdb/language.c
> @@ -1093,6 +1093,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::RETURN;
> +
> + default:
> + return lang_vars_shadowing::NONE;
> + }
> +}
> +
> /* Initialize the language routines. */
>
> void _initialize_language ();
> diff --git a/gdb/language.h b/gdb/language.h
> index 5e9599df25c..f867c25a185 100644
> --- a/gdb/language.h
> +++ b/gdb/language.h
> @@ -83,6 +83,21 @@ enum macro_expansion
> macro_expansion_no, macro_expansion_c
> };
>
> +/* In the case of variable shadowing if extra information should be
> + printed for the current language is compared against it. */
> +
> +enum class lang_vars_shadowing
> + {
> + /* Adds shadowed information for such variables. */
> + PRINT,
> +
> + /* Does not print shadowed variables. */
> + RETURN,
I think 'RETURN' is the wrong name here. This name seems to reflect
more what we do when we see this, rather than how we should handle the
shadowed variable. I wonder if something like 'HIDE' would make more
sense?
> +
> + /* Print variables without shadow information. */
> + NONE,
> + };
> +
>
> /* Per architecture (OS/ABI) language information. */
>
> @@ -825,6 +840,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 3cd84204574..e0d20793ecb 100644
> --- a/gdb/printcmd.c
> +++ b/gdb/printcmd.c
> @@ -56,6 +56,7 @@
> #include <optional>
> #include "gdbsupport/gdb-safe-ctype.h"
> #include "inferior.h"
> +#include "include/libiberty.h"
This is not needed. libiberty.h is included via
gdbsupport/common-defs.h, which is included into every file compiled
from the gdb/ directory.
>
> /* Chain containing all defined memory-tag subcommands. */
>
> @@ -2331,7 +2332,8 @@ clear_dangling_display_expressions (struct objfile *objfile)
> void
> print_variable_and_value (const char *name, struct symbol *var,
> const frame_info_ptr &frame,
> - struct ui_file *stream, int indent)
> + struct ui_file *stream, int indent,
> + bool shadowed, bool printed)
I noticed that these two function arguments are not commented anywhere.
So then I started thinking about what should be written about them, and
what the different values mean.
Here's what I came up with:
shadowed = false, printed = false -- this is a normal, non shadowed,
variable.
shadowed = true, printed = true -- this is an earlier shadowed
variable.
shadowed = true, printed = false -- this is a variable that is shadowing
an earlier variable with the
same name.
shadowed = false, printed = true -- I think this state is invalid.
So then I thought, maybe we should have an assert that the invalid state
isn't seen.
But then I thought, maybe we should merge the two arguments into a
single enum, like:
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
};
Now there's no invalid state that can be passed, and the update to the
comment on print_variable_and_value becomes much easier, just:
<ARG_NAME> indicates the shadowing state of this variable.
> {
>
> if (!name)
> @@ -2344,6 +2346,7 @@ print_variable_and_value (const char *name, struct symbol *var,
> {
> struct value *val;
> struct value_print_options opts;
> + const char *file_name = lbasename (var->owner.symtab->filename);
>
> /* READ_VAR_VALUE needs a block in order to deal with non-local
> references (i.e. to handle nested functions). In this context, we
> @@ -2353,6 +2356,29 @@ print_variable_and_value (const char *name, struct symbol *var,
> get_user_print_options (&opts);
> opts.deref_ref = true;
> common_val_print_checked (val, stream, indent, &opts, current_language);
> +
> + /* Print <%line, shadowed> after the variable value only when it is variable
> + shadowing case. */
> + if (shadowed)
> + {
> + if (var->line () > 0)
> + {
> + gdb_printf ("\t<%ps:%ps%ps>",
> + styled_string (file_name_style.style (), file_name),
> + styled_string (line_number_style.style (),
> + pulongest (var->line ())),
> + styled_string (metadata_style.style (),
> + printed ? ", shadowed" : ""));
> + }
Tom mentioned removing these { ... } in the V9 review. It's possible
that the guidelines you found are a little unclear; talking about
lines. It's really statements that counts, a single statement means no
'{ ... }'. The confusion is that we also include comments, which
obviously are not statements. So, this would not be GDB style:
if (some_condition)
/* This is a comment. */
do_something ();
Even though that is valid C++ and the 'do_something ()' is the body of
the 'if'. In this case GDB style is to add '{ ... }'. Only if you have
a single statement, and no comment, should the '{ ... }' be removed,
like:
if (some_condition)
do_something ();
But it doesn't matter how many lines the 'do_something ()' spans over.
> + else
> + {
> + gdb_printf ("\t<%ps:No line number information available%ps>",
> + styled_string (file_name_style.style (), file_name),
> + styled_string (metadata_style.style (),
> + printed ? ", shadowed" : ""));
If you do 'set style metadata background blue' the you will notice that
the ", " is also styled, which isn't ideal. Locally I changed this too:
/* Print <%line, shadowed> after the variable value only when it is variable
shadowing case. */
if (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_printf (&out, ", %ps",
styled_string (metadata_style.style (),
printed ? "shadowed" : ""));
gdb_puts (">", &out);
gdb_puts (out.c_str ());
}
which ensures that only those parts that should be styled, are actually
styled. All the tests still pass with this change, it's just the
styling that's updated.
> + }
> +
> + }
> }
> catch (const gdb_exception_error &except)
> {
> diff --git a/gdb/stack.c b/gdb/stack.c
> index 6c4e0cd9320..5f4cd46f2eb 100644
> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -58,6 +58,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. */
> @@ -2213,8 +2214,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))
> {
> @@ -2241,12 +2243,13 @@ iterate_over_block_locals (const struct block *b,
> }
> }
>
> -/* Iterate over all the local variables in block B, including all its
> - superblocks, stopping when the top-level block is reached. */
> +/* Call iterate_over_block_locals, passing through CB for BLOCK, and every
> + superblock up to, and including, the enclosing function block. */
>
> -void
> -iterate_over_block_local_vars (const struct block *block,
> - iterate_over_block_arg_local_vars_cb cb)
> +static void
> +iterate_over_local_vars_to_function_block
> + (const struct block *block,
> + gdb::function_view <void (const char *, struct symbol *)> cb)
> {
> while (block)
> {
> @@ -2260,6 +2263,61 @@ iterate_over_block_local_vars (const struct block *block,
> }
> }
>
> +/* Iterate over all the local variables in block B, including all its
> + superblocks, stopping when the top-level block is reached. */
> +
> +void
> +iterate_over_block_local_vars (const struct block *block,
> + iterate_over_block_arg_local_vars_cb cb)
> +{
In his v9 review Tom wrote:
I think the special shadowing logic should not be put in a generic
iteration function, but instead handled by the callers that care about
this detail. Is there a reason this couldn't be done?
See:
https://inbox.sourceware.org/gdb-patches/87v7rxfbcb.fsf@tromey.com
I wasn't entirely sure what he meant here, but this did get me thinking
more about other users of iterate_over_block_local_vars. Notice in the
rust case we no longer invoke `cb`. This means that there will be a
change of behaviour for those users, which I think right now, is just
tracing. I have no idea if this change of behaviour is important or
not... but it does worry me.
As a crude experiment I restored the old version of
iterate_over_block_local_vars, and renamed this version to
iterate_over_block_local_vars_for_printing. There's a bunch of minor
fallout that needs handling as a result, but this means that only the
printing logic sees the change in behaviour, which is probably the safer
choice.
I haven't tried it, but iterate_over_block_local_vars_for_printing can
probably be updated to make use of iterate_over_block_local_vars if that
is being brought back.
I'd be interested to hear your thoughts, do you think the change to the
tracing code will be OK? Or do you think we should try to maintain the
existing behaviour?
Thanks,
Andrew
@@ -56,6 +56,10 @@ info sharedlibrary
command are now for the full memory range allocated to the shared
library.
+info locals
+ GDB now shows the "shadowed" annotation and the location information in
+ the output of this command for variables shadowing case.
+
* Python API
** New class gdb.Color for dealing with colors.
@@ -8942,6 +8942,26 @@ 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
+1: int x = 3;
+2: @{
+3: int x = 4;
+4: int y = 52;
+5: x = 99; // breakpoint-line
+6: @}
+(gdb) info locals
+x = 4 <file.c:3>
+y = 52
+x = 3 <file.c:1, shadowed>
+@end smallexample
+
+A variable is 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).
@@ -1093,6 +1093,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::RETURN;
+
+ default:
+ return lang_vars_shadowing::NONE;
+ }
+}
+
/* Initialize the language routines. */
void _initialize_language ();
@@ -83,6 +83,21 @@ enum macro_expansion
macro_expansion_no, macro_expansion_c
};
+/* In the case of variable shadowing if extra information should be
+ printed for the current language is compared against it. */
+
+enum class lang_vars_shadowing
+ {
+ /* Adds shadowed information for such variables. */
+ PRINT,
+
+ /* Does not print shadowed variables. */
+ RETURN,
+
+ /* Print variables without shadow information. */
+ NONE,
+ };
+
/* Per architecture (OS/ABI) language information. */
@@ -825,6 +840,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
@@ -56,6 +56,7 @@
#include <optional>
#include "gdbsupport/gdb-safe-ctype.h"
#include "inferior.h"
+#include "include/libiberty.h"
/* Chain containing all defined memory-tag subcommands. */
@@ -2331,7 +2332,8 @@ clear_dangling_display_expressions (struct objfile *objfile)
void
print_variable_and_value (const char *name, struct symbol *var,
const frame_info_ptr &frame,
- struct ui_file *stream, int indent)
+ struct ui_file *stream, int indent,
+ bool shadowed, bool printed)
{
if (!name)
@@ -2344,6 +2346,7 @@ print_variable_and_value (const char *name, struct symbol *var,
{
struct value *val;
struct value_print_options opts;
+ const char *file_name = lbasename (var->owner.symtab->filename);
/* READ_VAR_VALUE needs a block in order to deal with non-local
references (i.e. to handle nested functions). In this context, we
@@ -2353,6 +2356,29 @@ print_variable_and_value (const char *name, struct symbol *var,
get_user_print_options (&opts);
opts.deref_ref = true;
common_val_print_checked (val, stream, indent, &opts, current_language);
+
+ /* Print <%line, shadowed> after the variable value only when it is variable
+ shadowing case. */
+ if (shadowed)
+ {
+ if (var->line () > 0)
+ {
+ gdb_printf ("\t<%ps:%ps%ps>",
+ styled_string (file_name_style.style (), file_name),
+ styled_string (line_number_style.style (),
+ pulongest (var->line ())),
+ styled_string (metadata_style.style (),
+ printed ? ", shadowed" : ""));
+ }
+ else
+ {
+ gdb_printf ("\t<%ps:No line number information available%ps>",
+ styled_string (file_name_style.style (), file_name),
+ styled_string (metadata_style.style (),
+ printed ? ", shadowed" : ""));
+ }
+
+ }
}
catch (const gdb_exception_error &except)
{
@@ -58,6 +58,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. */
@@ -2213,8 +2214,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))
{
@@ -2241,12 +2243,13 @@ iterate_over_block_locals (const struct block *b,
}
}
-/* Iterate over all the local variables in block B, including all its
- superblocks, stopping when the top-level block is reached. */
+/* Call iterate_over_block_locals, passing through CB for BLOCK, and every
+ superblock up to, and including, the enclosing function block. */
-void
-iterate_over_block_local_vars (const struct block *block,
- iterate_over_block_arg_local_vars_cb cb)
+static void
+iterate_over_local_vars_to_function_block
+ (const struct block *block,
+ gdb::function_view <void (const char *, struct symbol *)> cb)
{
while (block)
{
@@ -2260,6 +2263,61 @@ iterate_over_block_local_vars (const struct block *block,
}
}
+/* Iterate over all the local variables in block B, including all its
+ superblocks, stopping when the top-level block is reached. */
+
+void
+iterate_over_block_local_vars (const struct block *block,
+ iterate_over_block_arg_local_vars_cb 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_local_vars_to_function_block
+ (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_local_vars_to_function_block
+ (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 ();
+
+ /* 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 && shadowed)
+ cb (print_name, sym, true, already_printed);
+ /* 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::RETURN && shadowed && already_printed)
+ {
+ /* Nothing. */
+ }
+ else
+ cb (print_name, sym, false, false);
+ });
+}
+
/* Data to be passed around in the calls to the locals and args
iterators. */
@@ -2272,14 +2330,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, bool shadowed,
+ bool printed);
};
/* 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,
+ bool shadowed, bool printed)
{
frame_info_ptr frame;
@@ -2299,7 +2359,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, shadowed,
+ printed);
values_printed = 1;
}
@@ -2477,7 +2538,7 @@ iterate_over_block_arg_vars (const struct block *b,
struct symbol *sym2
= lookup_symbol_search_name (sym->search_name (),
b, SEARCH_VAR_DOMAIN).symbol;
- cb (sym->print_name (), sym2);
+ cb (sym->print_name (), sym2, false, false);
}
}
}
@@ -24,7 +24,8 @@ gdb::unique_xmalloc_ptr<char> find_frame_funname (const frame_info_ptr &frame,
enum language *funlang,
struct symbol **funcp);
-typedef gdb::function_view<void (const char *print_name, struct symbol *sym)>
+typedef gdb::function_view<void (const char *print_name, struct symbol *sym,
+ bool shadowed, bool printed)>
iterate_over_block_arg_local_vars_cb;
void iterate_over_block_arg_vars (const struct block *block,
new file mode 100644
@@ -0,0 +1,39 @@
+# Copyright 2023-2025 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 -1
+}
+
+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"
new file mode 100644
@@ -0,0 +1,30 @@
+-- Copyright 2023-2025 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;
new file mode 100755
@@ -0,0 +1,49 @@
+/* Copyright (C) 2023-2025 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>
+
+void
+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;
+} /* bp for locals 5 */
+
+int
+main (void)
+{
+ shadowing ();
+ return 0;
+}
new file mode 100755
@@ -0,0 +1,92 @@
+# Copyright 2023-2025 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 -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+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"
new file mode 100644
@@ -0,0 +1,16 @@
+/* Copyright (C) 2023-2025 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;
@@ -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>)?"
}
new file mode 100755
@@ -0,0 +1,36 @@
+# Copyright 2023-2025 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 -1
+}
+
+set line [gdb_get_line_number "set breakpoint here"]
+if {![runto ${srcfile}:$line]} {
+ untested "could not run to breakpoint"
+ return -1
+}
+
+# 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"
new file mode 100755
@@ -0,0 +1,20 @@
+// Copyright (C) 2023-2025 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
+}
@@ -1046,7 +1046,8 @@ collection_list::add_local_symbols (struct gdbarch *gdbarch, CORE_ADDR pc,
int count = 0;
auto do_collect_symbol = [&] (const char *print_name,
- struct symbol *sym)
+ struct symbol *sym,
+ bool shadowed, bool printed)
{
collect_symbol (sym, gdbarch, frame_regno,
frame_offset, pc, trace_string);
@@ -1561,7 +1561,9 @@ extern void print_variable_and_value (const char *name,
struct symbol *var,
const frame_info_ptr &frame,
struct ui_file *stream,
- int indent);
+ int indent,
+ bool shadowed,
+ bool printed);
extern void typedef_print (struct type *type, struct symbol *news,
struct ui_file *stream);