]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Fix C++ template function matching in cooked index
authorTom Tromey <tom@tromey.com>
Sat, 28 Dec 2024 21:10:56 +0000 (14:10 -0700)
committerTom Tromey <tom@tromey.com>
Fri, 24 Jan 2025 20:53:11 +0000 (13:53 -0700)
In commit 64a97606 ("Support template lookups in
strncmp_iw_with_mode"), gdb was changed so that a command like "break
func<templ>" would match instantiations like "func<templ<int>>".

The new indexer does not support this and so this is a regression.
This went unnoticed because gdb.linespec.cpcompletion.exp puts all
these functions into the main file, and this CU is expanded early.

This patch fixes the bug by changing the cooked index entry comparison
function.  It also updates the test to fail without this fix.

Regression tested on x86-64 Fedora 40.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32482

gdb/dwarf2/cooked-index.c
gdb/testsuite/gdb.linespec/cpcompletion.cc [new file with mode: 0644]
gdb/testsuite/gdb.linespec/cpcompletion.exp
gdb/testsuite/gdb.linespec/cpls.cc

index d776f2f9bcacd3df5ec9062b9ecc0b42eb63e28f..e024c78cf51406f51f404bdd8b152dd5d81eda29 100644 (file)
@@ -93,39 +93,37 @@ int
 cooked_index_entry::compare (const char *stra, const char *strb,
                             comparison_mode mode)
 {
-  auto munge = [] (char c) -> unsigned char
+  auto munge = [] (char c) constexpr -> unsigned char
     {
-      /* We want to sort '<' before any other printable character.
-        So, rewrite '<' to something just before ' '.  */
+      /* Treat '<' as if it ended the string.  This lets something
+        like "func<t>" match "func<t<int>>".  See the "Breakpoints in
+        template functions" section in the manual.  */
       if (c == '<')
-       return '\x1f';
+       return '\0';
       return TOLOWER ((unsigned char) c);
     };
 
-  while (*stra != '\0'
-        && *strb != '\0'
-        && (munge (*stra) == munge (*strb)))
+  unsigned char a = munge (*stra);
+  unsigned char b = munge (*strb);
+
+  while (a != '\0' && b != '\0' && a == b)
     {
-      ++stra;
-      ++strb;
+      a = munge (*++stra);
+      b = munge (*++strb);
     }
 
-  unsigned char c1 = munge (*stra);
-  unsigned char c2 = munge (*strb);
-
-  if (c1 == c2)
+  if (a == b)
     return 0;
 
   /* When completing, if STRB ends earlier than STRA, consider them as
-     equal.  When comparing, if STRB ends earlier and STRA ends with
-     '<', consider them as equal.  */
-  if (mode == COMPLETE || (mode == MATCH && c1 == munge ('<')))
+     equal.  */
+  if (mode == COMPLETE || (mode == MATCH && a == munge ('<')))
     {
-      if (c2 == '\0')
+      if (b == '\0')
        return 0;
     }
 
-  return c1 < c2 ? -1 : 1;
+  return a < b ? -1 : 1;
 }
 
 #if GDB_SELF_TEST
@@ -155,33 +153,36 @@ test_compare ()
                                           mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
-                                          mode_compare) < 0);
+                                          mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
                                           mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
-                                          mode_complete) < 0);
+                                          mode_complete) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
                                           mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
                                           mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
-                                          mode_compare) > 0);
+                                          mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
                                           mode_complete) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
-                                          mode_complete) > 0);
+                                          mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>",
                                           "name<arg<more>>",
                                           mode_compare) == 0);
+  SELF_CHECK (cooked_index_entry::compare ("name<arg>",
+                                          "name<arg<more>>",
+                                          mode_compare) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>",
-                                          mode_compare) < 0);
+                                          mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name",
                                           mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
-                                          mode_compare) > 0);
+                                          mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
                                           mode_complete) == 0);
 
@@ -191,7 +192,7 @@ test_compare ()
   SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("func", "func<type>",
-                                          mode_sort) < 0);
+                                          mode_sort) == 0);
   SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1",
                                           mode_sort) < 0);
 }
diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.cc b/gdb/testsuite/gdb.linespec/cpcompletion.cc
new file mode 100644 (file)
index 0000000..5b050d3
--- /dev/null
@@ -0,0 +1,23 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 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/>.  */
+
+extern int not_really_main ();
+
+int main ()
+{
+  return not_really_main ();
+}
index 09bd9a25d77bb79cd1c24452b6b727df5da607cd..2293238d011799bcbc14176181c634b228b6d752 100644 (file)
 load_lib completion-support.exp
 load_lib data-structures.exp
 
-standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc
+standard_testfile cpcompletion.cc cpls.cc cpls2.cc cpls-hyphen.cc
 
 set opts {}
 lappend opts debug
 lappend opts additional_flags=-std=c++11
 
 if {[prepare_for_testing "failed to prepare" $testfile \
-        [list $srcfile $srcfile2 $srcfile3] $opts]} {
+        [list $srcfile $srcfile2 $srcfile3 $srcfile4] $opts]} {
     return -1
 }
 
@@ -666,6 +666,7 @@ proc_with_prefix template-function-foo {} {
            $completion_list
        check_setting_bp_fails "$cmd_prefix foo<A"
 
+       clean_restart $::testfile
        # "foo<A>" should give any function with one parameter of any type
        # of A.  While the parameter list in the template should be ignored,
        # the function's argument list should not be ignored.
index 9f24e47fb92beed151cb7bca4357ddd2b8566d17..0ab7ad3e1959b4b9769dd404b7c39920128b0f4b 100644 (file)
@@ -486,7 +486,7 @@ file_constrained_test_cpls_function (int i)
 
 
 int
-main ()
+not_really_main ()
 {
   template2_struct_inst.template2_fn<int, int> ();
   template_struct_int.template_overload_fn(0);