[binutils-gdb] [gdb/exp] Fix ignoring of incorrect namespace prefix

Tom de Vries via Gdb-cvs <[email protected]> Tue, 14 Jul 2026 08:43:25 +0000 (GMT)
Newsgroups gmane.comp.gdb.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3D6f6e739987d6=
f0b904aa8e6d6bc7cc4e3d71d408

commit 6f6e739987d6f0b904aa8e6d6bc7cc4e3d71d408
Author: Tom de Vries <[email protected]>
Date:   Tue Jul 14 10:43:14 2026 +0200

    [gdb/exp] Fix ignoring of incorrect namespace prefix
   =20
    Consider test.c, compiled to a.out using "g++ -g test.c":
    ...
         1  namespace mod_a { int xxx =3D 10; }
         2  namespace mod_b { using namespace mod_a;
         3                    int yyy =3D 20; }
         4  int main (void) {
         5    using namespace mod_b;
         6    void (xxx + yyy);
         7    return 0;
         8  }
    ...
   =20
    When trying to print the value of non-existent variable mod_a::yyy, we =
get:
    ...
    $ gdb -q -batch a.out -ex start -ex "print mod_a::yyy"
      ...
    Temporary breakpoint 1, main () at test.c:7
    7         return 0;
    $1 =3D 20
    ...
   =20
    The problem is in cp_lookup_symbol_via_imports, where we decide that the
    "using namespace mod_b" from main is applicable in scope mod_a.
   =20
    More concretely, cp_lookup_symbol_via_imports is called with:
    - scope =3D=3D "mod_a",
    - name =3D=3D "yyy", and
    - block.m_function.m_name =3D=3D "main()",
    and when looking at "using namespace mod_b":
    ...
    (gdb) p *current
    $12 =3D {import_src =3D 0x344018c "mod_b", import_dest =3D 0x1b477a0 "",
           alias =3D 0x0, declaration =3D 0x0, next =3D 0x0, decl_line =3D =
5,
           searched =3D 0, excludes =3D {0x0}}
    ...
    we hit "directive_match =3D true" because strlen (current->import_dest)=
 =3D=3D 0.
   =20
    Fix this by being more strict in the calculation of directive_match:
    ...
              if (len =3D=3D 0)
    -           directive_match =3D true;
    +           {
    +             const char *current_scope =3D (block->function_block () !=
=3D nullptr
    +                                          ? block->scope ()
    +                                          : nullptr /* Don't know.  */=
);
    +             directive_match =3D (current_scope !=3D nullptr
    +                                ? streq (scope, current_scope)
    +                                : true /* Assume there's a match.  */);
    +           }
    ...
    which gets us:
    - current_scope =3D=3D "", and
    - directive_match =3D=3D false,
    because scope =3D=3D "mod_a", so streq (scope, current_scope) =3D=3D fa=
lse.
   =20
    As is clear from the code, in case we don't know the current scope, we =
assume
    there's a match.  This may be harmless, or this may describe a cornerca=
se we
    haven't run into yet.  If so, it's a pre-existing issue.
   =20
    The new test-case contains regression tests for:
    - PR34051, and
    - PR34034 for which it contains a kfail.
   =20
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=3D34051

Diff:
---
 gdb/cp-namespace.c                 |  9 ++++-
 gdb/testsuite/gdb.cp/nsusing-2.cc  | 62 +++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.cp/nsusing-2.exp | 75 ++++++++++++++++++++++++++++++++++=
++++
 3 files changed, 145 insertions(+), 1 deletion(-)

diff --git a/gdb/cp-namespace.c b/gdb/cp-namespace.c
index 87c491d821b..17c4d40e294 100644
--- a/gdb/cp-namespace.c
+++ b/gdb/cp-namespace.c
@@ -449,7 +449,14 @@ cp_lookup_symbol_via_imports (const char *scope,
       if (search_parents)
 	{
 	  if (len =3D=3D 0)
-	    directive_match =3D true;
+	    {
+	      const char *current_scope =3D (block->function_block () !=3D nullptr
+					   ? block->scope ()
+					   : nullptr /* Don't know.  */);
+	      directive_match =3D (current_scope !=3D nullptr
+				 ? streq (scope, current_scope)
+				 : true /* Assume there's a match.  */);
+	    }
 	  else
 	    directive_match =3D (startswith (scope, current->import_dest)
 			       && (scope[len] =3D=3D ':'
diff --git a/gdb/testsuite/gdb.cp/nsusing-2.cc b/gdb/testsuite/gdb.cp/nsusi=
ng-2.cc
new file mode 100644
index 00000000000..226bda3e101
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nsusing-2.cc
@@ -0,0 +1,62 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 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/>.  =
*/
+
+/* C++ variant of the Fortran example from PR34034.  */
+
+namespace mod_a {
+  int xxx =3D 10;
+}
+
+namespace mod_b {
+  using namespace mod_a;
+  int yyy =3D 20;
+}
+
+static void foo () {}
+
+int
+main (void)
+{
+  foo ();	/* main-entry.  */
+
+  { /* Variant 1: using block is stop block, using block is not function b=
lock.  */
+    using namespace mod_b;
+    void (xxx + yyy);
+    foo ();	/* main-1.  */
+  }
+
+  { /* Variant 2: using block is super block of stop block, using block is=
 not function block.  */
+    using namespace mod_b;
+    {
+      void (xxx + yyy);
+      foo ();	/* main-2.  */
+    }
+  }
+
+  using namespace mod_b;
+
+  { /* Variant 3: using block is super block of stop block, using block is=
 function block.  */
+    void (xxx + yyy);
+    foo ();	/* main-3.  */
+  }
+
+  /* Variant 4: using block is stop block, using block is function block. =
 */
+  void (xxx + yyy);
+  foo ();	/* main-4.  */
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/nsusing-2.exp b/gdb/testsuite/gdb.cp/nsus=
ing-2.exp
new file mode 100644
index 00000000000..65e685d0d32
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nsusing-2.exp
@@ -0,0 +1,75 @@
+# Copyright 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/>.
+
+# Test recursive "using namespace".  Regression test for PR34034 and PR340=
51.
+
+standard_testfile .cc
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
+	 {debug c++}]} {
+    return
+}
+
+with_test_prefix pre-main {
+    gdb_test "print mod_a::xxx" " =3D 10"
+    gdb_test "print mod_b::yyy" " =3D 20"
+
+    # Namespace mod_b is using namespace mod_a, so mod_a::xxx is available=
 as
+    # mod_b::xxx.  This is not available here though, but later, at
+    # start-of-main.  I wonder if this should also be available here.
+    gdb_test "print mod_b::xxx" \
+	[string_to_regexp {No symbol "xxx" in namespace "mod_b".}]
+}
+
+set line_main_entry [gdb_get_line_number main-entry]
+if {![runto $srcfile:$line_main_entry]} {
+    return
+}
+
+# Start of main.  Function main is not yet using namespace mod_b.
+with_test_prefix start-of-main {
+    # Namespace mod_b is using namespace mod_a, so mod_a::xxx is available=
 as
+    # mod_b::xxx.  See also the note at the identical command in pre-main.
+    gdb_test "print mod_b::xxx" " =3D 10"
+
+    # Same command as in end-of-main, but not a regression test for PR3403=
4.
+    gdb_test "print xxx" \
+	[string_to_regexp {No symbol "xxx" in current context.}]
+
+    # Same test as in end-of-main, but not a regression test for PR34051.
+    gdb_test "print mod_a::yyy" \
+	[string_to_regexp {No symbol "yyy" in namespace "mod_a".}]
+}
+
+# After start of main.  Function main is using namespace mod_b.  Check 4 v=
ariants.
+foreach_with_prefix n {1 2 3 4} {
+    set line_main_n [gdb_get_line_number "main-$n"]
+    gdb_test "next" \
+	[subst_vars {$line_main_n\t[^\r\n]+}]
+
+    # Function main is using namespace mod_b, so mod_b::yyy is available as
+    # yyy.
+    gdb_test "print yyy" " =3D 20"
+
+    # Function main is using namespace mod_b, and namespace mod_b is using
+    # namespace mod_a, so mod_a::xxx is available as xxx.  Regression test=
 for
+    # PR34034.
+    setup_kfail exp/34034 *-*-*
+    gdb_test "print xxx" " =3D 10"
+
+    # This used to print " $<n> =3D 20".  Regression test for PR34051.
+    gdb_test "print mod_a::yyy" \
+	[string_to_regexp {No symbol "yyy" in namespace "mod_a".}]
+}