diff --git a/gdb/NEWS b/gdb/NEWS
index 730d68b5dcd..63df5feaa07 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -113,6 +113,10 @@ qXfer:threads:read
    multiple times, and the same mapping was being reused.  In all
    other cases, this field will have the value 'false'.
 
+** GDB now shows "shadowed", "file" and "line" fields in the output
+   of '-stack-list-locals/variables' mi commands for variables
+   shadowing case.
+
 * 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 129baf93f77..9b4ec5938d1 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -35184,6 +35184,24 @@ 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
+1: int x = 3;
+2: @{
+3:       int x = 4;
+4:       int y = 5;
+4:       x = 99; // breakpoint-line
+5: @}
+(gdb) -stack-list-variables 2
+^done,variables=[@{name="x",type="int",value="4",file="/home/src/name.c",line="3"@},@{name="y",type="int",value="5",file="/home/src/name.c",line="4"@},@{name="x",type="int",value="3",file="/home/src/name.c",line="1",shadowed="true"@}]
+@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} attribute.  The
+location can help to locate the instances of shadowed variables.  So,
+location information is only added for shadowed variables.
+
 @subsubheading Example
 
 @smallexample
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 05bc0ffce27..ec4d89cb626 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -34,6 +34,7 @@
 #include <optional>
 #include "gdbsupport/gdb-safe-ctype.h"
 #include "inferior.h"
+#include "gdbsupport/unordered_map.h"
 
 enum what_to_list { locals, arguments, all };
 
@@ -492,7 +493,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;
 
@@ -521,6 +524,19 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
     tuple_emitter.emplace (uiout, nullptr);
 
   string_file stb;
+  const char *name = arg->sym->print_name ();
+  /* To distinguish innermost variable from the rest in the shadowed_vars
+     this boolen 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::RETURN && shadowed && already_printed)
+    return;
 
   stb.puts (arg->sym->print_name ());
   if (arg->entry_kind == print_entry_values_only)
@@ -562,6 +578,59 @@ 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))
+    {
+      uiout->field_string ("file", arg->sym->owner.symtab->filename);
+      uiout->field_unsigned ("line", arg->sym->m_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)
+{
+  int print_me = false;
+
+  switch (sym->aclass ())
+    {
+      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,
@@ -575,9 +644,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);
 
@@ -598,42 +668,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->aclass ())
+	  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 symbol *sym2;
 	      struct frame_arg arg, entryarg;
@@ -668,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..d01717099e1
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.c
@@ -0,0 +1,48 @@
+/* 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;	/* 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;
+} /* 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..7114dde56f0
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
@@ -0,0 +1,141 @@
+# 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 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 -1
+}
+
+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\",file=\"$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\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
+set stack_test3_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"5\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",file=\"$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\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d1\",shadowed=\"true\"\}\\\]"
+set stack_test4_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"6\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d3\"\},\{name=\"val2\",type=\"unsigned int\",value=\"7\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d3\"\},\{name=\"val3\",type=\"unsigned int\",value=\"8\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d2\"\},\{name=\"val1\",type=\"unsigned int\",value=\"5\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d1\",shadowed=\"true\"\},\{name=\"a\",type=\"int\",value=\"103\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",file=\"$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\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\"},{name=\"val2\",file=\"$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\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\"},{name=\"val2\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},{name=\"val2\",file=\"$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\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d3\"},{name=\"val2\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d3\"},{name=\"val3\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d2\"},{name=\"val1\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d2\",shadowed=\"true\"\},\{name=\"val2\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val2_d2\",shadowed=\"true\"\},\{name=\"val3\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val3_d1\",shadowed=\"true\"\},{name=\"a\"},{name=\"val1\",file=\"$srcdir/$subdir/$srcfile\",line=\"$val1_d1\",shadowed=\"true\"\},{name=\"val2\",file=\"$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"
