public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* C++ template completion support v3
@ 2021-12-09 19:17 Keith Seitz
  2021-12-09 19:17 ` [PATCH v3 1/3] Move find_toplevel_char to cp-support.[ch] Keith Seitz
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Keith Seitz @ 2021-12-09 19:17 UTC (permalink / raw)
  To: gdb-patches

I would like to resurrect this patch series (after a very long hiatus).

There are minimal changes to the original series, last posted here:

https://sourceware.org/pipermail/gdb-patches/2020-April/167780.html

During that last series, Tom (Tromey) gave it the LGTM, but there was
a request to add unittests for strncmp_iw_with_mode. This new series
accommodates that request.

The second patch from the original submission ("Remove hard-coded
line number from templates.exp") is no longer necessary,
having been fixed by Andrew Burgess in November. 

I have tested this on x86_64 Fedora 34 w/system GCC using
{unix,native-gdbserver,native-extended-gdbserver}/-m{32,64}.

I apologize that it has taken so long to get back to this.

Keith
--
gdb/NEWS                                    |   6 +-
 gdb/cp-support.c                            |  82 +++-
 gdb/cp-support.h                            |   7 +
 gdb/doc/gdb.texinfo                         |  46 ++
 gdb/linespec.c                              |  77 ----
 gdb/linespec.h                              |   7 -
 gdb/testsuite/gdb.cp/templates.cc           |  47 ++
 gdb/testsuite/gdb.cp/templates.exp          |  67 +++
 gdb/testsuite/gdb.linespec/cpcompletion.exp | 437 ++++++++++++++++++-
 gdb/testsuite/gdb.linespec/cpls-ops.exp     |  53 +--
 gdb/testsuite/gdb.linespec/cpls.cc          | 110 ++++-
 gdb/utils.c                                 | 647 +++++++++++++++++++++++++++-
 gdb/utils.h                                 |   7 +-
 13 files changed, 1468 insertions(+), 125 deletions(-)



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

* [PATCH v3 1/3] Move find_toplevel_char to cp-support.[ch]
  2021-12-09 19:17 C++ template completion support v3 Keith Seitz
@ 2021-12-09 19:17 ` Keith Seitz
  2021-12-09 19:17 ` [PATCH v3 2/3] Unit tests for strncmp_iw_with_mode Keith Seitz
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 10+ messages in thread
From: Keith Seitz @ 2021-12-09 19:17 UTC (permalink / raw)
  To: gdb-patches

find_toplevel_char is being used more and more outside of linespec.c, so
this patch moves it into cp-support.[ch].
---
 gdb/cp-support.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/cp-support.h |  7 +++++
 gdb/linespec.c   | 77 ------------------------------------------------
 gdb/linespec.h   |  7 -----
 4 files changed, 81 insertions(+), 84 deletions(-)

diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 0e506f5b46e..7cc089cbc17 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -2199,6 +2199,80 @@ info_vtbl_command (const char *arg, int from_tty)
   cplus_print_vtable (value);
 }
 
+/* See description in cp-support.h.  */
+
+const char *
+find_toplevel_char (const char *s, char c)
+{
+  int quoted = 0;		/* zero if we're not in quotes;
+				   '"' if we're in a double-quoted string;
+				   '\'' if we're in a single-quoted string.  */
+  int depth = 0;		/* Number of unclosed parens we've seen.  */
+  const char *scan;
+
+  for (scan = s; *scan; scan++)
+    {
+      if (quoted)
+	{
+	  if (*scan == quoted)
+	    quoted = 0;
+	  else if (*scan == '\\' && *(scan + 1))
+	    scan++;
+	}
+      else if (*scan == c && ! quoted && depth == 0)
+	return scan;
+      else if (*scan == '"' || *scan == '\'')
+	quoted = *scan;
+      else if (*scan == '(' || *scan == '<')
+	depth++;
+      else if ((*scan == ')' || *scan == '>') && depth > 0)
+	depth--;
+      else if (*scan == 'o' && !quoted && depth == 0)
+	{
+	  /* Handle C++ operator names.  */
+	  if (strncmp (scan, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0)
+	    {
+	      scan += CP_OPERATOR_LEN;
+	      if (*scan == c)
+		return scan;
+	      while (ISSPACE (*scan))
+		{
+		  ++scan;
+		  if (*scan == c)
+		    return scan;
+		}
+	      if (*scan == '\0')
+		break;
+
+	      switch (*scan)
+		{
+		  /* Skip over one less than the appropriate number of
+		     characters: the for loop will skip over the last
+		     one.  */
+		case '<':
+		  if (scan[1] == '<')
+		    {
+		      scan++;
+		      if (*scan == c)
+			return scan;
+		    }
+		  break;
+		case '>':
+		  if (scan[1] == '>')
+		    {
+		      scan++;
+		      if (*scan == c)
+			return scan;
+		    }
+		  break;
+		}
+	    }
+	}
+    }
+
+  return 0;
+}
+
 void _initialize_cp_support ();
 void
 _initialize_cp_support ()
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 841ee4f5331..b59753bcfd4 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -190,4 +190,11 @@ extern struct cmd_list_element *maint_cplus_cmd_list;
 
 gdb::unique_xmalloc_ptr<char> gdb_demangle (const char *name, int options);
 
+/* Find an instance of the character C in the string S that is outside
+   of all parenthesis pairs, single-quoted strings, and double-quoted
+   strings.  Also, ignore the char within a template name, like a ','
+   within foo<int, int>.  */
+
+extern const char *find_toplevel_char (const char *s, char c);
+
 #endif /* CP_SUPPORT_H */
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 0d3820b9785..03d89fb3cfa 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -1298,83 +1298,6 @@ find_methods (struct type *t, enum language t_lang, const char *name,
     superclasses->push_back (TYPE_BASECLASS (t, ibase));
 }
 
-/* Find an instance of the character C in the string S that is outside
-   of all parenthesis pairs, single-quoted strings, and double-quoted
-   strings.  Also, ignore the char within a template name, like a ','
-   within foo<int, int>, while considering C++ operator</operator<<.  */
-
-const char *
-find_toplevel_char (const char *s, char c)
-{
-  int quoted = 0;		/* zero if we're not in quotes;
-				   '"' if we're in a double-quoted string;
-				   '\'' if we're in a single-quoted string.  */
-  int depth = 0;		/* Number of unclosed parens we've seen.  */
-  const char *scan;
-
-  for (scan = s; *scan; scan++)
-    {
-      if (quoted)
-	{
-	  if (*scan == quoted)
-	    quoted = 0;
-	  else if (*scan == '\\' && *(scan + 1))
-	    scan++;
-	}
-      else if (*scan == c && ! quoted && depth == 0)
-	return scan;
-      else if (*scan == '"' || *scan == '\'')
-	quoted = *scan;
-      else if (*scan == '(' || *scan == '<')
-	depth++;
-      else if ((*scan == ')' || *scan == '>') && depth > 0)
-	depth--;
-      else if (*scan == 'o' && !quoted && depth == 0)
-	{
-	  /* Handle C++ operator names.  */
-	  if (strncmp (scan, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0)
-	    {
-	      scan += CP_OPERATOR_LEN;
-	      if (*scan == c)
-		return scan;
-	      while (isspace (*scan))
-		{
-		  ++scan;
-		  if (*scan == c)
-		    return scan;
-		}
-	      if (*scan == '\0')
-		break;
-
-	      switch (*scan)
-		{
-		  /* Skip over one less than the appropriate number of
-		     characters: the for loop will skip over the last
-		     one.  */
-		case '<':
-		  if (scan[1] == '<')
-		    {
-		      scan++;
-		      if (*scan == c)
-			return scan;
-		    }
-		  break;
-		case '>':
-		  if (scan[1] == '>')
-		    {
-		      scan++;
-		      if (*scan == c)
-			return scan;
-		    }
-		  break;
-		}
-	    }
-	}
-    }
-
-  return 0;
-}
-
 /* The string equivalent of find_toplevel_char.  Returns a pointer
    to the location of NEEDLE in HAYSTACK, ignoring any occurrences
    inside "()" and "<>".  Returns NULL if NEEDLE was not found.  */
diff --git a/gdb/linespec.h b/gdb/linespec.h
index 6b2847b429c..f7ab2709a1e 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -162,13 +162,6 @@ extern const char *get_gdb_linespec_parser_quote_characters (void);
 
 extern int is_ada_operator (const char *string);
 
-/* Find an instance of the character C in the string S that is outside
-   of all parenthesis pairs, single-quoted strings, and double-quoted
-   strings.  Also, ignore the char within a template name, like a ','
-   within foo<int, int>.  */
-
-extern const char *find_toplevel_char (const char *s, char c);
-
 /* Find the end of the (first) linespec pointed to by *STRINGP.
    STRINGP will be advanced to this point.  */
 
-- 
2.31.1


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

* [PATCH v3 2/3] Unit tests for strncmp_iw_with_mode
  2021-12-09 19:17 C++ template completion support v3 Keith Seitz
  2021-12-09 19:17 ` [PATCH v3 1/3] Move find_toplevel_char to cp-support.[ch] Keith Seitz
@ 2021-12-09 19:17 ` Keith Seitz
  2021-12-09 19:17 ` [PATCH v3 3/3] Support template lookups in strncmp_iw_with_mode Keith Seitz
  2022-02-18 20:34 ` C++ template completion support v3 Tom Tromey
  3 siblings, 0 replies; 10+ messages in thread
From: Keith Seitz @ 2021-12-09 19:17 UTC (permalink / raw)
  To: gdb-patches

This patch attempts to make a start at adding unit tests for
strncmp_iw_with_mode.  While there is quite a bit of testing
of this function in other tests, these are currently end-to-end
tests.

This patch attempts to cover the basics of string matching, white
space, C++ ABI tags, and several other topics. However, one area
that is ostensibly missing is testing the `match_for_lcd' feature.
This is otherwise tested as part of our end-to-end DejaGNU-based
testing.
---
 gdb/utils.c | 565 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 565 insertions(+)

diff --git a/gdb/utils.c b/gdb/utils.c
index e27a8818b94..c4d926cff84 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2704,6 +2704,569 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
     return 1;
 }
 
+#if GDB_SELF_TEST
+
+/* Unit tests for strncmp_iw_with_mode.  */
+
+#define CHECK_MATCH_LM(S1, S2, MODE, LANG, LCD)			\
+  SELF_CHECK (strncmp_iw_with_mode ((S1), (S2), strlen ((S2)),	\
+				    strncmp_iw_mode::MODE,				\
+				    (LANG), (LCD)) == 0)
+
+#define CHECK_MATCH_LANG(S1, S2, MODE, LANG)			\
+  CHECK_MATCH_LM ((S1), (S2), MODE, (LANG), nullptr)
+
+#define CHECK_MATCH(S1, S2, MODE)						\
+  CHECK_MATCH_LANG ((S1), (S2), MODE, language_minimal)
+
+#define CHECK_NO_MATCH_LM(S1, S2, MODE, LANG, LCD)		\
+  SELF_CHECK (strncmp_iw_with_mode ((S1), (S2), strlen ((S2)),	\
+				    strncmp_iw_mode::MODE,				\
+				    (LANG)) != 0)
+
+#define CHECK_NO_MATCH_LANG(S1, S2, MODE, LANG)		\
+  CHECK_NO_MATCH_LM ((S1), (S2), MODE, (LANG), nullptr)
+
+#define CHECK_NO_MATCH(S1, S2, MODE)				       \
+  CHECK_NO_MATCH_LANG ((S1), (S2), MODE, language_minimal)
+
+static void
+check_scope_operator (enum language lang)
+{
+  CHECK_MATCH_LANG ("::", "::", NORMAL, lang);
+  CHECK_MATCH_LANG ("::foo", "::", NORMAL, lang);
+  CHECK_MATCH_LANG ("::foo", "::foo", NORMAL, lang);
+  CHECK_MATCH_LANG (" :: foo ", "::foo", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a ::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a\t::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a \t::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a\t ::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a:: b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a::\tb", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a:: \tb", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a::\t b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a :: b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a ::\tb", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a\t:: b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b", "a \t::\t b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a ::b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a\t::b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a \t::b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a\t ::b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a:: b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::\tb", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a:: \tb", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::\t b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a :: b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a ::\tb", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a\t:: b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a \t::\t b", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b::c", "a::b::c", NORMAL, lang);
+  CHECK_MATCH_LANG (" a:: b:: c", "a::b::c", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b::c", " a:: b:: c", NORMAL, lang);
+  CHECK_MATCH_LANG ("a ::b ::c", "a::b::c", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b::c", "a :: b:: c", NORMAL, lang);
+  CHECK_MATCH_LANG ("\ta::\tb::\tc", "\ta::\tb::\tc", NORMAL, lang);
+  CHECK_MATCH_LANG ("a\t::b\t::c\t", "a\t::b\t::c\t", NORMAL, lang);
+  CHECK_MATCH_LANG (" \ta:: \tb:: \tc", " \ta:: \tb:: \tc", NORMAL, lang);
+  CHECK_MATCH_LANG ("\t a::\t b::\t c", "\t a::\t b::\t c", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b::c", "\ta::\tb::\tc", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b::c", "a\t::b\t::c\t", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b::c", " \ta:: \tb:: \tc", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b::c", "\t a::\t b::\t c", NORMAL, lang);
+  CHECK_MATCH_LANG ("\ta::\tb::\tc", "a::b::c", NORMAL, lang);
+  CHECK_MATCH_LANG ("a\t::b\t::c\t", "a::b::c", NORMAL, lang);
+  CHECK_MATCH_LANG (" \ta:: \tb:: \tc", "a::b::c", NORMAL, lang);
+  CHECK_MATCH_LANG ("\t a::\t b::\t c", "a::b::c", NORMAL, lang);
+  CHECK_MATCH_LANG ("a :: b:: c\t", "\ta :: b\t::  c\t\t", NORMAL, lang);
+  CHECK_MATCH_LANG ("  a::\t  \t    b::     c\t", "\ta ::b::  c\t\t",
+	      NORMAL, lang);
+  CHECK_MATCH_LANG ("a      :: b               :: \t\t\tc\t",
+	      "\t\t\t\ta        ::   \t\t\t        b             \t\t::c",
+	      NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b()", "a", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b()", "a::", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b()", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a)", "a", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a)", "a::", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a)", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a,b)", "a", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a,b)", "a::", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a,b)", "a::b", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a,b,c)", "a", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a,b,c)", "a::", NORMAL, lang);
+  CHECK_MATCH_LANG ("a::b(a,b,c)", "a::b", NORMAL, lang);
+
+  CHECK_NO_MATCH_LANG ("a::", "::a", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("::a", "::a()", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("::", "::a", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a:::b", "a::b", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b()", "a::b(a)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b(a)", "a::b()", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b(a,b)", "a::b(a,a)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a()", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::()", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::b()", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a(a)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::(a)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::b()", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a(a,b)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::(a,b)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::b(a,b)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a(a,b,c)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::(a,b,c)", NORMAL, lang);
+  CHECK_NO_MATCH_LANG ("a::b", "a::b(a,b,c)", NORMAL, lang);
+}
+
+/* Callback for strncmp_iw_with_mode unit tests.  */
+
+static void
+strncmp_iw_with_mode_tests ()
+{
+  /* Some of the following tests are nonsensical, but could be input by a
+     deranged script (or user).  */
+
+  /* strncmp_iw_mode::NORMAL: strcmp()-like but ignore any whitespace...  */
+
+  CHECK_MATCH ("", "", NORMAL);
+  CHECK_MATCH ("foo", "foo", NORMAL);
+  CHECK_MATCH (" foo", "foo", NORMAL);
+  CHECK_MATCH ("foo ", "foo", NORMAL);
+  CHECK_MATCH (" foo ", "foo", NORMAL);
+  CHECK_MATCH ("  foo", "foo", NORMAL);
+  CHECK_MATCH ("foo  ", "foo", NORMAL);
+  CHECK_MATCH ("  foo  ", "foo", NORMAL);
+  CHECK_MATCH ("\tfoo", "foo", NORMAL);
+  CHECK_MATCH ("foo\t", "foo", NORMAL);
+  CHECK_MATCH ("\tfoo\t", "foo", NORMAL);
+  CHECK_MATCH (" \tfoo \t", "foo", NORMAL);
+  CHECK_MATCH ("\t foo\t ", "foo", NORMAL);
+  CHECK_MATCH ("\t \t     \t\t\t\t   foo\t\t\t  \t\t   \t   \t    \t  \t ",
+	       "foo", NORMAL);
+  CHECK_MATCH ("foo",
+	       "\t \t     \t\t\t\t   foo\t\t\t  \t\t   \t   \t    \t  \t ",
+	       NORMAL);
+  CHECK_MATCH ("foo bar", "foo", NORMAL);
+  CHECK_NO_MATCH ("foo", "bar", NORMAL);
+  CHECK_NO_MATCH ("foo bar", "foobar", NORMAL);
+  CHECK_NO_MATCH (" foo ", "bar", NORMAL);
+  CHECK_NO_MATCH ("foo", " bar ", NORMAL);
+  CHECK_NO_MATCH (" \t\t    foo\t\t ", "\t    \t    \tbar\t", NORMAL);
+  CHECK_NO_MATCH ("@!%&", "@!%&foo", NORMAL);
+
+  /* ... and function parameters in STRING1.  */
+  CHECK_MATCH ("foo()", "foo()", NORMAL);
+  CHECK_MATCH ("foo ()", "foo()", NORMAL);
+  CHECK_MATCH ("foo  ()", "foo()", NORMAL);
+  CHECK_MATCH ("foo\t()", "foo()", NORMAL);
+  CHECK_MATCH ("foo\t  ()", "foo()", NORMAL);
+  CHECK_MATCH ("foo  \t()", "foo()", NORMAL);
+  CHECK_MATCH ("foo()", "foo ()", NORMAL);
+  CHECK_MATCH ("foo()", "foo  ()", NORMAL);
+  CHECK_MATCH ("foo()", "foo\t()", NORMAL);
+  CHECK_MATCH ("foo()", "foo\t ()", NORMAL);
+  CHECK_MATCH ("foo()", "foo \t()", NORMAL);
+  CHECK_MATCH ("foo()", "foo()", NORMAL);
+  CHECK_MATCH ("foo ()", "foo ()", NORMAL);
+  CHECK_MATCH ("foo  ()", "foo  ()", NORMAL);
+  CHECK_MATCH ("foo\t()", "foo\t()", NORMAL);
+  CHECK_MATCH ("foo\t  ()", "foo\t ()", NORMAL);
+  CHECK_MATCH ("foo  \t()", "foo \t()", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo( a)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(a )", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(\ta)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(a\t)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(\t a)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo( \ta)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(a\t )", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(a \t)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo( a )", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(\ta\t)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(\t a\t )", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo( \ta \t)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo( a)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(a )", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(\ta)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(a\t)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(\t a)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo( \ta)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(a\t )", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(a \t)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo( a )", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(\ta\t)", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo(\t a\t )", NORMAL);
+  CHECK_MATCH ("foo(a)", "foo( \ta \t)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a ,b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a\t,b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a,\tb)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a\t,\tb)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a \t,b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a\t ,b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a,\tb)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a, \tb)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a,\t b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a ,b)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a\t,b)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a,\tb)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a\t,\tb)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a \t,b)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a\t ,b)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a,\tb)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a, \tb)", NORMAL);
+  CHECK_MATCH ("foo(a,b)", "foo(a,\t b)", NORMAL);
+  CHECK_MATCH ("foo(a,b,c,d)", "foo(a,b,c,d)", NORMAL);
+  CHECK_MATCH (" foo ( a , b , c , d ) ", "foo(a,b,c,d)", NORMAL);
+  CHECK_MATCH (" foo ( a , b , c , d ) ", "foo( a , b , c , d )", NORMAL);
+  CHECK_MATCH ("foo &\t*(\ta b    *\t\t&)", "foo", NORMAL);
+  CHECK_MATCH ("foo &\t*(\ta b    *\t\t&)", "foo&*(a b * &)", NORMAL);
+  CHECK_MATCH ("foo(a) b", "foo(a)", NORMAL);
+  CHECK_MATCH ("*foo(*a&)", "*foo", NORMAL);
+  CHECK_MATCH ("*foo(*a&)", "*foo(*a&)", NORMAL);
+  CHECK_MATCH ("*a&b#c/^d$foo(*a&)", "*a&b#c/^d$foo", NORMAL);
+  CHECK_MATCH ("* foo", "*foo", NORMAL);
+  CHECK_MATCH ("foo&", "foo", NORMAL);
+  CHECK_MATCH ("foo*", "foo", NORMAL);
+  CHECK_MATCH ("foo.", "foo", NORMAL);
+  CHECK_MATCH ("foo->", "foo", NORMAL);
+
+  CHECK_NO_MATCH ("foo", "foo(", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo()", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo(a)", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo(a)", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo*", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo (*", NORMAL);
+  CHECK_NO_MATCH ("foo*", "foo (*", NORMAL);
+  CHECK_NO_MATCH ("foo *", "foo (*", NORMAL);
+  CHECK_NO_MATCH ("foo&", "foo (*", NORMAL);
+  CHECK_NO_MATCH ("foo &", "foo (*", NORMAL);
+  CHECK_NO_MATCH ("foo &*", "foo (&)", NORMAL);
+  CHECK_NO_MATCH ("foo & \t    *\t", "foo (*", NORMAL);
+  CHECK_NO_MATCH ("foo & \t    *\t", "foo (*", NORMAL);
+  CHECK_NO_MATCH ("foo(a*) b", "foo(a) b", NORMAL);
+  CHECK_NO_MATCH ("foo[aqi:A](a)", "foo(b)", NORMAL);
+  CHECK_NO_MATCH ("*foo", "foo", NORMAL);
+  CHECK_NO_MATCH ("*foo", "foo*", NORMAL);
+  CHECK_NO_MATCH ("*foo*", "*foo&", NORMAL);
+  CHECK_NO_MATCH ("*foo*", "foo *", NORMAL);
+  CHECK_NO_MATCH ("&foo", "foo", NORMAL);
+  CHECK_NO_MATCH ("&foo", "foo&", NORMAL);
+  CHECK_NO_MATCH ("foo&", "&foo", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo&", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo*", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo.", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo->", NORMAL);
+  CHECK_NO_MATCH ("foo bar", "foo()", NORMAL);
+  CHECK_NO_MATCH ("foo bar", "foo bar()", NORMAL);
+  CHECK_NO_MATCH ("foo()", "foo(a)", NORMAL);
+  CHECK_NO_MATCH ("*(*)&", "*(*)*", NORMAL);
+  CHECK_NO_MATCH ("foo(a)", "foo()", NORMAL);
+  CHECK_NO_MATCH ("foo(a)", "foo(b)", NORMAL);
+  CHECK_NO_MATCH ("foo(a,b)", "foo(a,b,c)", NORMAL);
+  CHECK_NO_MATCH ("foo(a\\b)", "foo()", NORMAL);
+  CHECK_NO_MATCH ("foo bar(a b c d)", "foobar", NORMAL);
+  CHECK_NO_MATCH ("foo bar(a b c d)", "foobar ( a b   c \td\t)\t", NORMAL);
+
+  /* Test scope operator.  */
+  check_scope_operator (language_minimal);
+  check_scope_operator (language_cplus);
+  check_scope_operator (language_fortran);
+  check_scope_operator (language_rust);
+
+  /* Test C++ user-defined operators.  */
+  CHECK_MATCH_LANG ("operator foo(int&)", "operator foo(int &)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo(int &)", "operator foo(int &)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo(int\t&)", "operator foo(int\t&)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo (int)", "operator foo(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo\t(int)", "operator foo(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo \t(int)", "operator foo(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo (int)", "operator foo \t(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo\t(int)", "operator foo \t(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("operator foo \t(int)", "operator foo \t(int)", NORMAL,
+		    language_cplus);
+
+  CHECK_MATCH_LANG ("a::operator foo(int&)", "a::operator foo(int &)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a :: operator foo(int &)", "a::operator foo(int &)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a \t:: \toperator foo(int\t&)", "a::operator foo(int\t&)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a::operator foo (int)", "a::operator foo(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a::operator foo\t(int)", "a::operator foo(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a::operator foo \t(int)", "a::operator foo(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a::operator foo (int)", "a::operator foo \t(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a::operator foo\t(int)", "a::operator foo \t(int)", NORMAL,
+		    language_cplus);
+  CHECK_MATCH_LANG ("a::operator foo \t(int)", "a::operator foo \t(int)", NORMAL,
+		    language_cplus);
+
+  CHECK_NO_MATCH_LANG ("operator foo(int)", "operator foo(char)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("operator foo(int)", "operator foo(int *)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("operator foo(int)", "operator foo(int &)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("operator foo(int)", "operator foo(int, char *)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("operator foo(int)", "operator bar(int)", NORMAL,
+		       language_cplus);
+
+  CHECK_NO_MATCH_LANG ("a::operator b::foo(int)", "a::operator a::foo(char)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("a::operator foo(int)", "a::operator foo(int *)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("a::operator foo(int)", "a::operator foo(int &)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("a::operator foo(int)", "a::operator foo(int, char *)", NORMAL,
+		       language_cplus);
+  CHECK_NO_MATCH_LANG ("a::operator foo(int)", "a::operator bar(int)", NORMAL,
+		       language_cplus);
+
+  /* Skip "[abi:cxx11]" tags in the symbol name if the lookup name
+     doesn't include them.  These are not language-specific in
+     strncmp_iw_with_mode.  */
+
+  CHECK_MATCH ("foo[abi:a]", "foo", NORMAL);
+  CHECK_MATCH ("foo[abi:a]()", "foo", NORMAL);
+  CHECK_MATCH ("foo[abi:a](a)", "foo", NORMAL);
+  CHECK_MATCH ("foo[abi:a](a&,b*)", "foo", NORMAL);
+  CHECK_MATCH ("foo[abi:a](a,b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo[abi:a](a,b) c", "foo(a,b) c", NORMAL);
+  CHECK_MATCH ("foo[abi:a](a)", "foo(a)", NORMAL);
+  CHECK_MATCH ("foo[abi:a](a,b)", "foo(a,b)", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[ abi:a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[\tabi:a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[ \tabi:a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[\t abi:a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi :a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi\t:a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi \t:a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi\t :a]", "foo[abi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[ abi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[\tabi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[ \tabi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[\t abi:a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi :a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi\t:a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi \t:a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi\t :a]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi:a ]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi:a\t]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi:a \t]", NORMAL);
+  CHECK_MATCH ("foo[abi:a]", "foo[abi:a\t ]", NORMAL);
+  CHECK_MATCH ("foo[abi:a,b]", "foo[abi:a,b]", NORMAL);
+  CHECK_MATCH ("foo[abi:::]", "foo[abi:::]", NORMAL);
+  CHECK_MATCH ("foo[abi : : : ]", "foo[abi:::]", NORMAL);
+  CHECK_MATCH ("foo[abi:::]", "foo[abi : : : ]", NORMAL);
+  CHECK_MATCH ("foo[ \t abi  \t:\t:   :   \t]",
+	       "foo[   abi :                \t    ::]",
+	       NORMAL);
+  CHECK_MATCH ("foo< bar< baz< quxi > > >(int)", "foo<bar<baz<quxi>>>(int)",
+	       NORMAL);
+  CHECK_MATCH ("\tfoo<\tbar<\tbaz\t<\tquxi\t>\t>\t>(int)",
+	       "foo<bar<baz<quxi>>>(int)", NORMAL);
+  CHECK_MATCH (" \tfoo \t< \tbar \t< \tbaz \t< \tquxi \t> \t> \t> \t( \tint \t)",
+	       "foo<bar<baz<quxi>>>(int)", NORMAL);
+  CHECK_MATCH ("foo<bar<baz<quxi>>>(int)",
+	       "foo < bar < baz < quxi > > > (int)", NORMAL);
+  CHECK_MATCH ("foo<bar<baz<quxi>>>(int)",
+	       "\tfoo\t<\tbar\t<\tbaz\t<\tquxi\t>\t>\t>\t(int)", NORMAL);
+  CHECK_MATCH ("foo<bar<baz<quxi>>>(int)",
+	       " \tfoo \t< \tbar \t< \tbaz \t< \tquxi \t> \t> \t> \t( \tint \t)", NORMAL);
+  CHECK_MATCH ("foo<bar<baz>>::foo(quxi &)", "fo", NORMAL);
+  CHECK_MATCH ("foo<bar<baz>>::foo(quxi &)", "foo", NORMAL);
+  CHECK_MATCH ("foo<bar<baz>>::foo(quxi &)", "foo<bar<baz>>::", NORMAL);
+  CHECK_MATCH ("foo<bar<baz>>::foo(quxi &)", "foo<bar<baz> >::foo", NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo[abi:a][abi:b](bar[abi:c][abi:d])",
+	       NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo", NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo(bar)", NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo[abi:a](bar)", NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo(bar[abi:c])", NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo[abi:a](bar[abi:c])", NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo[abi:a][abi:b](bar)", NORMAL);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo[abi:a][abi:b](bar[abi:c])",
+	       NORMAL);
+  CHECK_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo", NORMAL);
+  CHECK_NO_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo()", NORMAL);
+  CHECK_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo<bar>", NORMAL);
+  CHECK_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo<bar>(char*, baz)", NORMAL);
+  CHECK_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo<bar>(char*, baz[abi:b])",
+	      NORMAL);
+  CHECK_NO_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo<bar>(char*, baz[abi:A])",
+	      NORMAL);
+  CHECK_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo<bar[abi:a]>(char*, baz)",
+	      NORMAL);
+  CHECK_NO_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo<bar[abi:A]>(char*, baz)",
+	      NORMAL);
+  CHECK_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])", "foo<bar[abi:a]>(char*, baz[abi:b])",
+	      NORMAL);
+  CHECK_NO_MATCH("foo<bar[abi:a]>(char *, baz[abi:b])",
+		 "foo<bar[abi:a]>(char*, baz[abi:B])", NORMAL);
+
+  CHECK_NO_MATCH ("foo", "foo[", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[ a]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[a ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[ a ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[\ta]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[a \t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[a\t ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[ \ta]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[\t a]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[ \ta \t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[\t a\t ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[ abi]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[\tabi]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi\t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[ \tabi]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[\t abi]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi \t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi\t ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi :]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi\t:]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi \t:]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi\t :]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi: ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:\t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi: \t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:\t ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi: a]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:\ta]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi: \ta]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:\t a]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:a ]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:a\t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:a \t]", NORMAL);
+  CHECK_NO_MATCH ("foo", "foo[abi:a\t ]", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a)", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a)", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a)", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a)", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a) c", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a) .", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a) *", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a]()", "foo(a) &", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b) c", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b) .", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b) *", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b) &", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b)c", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b).", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b)*", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b)", "foo(a,b)&", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a,b) d", "foo(a,b) c", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a)", "foo()", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a)", "foo(b)", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a)", "foo[abi:b](a)", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a](a)", "foo[abi:a](b)", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:]", "foo[abi:a]", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:", "foo[abi:a]", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:]", "foo[abi:a", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:,]", "foo[abi:a]", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:a,b]", "foo[abi:a]", NORMAL);
+  CHECK_NO_MATCH ("foo[abi::a]", "foo[abi:a]", NORMAL);
+  CHECK_NO_MATCH ("foo[abi:,([a]", "foo[abi:a]", NORMAL);
+
+  CHECK_MATCH ("foo <a, b [, c (",  "foo", NORMAL);
+  CHECK_MATCH ("foo >a, b ], c )",  "foo", NORMAL);
+  CHECK_MATCH ("@!%&\\*", "@!%&\\*", NORMAL);
+  CHECK_MATCH ("()", "()", NORMAL);
+  CHECK_MATCH ("*(*)*", "*(*)*", NORMAL);
+  CHECK_MATCH ("[]", "[]", NORMAL);
+  CHECK_MATCH ("<>", "<>", NORMAL);
+
+  /* strncmp_iw_with_mode::MATCH_PARAMS: the "strcmp_iw hack."  */
+  CHECK_MATCH ("foo2", "foo", NORMAL);
+  CHECK_NO_MATCH ("foo2", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", "foo ", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", "foo\t", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", "foo \t", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", "foo\t ", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", "foo \t", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", " foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", "\tfoo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", " \tfoo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2", "\t foo", MATCH_PARAMS);
+  CHECK_NO_MATCH (" foo2", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("\tfoo2", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH (" \tfoo2", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("\t foo2", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH (" foo2 ", " foo ", MATCH_PARAMS);
+  CHECK_NO_MATCH ("\tfoo2\t", "\tfoo\t", MATCH_PARAMS);
+  CHECK_NO_MATCH (" \tfoo2 \t", " \tfoo \t", MATCH_PARAMS);
+  CHECK_NO_MATCH ("\t foo2\t ", "\t foo\t ", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 ", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2\t", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 ", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 \t", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2\t ", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 (args)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 (args)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2\t(args)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 \t(args)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2\t (args)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 ( args)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2(args )", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2(args\t)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 (args \t)", "foo", MATCH_PARAMS);
+  CHECK_NO_MATCH ("foo2 (args\t )", "foo", MATCH_PARAMS);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo[abi:a][abi:b](bar[abi:c][abi:d])",
+	       MATCH_PARAMS);
+  CHECK_MATCH ("foo[abi:a][abi:b](bar[abi:c][abi:d])", "foo", MATCH_PARAMS);
+
+  /* strncmp_iw_with_mode also supports case insensitivity.  */
+  {
+    CHECK_NO_MATCH ("FoO", "foo", NORMAL);
+    CHECK_NO_MATCH ("FoO", "foo", MATCH_PARAMS);
+
+    scoped_restore restore_case = make_scoped_restore (&case_sensitivity);
+    case_sensitivity = case_sensitive_off;
+
+    CHECK_MATCH ("FoO", "foo", NORMAL);
+    CHECK_MATCH ("FoO", "foo", MATCH_PARAMS);
+    CHECK_MATCH ("foo", "FoO", NORMAL);
+    CHECK_MATCH ("foo", "FoO", MATCH_PARAMS);
+
+    CHECK_MATCH ("FoO[AbI:abC]()", "foo", NORMAL);
+    CHECK_NO_MATCH ("FoO[AbI:abC]()", "foo", MATCH_PARAMS);
+    CHECK_MATCH ("FoO2[AbI:abC]()", "foo", NORMAL);
+    CHECK_NO_MATCH ("FoO2[AbI:abC]()", "foo", MATCH_PARAMS);
+
+    CHECK_MATCH ("foo[abi:abc]()", "FoO[AbI:abC]()", NORMAL);
+    CHECK_MATCH ("foo[abi:abc]()", "FoO[AbI:AbC]()", MATCH_PARAMS);
+    CHECK_MATCH ("foo[abi:abc](xyz)", "FoO[AbI:abC](XyZ)", NORMAL);
+    CHECK_MATCH ("foo[abi:abc](xyz)", "FoO[AbI:abC](XyZ)", MATCH_PARAMS);
+    CHECK_MATCH ("foo[abi:abc][abi:def](xyz)", "FoO[AbI:abC](XyZ)", NORMAL);
+    CHECK_MATCH ("foo[abi:abc][abi:def](xyz)", "FoO[AbI:abC](XyZ)",
+		 MATCH_PARAMS);
+    CHECK_MATCH ("foo<bar<baz>>(bar<baz>)", "FoO<bAr<BaZ>>(bAr<BaZ>)",
+		 NORMAL);
+    CHECK_MATCH ("foo<bar<baz>>(bar<baz>)", "FoO<bAr<BaZ>>(bAr<BaZ>)",
+		 MATCH_PARAMS);
+  }
+}
+
+#undef MATCH
+#undef NO_MATCH
+#endif
+
 /* See utils.h.  */
 
 int
@@ -3553,5 +4116,7 @@ When set, debugging messages will be marked with seconds and microseconds."),
 #if GDB_SELF_TEST
   selftests::register_test ("gdb_realpath", gdb_realpath_tests);
   selftests::register_test ("gdb_argv_array_view", gdb_argv_as_array_view_test);
+  selftests::register_test ("strncmp_iw_with_mode",
+			    strncmp_iw_with_mode_tests);
 #endif
 }
-- 
2.31.1


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

* [PATCH v3 3/3] Support template lookups in strncmp_iw_with_mode
  2021-12-09 19:17 C++ template completion support v3 Keith Seitz
  2021-12-09 19:17 ` [PATCH v3 1/3] Move find_toplevel_char to cp-support.[ch] Keith Seitz
  2021-12-09 19:17 ` [PATCH v3 2/3] Unit tests for strncmp_iw_with_mode Keith Seitz
@ 2021-12-09 19:17 ` Keith Seitz
  2021-12-09 19:40   ` Eli Zaretskii
  2022-02-18 20:34 ` C++ template completion support v3 Tom Tromey
  3 siblings, 1 reply; 10+ messages in thread
From: Keith Seitz @ 2021-12-09 19:17 UTC (permalink / raw)
  To: gdb-patches

This patch adds support for wild template parameter list matches, similar
to how ABI tags or function overloads are now handled.

With this patch, users will be able to "gloss over" the details of matching
template parameter lists.  This is accomplished by adding (yet more) logic
to strncmp_iw_with_mode to skip parameter lists if none is explicitly given
by the user.

Here's a simple example using gdb.linespec/cpls-ops.exp:

Before
------
(gdb) ptype test_op_call
type = struct test_op_call {
  public:
    void operator()(void);
    void operator()(int);
    void operator()(long);
    void operator()<int>(int *);
}
(gdb) b test_op_call::operator()
Breakpoint 1 at 0x400583: test_op_call::operator(). (3 locations)
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   <MULTIPLE>
1.1                         y     0x400583 in test_op_call::operator()(int)
                                                   at cpls-ops.cc:43
1.2                         y     0x40058e in test_op_call::operator()()
                                                   at cpls-ops.cc:47
1.3                         y     0x40059e in test_op_call::operator()(long)
                                                   at cpls-ops.cc:51

The breakpoint at test_op_call::operator()<int> was never set.

After
-----
(gdb) b test_op_call::operator()
Breakpoint 1 at 0x400583: test_op_call::operator(). (4 locations)
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   <MULTIPLE>
1.1                         y     0x400583 in test_op_call::operator()(int)
                                                   at cpls-ops.cc:43
1.2                         y     0x40058e in test_op_call::operator()()
                                                   at cpls-ops.cc:47
1.3                         y     0x40059e in test_op_call::operator()(long)
                                                   at cpls-ops.cc:51
1.4                         y     0x4008d0 in test_op_call::operator()<int>(int*)
                                                   at cpls-ops.cc:57

Similar to how scope lookups work, passing "-qualified" to the break command
will cause a literal lookup of the symbol.  In the example immediately above,
this will cause GDB to only find the three non-template functions.
---
 gdb/NEWS                                    |   4 +
 gdb/cp-support.c                            |   8 +-
 gdb/doc/gdb.texinfo                         |  46 +++
 gdb/testsuite/gdb.cp/templates.cc           |  47 +++
 gdb/testsuite/gdb.cp/templates.exp          |  67 +++
 gdb/testsuite/gdb.linespec/cpcompletion.exp | 437 +++++++++++++++++++-
 gdb/testsuite/gdb.linespec/cpls-ops.exp     |  53 ++-
 gdb/testsuite/gdb.linespec/cpls.cc          | 110 ++++-
 gdb/utils.c                                 |  82 +++-
 gdb/utils.h                                 |   7 +-
 10 files changed, 821 insertions(+), 40 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 13b66286876..6f5bf73f331 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 11
 
+* C++ developers may now ignore template parameter lists when specifying
+  locations, e.g., while setting breakpoints.  This is analogous to how
+  completion and symbol lookup handle overloaded functions or ABI tags.
+
 * New commands
 
 maint set backtrace-on-fatal-signal on|off
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 7cc089cbc17..04d0b4394ef 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1692,6 +1692,12 @@ cp_search_name_hash (const char *search_name)
 	  && string[5] != ':')
 	break;
 
+      /* Ignore template parameter lists.  */
+      if (string[0] == '<'
+	  && string[1] != '(' && string[1] != '<' && string[1] != '='
+	  && string[1] != ' ' && string[1] != '\0')
+	break;
+
       hash = SYMBOL_HASH_NEXT (hash, *string);
     }
   return hash;
@@ -1745,7 +1751,7 @@ cp_symbol_name_matches_1 (const char *symbol_search_name,
   while (true)
     {
       if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len,
-				mode, language_cplus, match_for_lcd) == 0)
+				mode, language_cplus, match_for_lcd, true) == 0)
 	{
 	  if (comp_match_res != NULL)
 	    {
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d0c5bcf18e1..f62035df434 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -16874,6 +16874,52 @@ also use the @value{GDBN} command-line word completion facilities to list the
 available choices, or to finish the type list for you.
 @xref{Completion,, Command Completion}, for details on how to do this.
 
+@item @r{Breakpoints in template functions}
+
+Similar to overloaded symbols, @value{GDBN} will set breakpoints in all
+template instantiations with the user-specified name.  To set a breakpoint in
+a specific template instantiation, include the template parameter list.
+
+@smallexample
+(gdb) b mytemplate
+Breakpoint 1 at 0x40085b: mytemplate. (2 locations)
+(gdb) info breakpoints
+Num     Type           Disp Enb Address    What
+1       breakpoint     keep y   <MULTIPLE>
+1.1                         y     0x40085b in mytemplate<int>(int)
+                                           at mytemplate.cc:8
+1.2                         y     0x40087a in mytemplate<double>(double)
+                                           at mytemplate.cc:8
+@end smallexample
+
+@noindent
+In the above example, @value{GDBN} searches all namespaces and types for
+functions named @code{mytemplate}, ignoring template parameter lists and
+function arguments.
+
+In the below example, a template parameter list is specified, and @value{GDBN}
+searches all namespaces and types for the specific instantiation:
+
+@smallexample
+(gdb) b mytemplate<int>
+Breakpoint 2 at 0x40085b: file mytemplate.cc, line 8.
+(gdb) info breakpoints
+Num     Type           Disp Enb Address    What
+2       breakpoint     keep y   0x40085b   in mytemplate<int>(int)
+                                           at mytemplate.cc:8
+@end smallexample
+
+Just as with function overloads, the @kbd{-qualified} flag may be used to
+override this behavior, causing @value{GDBN} to search for a specific
+function without ignoring template parameter lists.
+
+@smallexample
+(gdb) b mytemplate
+Breakpoint 3 at 0x40085b: mytemplate. (2 locations)
+(gdb) b -qualified mytemplate
+Function "mytemplate" not defined.
+@end smallexample
+
 @item @r{Breakpoints in functions with ABI tags}
 
 The GNU C@t{++} compiler introduced the notion of ABI ``tags'', which
diff --git a/gdb/testsuite/gdb.cp/templates.cc b/gdb/testsuite/gdb.cp/templates.cc
index d5b24af3a4e..1b9a2d48819 100644
--- a/gdb/testsuite/gdb.cp/templates.cc
+++ b/gdb/testsuite/gdb.cp/templates.cc
@@ -742,6 +742,34 @@ template<class C> int FunctionArg<C>::method(Empty<void (FunctionArg<C>)> &arg)
 Empty<void(FunctionArg<int>)> empty;
 FunctionArg<int> arg;
 
+template <typename T1>
+struct Foozle
+{
+  int x;
+  T1 t;
+  template <typename T2>
+  T2 fogey (T2 plop);
+};
+
+template <typename T1>
+template <typename T2>
+T2 Foozle<T1>::fogey (T2 plop)
+{
+  return plop;
+}
+
+template <typename T>
+int operator< (T &lhs, T &rhs)
+{
+  return 0;
+}
+
+template <typename T>
+int operator<< (T &obj, T &val)
+{
+  return 1;
+};
+
 int main()
 {
     int i;
@@ -815,5 +843,24 @@ int main()
 
   arg.method(empty);
 
+  Empty<int> e;
+  Foozle<int> fzi;
+  x = fzi.fogey (0);
+  c = fzi.fogey<char> ('a');
+  e = fzi.fogey<Empty<int>> (e);
+  Foozle<char> fzc;
+  c = fzc.fogey ('b');
+  x = fzc.fogey<int> (0);
+  e = fzc.fogey<Empty<int>> (e);
+  Foozle<Empty<int>> fze;
+  e = fze.fogey (e);
+  c = fze.fogey<char> ('c');
+  x = fze.fogey<int> (0);
+
+  z = e < e;
+  z += e << e;
+  z += fzi < fzi;
+  z += fzi << fzi;
+
   return 0;	/* Final breakpoint.  */
 }
diff --git a/gdb/testsuite/gdb.cp/templates.exp b/gdb/testsuite/gdb.cp/templates.exp
index 5f0538d8d4b..c0d574ed08a 100644
--- a/gdb/testsuite/gdb.cp/templates.exp
+++ b/gdb/testsuite/gdb.cp/templates.exp
@@ -614,3 +614,70 @@ gdb_test "print Garply<Garply<char> >::garply" \
 # Now should work fine
 gdb_test "break Garply<Garply<char> >::garply" \
     "Breakpoint \[0-9\]* at $hex: file .*templates.cc, line.*"
+
+#
+# Template wild-matching tests
+#
+
+# Turn off "ask" when multiple symbols are seen.
+gdb_test_no_output "set multiple-symbols all"
+
+# Test setting breakpoints in a method of all class template instantiations,
+# including overloads.
+gdb_test "break Foo::foo"  "Breakpoint.*at.* \\(3 locations\\)"
+foreach t [list "int" "char" "volatile char *"] {
+    gdb_breakpoint "Foo<$t>::foo (int, $t)"
+    gdb_breakpoint "Foo::foo (int, $t)"
+}
+
+gdb_test "break Bar::bar" "Breakpoint.*at.* \\(2 locations\\)"
+gdb_test "break Bar::bar (int, int)" "Breakpoint.*at.* \\(2 locations\\)"
+foreach val [list 1 33] {
+    gdb_breakpoint "Bar<int, $val>::bar (int, int)"
+}
+
+# Test setting breakpoints in a member function template of a class template,
+# including overloads.
+gdb_test "break Foozle::fogey" "Breakpoint.*at.* \\(9 locations\\)" \
+    "break at template method fogey"
+foreach t [list "int" "char" "Empty<int>"] {
+    gdb_test "break Foozle::fogey ($t)" "Breakpoint.*at.* \\(3 locations\\)"
+    gdb_test "break Foozle::fogey<$t>" "Breakpoint.*at.* \\(3 locations\\)"
+    foreach u [list "int" "char" "Empty<int>"] {
+	gdb_breakpoint "Foozle<$t>::fogey<$u>" message
+	gdb_breakpoint "Foozle<$t>::fogey<$u> ($u)" message
+    }
+}
+
+# Test templated operators < and <<.  Restrict results to only the test
+# source file.
+# operator<:
+#   1. operator< (const T2&, const T2&)
+#   2. operator< (const T2&, char)
+#   3. operator< <Empty<int>>
+#   4. operator< <Foozle<in>>
+#
+# operator<<:
+#   1. operator<< <Empty<int>>
+#   2. operator<< <Foozle<int>>
+gdb_test "break -source $srcfile -func operator<" \
+    "Breakpoint.*at.* \\(4 locations\\)"
+gdb_test "break -source $srcfile -func operator<<" \
+    "Breakpoint.*at.* \\(2 locations\\)"
+foreach t [list "Empty" "Foozle"] {
+    set tt "$t<int>"
+    gdb_breakpoint "operator< <$tt>" message
+    gdb_breakpoint "operator<< <$tt>" message
+
+    # Try a specific instance, both with and without whitespace
+    # after the template-template parameter.
+    gdb_breakpoint "operator< <$tt> ($tt&, $tt&)" message
+    gdb_breakpoint "operator< <$tt > ($tt&, $tt&)" message
+    gdb_breakpoint "operator<< <$tt> ($tt&, $tt&)" message
+    gdb_breakpoint "operator<< <$tt > ($tt&, $tt&)" message
+}
+
+# Test that "-qualified" finds no matching locations.
+gdb_test_no_output "set breakpoint pending off"
+gdb_test "break -qualified Foozle::fogey" \
+    "Function \"Foozle::fogey\" not defined."
diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
index ebac3f90c2d..b4b10048f16 100644
--- a/gdb/testsuite/gdb.linespec/cpcompletion.exp
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -16,6 +16,7 @@
 # This file is part of the gdb testsuite.
 
 load_lib completion-support.exp
+load_lib data-structures.exp
 
 standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc
 
@@ -32,6 +33,217 @@ if { ![readline_is_used] } {
     return -1
 }
 
+#
+# Some convenience procedures for testing template parameter list
+# completion.
+#
+
+# For the variable named ARGLISTVAR, which should be the name of an
+# argument list in the calling frame, "consume" the top-most token.
+# [See comments for makefoo for description of arglist format.]
+
+proc consume {arglistvar} {
+  upvar $arglistvar arglist
+
+  # ARGLIST is a string -- simply strip off the first character.
+  set arglist [string range $arglist 1 end]
+}
+
+# Create a function template named NAME, using the given stack ID to grab
+# NUM template parameters.  The result is pushed back onto the
+# stack.  NUM may be "all," in which case we use the entire stack
+# to create the function template, including function arguments.
+# The resulting template function's arguments are taken from the test
+# source code for the function "foo" and is not generalized.
+
+proc maket {sid name {num 1}} {
+
+  # Set up a temporary stack of parameters.  This will reverse
+  # the order in SID so that when they are popped again below,
+  # we get them in the correct order.  This also takes into account
+  # how many levels of the result stack we want to consider.
+
+  set paramstack [::Stack::new]
+  if {[string equal $num "all"]} {
+    while {![stack empty $sid]} {
+      stack push $paramstack [stack pop $sid]
+    }
+  } else {
+    for {set i 0} {$i < $num} {incr i} {
+      stack push $paramstack [stack pop $sid]
+    }
+  }
+
+  # Construct the function template and push it back to the
+  # top of the stack given by SID.
+  set result ""
+  set first true
+  while {![stack empty $paramstack]} {
+    set top [stack pop $paramstack]
+    if {$first} {
+      set first false
+    } else {
+      append result ", "
+    }
+    append result $top
+  }
+
+  # Save argument list.
+  set args $result
+
+  # GDB outputs "> >" instead of ">>".
+  if {[string index $top end] == ">"} {
+      append result " "
+  }
+  set result "$name<$result>"
+  if {[string equal $num "all"]} {
+    append result "($args)"
+  }
+  stack push $sid $result
+  stack delete $paramstack
+}
+
+# Given the stack SID and the name of a variable of the desired template
+# parameters, construct the actual template parameter and push it to the
+# top of the stack.
+
+proc makearg {sid arglistvar} {
+    upvar $arglistvar arglist
+
+  set c [string index $arglist 0]
+  consume arglist
+  switch $c {
+    A -
+    B {
+      makearg $sid arglist
+      makearg $sid arglist
+      maket $sid $c 2
+    }
+
+    a -
+    b -
+    c -
+    d {
+      makearg $sid arglist
+      maket $sid $c
+    }
+
+    i {
+      stack push $sid "int"
+    }
+
+    n {
+      # These are not templates.
+      set c [string index $arglist 0]
+      stack push $sid "n::n$c"
+      consume arglist
+    }
+
+    N {
+      set c [string index $arglist 0]
+      makearg $sid arglist
+      set top [stack pop $sid]
+      stack push $sid "n::N$top"
+    }
+
+    default { error "unhandled arglist identifier: '$c'" }
+  }
+}
+
+# Given ARGLIST, construct a class template for the type and return
+# it as a string.
+
+proc maketype {arglist} {
+    set s [Stack::new]
+    makearg $s arglist
+    set result [stack pop $s]
+    stack delete $s
+    return $result
+}
+
+# Returns a function template declaration for the function "foo" in the
+# corresponding test source code.  ARGLIST specifies the exact instantiation
+# that is desired.
+#
+# Generically, this procedure returns a string of the form,
+# "foo<parameter-list> (arg-list)", where ARGLIST describes the parameter(s).
+#
+# Valid specifiers for ARGLIST (must be kept in sync with source code):
+#
+# i: Creates an "int" type.
+# a, b, c, d: Creates the struct template of the same name, taking a single
+#    template parameter.
+# A, B: Creates the struct template of the same name, taking two template
+#    parameters.
+# na, nb: Creates the non-template structs n::na and n::nb, respectively.
+# NA, NB: Creates the struct templates n::NA and n::NB, respectively, which
+#    take two template parameters.
+#
+# Examples:
+# makefoo i
+# --> foo<int> (int)
+# makefoo ii
+# --> foo<int, int> (int, int)
+# makefoo Aiabi
+# --> foo<A<int, a<b<int> > > > (A<int, a<b<int> > >)
+# makefoo NANAiaiNBbiabi
+# --> foo<n::NA<n::NA<int, a<int> >, n::NB<b<int>, a<b<int> > > > >
+#          (n::NA<n::NA<int, a<int> >, n::NB<b<int>, a<b<int> > > >)
+
+proc makefoo {arglist} {
+    set s [::Stack::new]
+    while {[string length $arglist] > 0} {
+	makearg $s arglist
+    }
+
+  maket $s "foo" all
+  set result [stack pop $s]
+  stack delete $s
+  return $result
+}
+
+# Test wrapper for a single "makefoo" unit test.
+
+proc test_makefoo_1 {arglist expected} {
+  set exp "foo<$expected"
+  if {[string index $exp end] == ">"} {
+    append exp " "
+  }
+  append exp ">"
+  append exp "($expected)"
+
+  set calc [makefoo $arglist]
+  send_log "makefoo $arglist = $calc\n"
+  send_log "expecting: $exp\n"
+  if {[string equal $exp $calc]} {
+    pass "makefoo unit test: $arglist"
+  } else {
+      fail "makefoo unit test: $arglist"
+  }
+}
+
+# Test whether the procedure "makefoo" is functioning as expected.
+
+proc test_makefoo {} {
+  test_makefoo_1 "i" "int"
+  test_makefoo_1 "ai" "a<int>"
+  test_makefoo_1 "aai" "a<a<int> >"
+  test_makefoo_1 "ii" "int, int"
+  test_makefoo_1 "aaibi" "a<a<int> >, b<int>"
+    test_makefoo_1 \
+      "ababiibababai" "a<b<a<b<int> > > >, int, b<a<b<a<b<a<int> > > > > >"
+  test_makefoo_1 "Aii" "A<int, int>"
+  test_makefoo_1 "ABaibibi" "A<B<a<int>, b<int> >, b<int> >"
+  test_makefoo_1 "na" "n::na"
+  test_makefoo_1 "nana" "n::na, n::na"
+  test_makefoo_1 "NAii" "n::NA<int, int>"
+  test_makefoo_1 "NANAiiNAii" "n::NA<n::NA<int, int>, n::NA<int, int> >"
+}
+
+#
+# Tests start here.
+#
+
 # Disable the completion limit for the whole testcase.
 gdb_test_no_output "set max-completions unlimited"
 
@@ -385,12 +597,11 @@ proc_with_prefix template-ret-type {} {
 	    test_complete_prefix_range $complete_line $start
 	}
 
-	# Setting a breakpoint without the template params doesn't work.
-	check_setting_bp_fails "$cmd_prefix template2_fn"
-	# However, setting a breakpoint with template params and without
-	# the method params does work, just like with non-template
-	# functions.  It also works with or without return type.
+	# Setting a breakpoint with or without template params and without
+	# the method params works, just like with non-template functions.
+	#  It also works with or without return type.
 	foreach linespec [list \
+			      "template2_fn" \
 			      "${method_name}" \
 			      "${method_name}${param_list}" \
 			      "${struct_type}::${method_name}" \
@@ -404,6 +615,218 @@ proc_with_prefix template-ret-type {} {
     }
 }
 
+# Test completion of function template foo.
+
+proc_with_prefix template-function-foo {} {
+
+    foreach cmd_prefix {"b" "b -function"} {
+	# "foo" is ambiguous, this will set many different breakpoints.
+	set completion_list \
+	    [list \
+		 [makefoo Aabiaai] \
+		 [makefoo Aabiabi] \
+		 [makefoo Aabicdi] \
+		 [makefoo AabicdiAabiaai] \
+		 [makefoo AabicdiAabiabi] \
+		 [makefoo AabicdiBabicdi] \
+		 [makefoo Babicdi] \
+		 [makefoo aai] \
+		 [makefoo aaiabi] \
+		 [makefoo aaicci] \
+		 [makefoo aaicdi] \
+		 [makefoo abi] \
+		 [makefoo anabnb] \
+		 [makefoo cci] \
+		 [makefoo cdi] \
+		 [makefoo NAnanbNBnanb] \
+		 [makefoo nanb]]
+	test_gdb_complete_multiple "$cmd_prefix " "foo" "<" $completion_list
+	check_bp_locations_match_list "$cmd_prefix foo" $completion_list
+
+	# "foo<" should give the same result, but it should not set any
+	# breakpoints.
+	test_gdb_complete_multiple "$cmd_prefix " "foo<" "" $completion_list
+	check_setting_bp_fails "$cmd_prefix foo<"
+
+	# "foo<A" should only give completions in COMPLETION_LIST that
+	# start with "A" but should set no breakpoints.
+	set completion_list \
+	    [list \
+		 [makefoo Aabiaai] \
+		 [makefoo Aabiabi] \
+		 [makefoo Aabicdi] \
+		 [makefoo AabicdiAabiaai] \
+		 [makefoo AabicdiAabiabi] \
+		 [makefoo AabicdiBabicdi]]
+	test_gdb_complete_multiple "$cmd_prefix " "foo<A" "<a<b<int> >, " \
+	    $completion_list
+	check_setting_bp_fails "$cmd_prefix foo<A"
+
+	# "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.
+	set completion_list \
+	    [list \
+		 [makefoo Aabiaai] \
+		 [makefoo Aabiabi] \
+		 [makefoo Aabicdi]]
+	test_gdb_complete_multiple "$cmd_prefix " "foo<A>" \
+	    "(A<a<b<int> >, " $completion_list
+	check_bp_locations_match_list "$cmd_prefix foo<A>" $completion_list
+
+	# "foo<A," should complete to any function with more than one
+	# parameter where the first parameter is any type of A.  Insufficient
+	# location to set breakpoints.
+	set completion_list \
+	    [list \
+		 [makefoo AabicdiAabiaai] \
+		 [makefoo AabicdiAabiabi] \
+		 [makefoo AabicdiBabicdi]]
+	test_gdb_complete_multiple "$cmd_prefix " "foo<A," " " \
+	    $completion_list
+	check_setting_bp_fails "$cmd_prefix foo<A,"
+
+	# "foo<A<a<b<int>, a" should give all completions starting with
+	# "Aabia" but it is insufficient to set breakpoints.
+	set completion_list \
+	    [list \
+		 [makefoo Aabiaai] \
+		 [makefoo Aabiabi]]
+	test_gdb_complete_multiple "$cmd_prefix " "foo<A<a<b<int> >, a" \
+	    "<" $completion_list
+	check_setting_bp_fails "$cmd_prefix foo<A<a<b<int> >, a"
+
+	# "foo<A<a<b<int>, a<" should yield the same results as above.
+	test_gdb_complete_multiple "$cmd_prefix " "foo<A<a<b<int> >, a<" \
+	    "" $completion_list
+	check_setting_bp_fails "$cmd_prefix foo<A<a<b<int> >, a<"
+
+	# "foo<A<a<b<int>, a<a" is unique but insufficient to set a
+	# breakpoint.  This has an ignored template parameter list, so
+	# the completion will contain an ignored list ("a<a>")
+	test_gdb_complete_unique "$cmd_prefix foo<A<a<b<int> >, a<a" \
+	    "$cmd_prefix [makefoo Aabiaai]"
+	check_setting_bp_fails "$cmd_prefix foo<A<b<int> >, a<a"
+
+	# "foo<A<a<b<int>, a<b" is also unique.  Same parameter ignoring
+	# happens here, too (except "a<b>").
+	test_gdb_complete_unique "$cmd_prefix foo<A<a<b<int> >, a<b" \
+	    "$cmd_prefix [makefoo Aabiabi]"
+	check_setting_bp_fails "$cmd_prefix foo<A<a<b<int> >, a<b"
+
+	# "foo<B" is unique but insufficient to set a breakpoint.
+	test_gdb_complete_unique "$cmd_prefix foo<B" \
+	    "$cmd_prefix [makefoo Babicdi]"
+	check_setting_bp_fails "$cmd_prefix foo<B"
+
+	# "foo<B>" yields the same unique result and sets a breakpoint.
+	# Since the input skips the parameter list, so does the completion.
+	test_gdb_complete_unique "$cmd_prefix foo<B>" \
+	    "$cmd_prefix foo<B>(B<a<b<int> >, c<d<int> > >)"
+	check_bp_locations_match_list "$cmd_prefix foo<B>" \
+	    [list [makefoo Babicdi]]
+
+	# "foo<B," should return no completions and no breakpoints.
+	test_gdb_complete_none "$cmd_prefix foo<B,"
+	check_setting_bp_fails "$cmd_prefix foo<B,"
+
+	# "foo<n::" should yield only the functions starting with
+	# "n" and "N" and no breakpoints.
+	set completion_list \
+	    [list \
+		 [makefoo NAnanbNBnanb] \
+		 [makefoo nanb]]
+	test_gdb_complete_multiple "$cmd_prefix " "foo<n::" "" \
+	    $completion_list
+	check_setting_bp_fails "$cmd_prefix foo<n::"
+
+	# "foo<A<a, c> >" is unique and sets a breakpoint.
+	# Multiple template parameter lists are skipped, so GDB will ignore
+	# them in the completion.
+	test_gdb_complete_unique "$cmd_prefix foo<A<a, c> >" \
+	    "$cmd_prefix foo<A<a, c> >(A<a<b<int> >, c<d<int> > >)"
+	check_bp_locations_match_list "$cmd_prefix foo<A<a, c> >" \
+	    [list [makefoo Aabicdi]]
+    }
+}
+
+# Helper for template-class-with-method to build completion lists.
+
+proc makem {arglist_list} {
+    set completion_list {}
+    foreach arglist $arglist_list {
+	lappend completion_list "[maketype $arglist]::method()"
+    }
+    return $completion_list
+}
+
+# Returns a list of elements that look like
+#   void TYPE::method()
+# where TYPE is derived from each arglist in ARGLIST_LIST.
+
+proc test_makem_1 {arglist_list expected_list} {
+    set result [makem $arglist_list]
+    send_log "makem $arglist = $result\n"
+    send_log "expecting $expected_list\n"
+
+    # Do list equality via canonical strings.
+    if {[expr {[list {*}$expected_list] eq [list {*}$result]}]} {
+	pass "makem unit test: $arglist"
+    } else {
+	fail "makem unit test: $arglist"
+    }
+}
+
+# Unit tests for makem.
+
+proc test_makem {} {
+    test_makem_1 ai {"a<int>::method()"}
+    test_makem_1 bi {"b<int>::method()"}
+    test_makem_1 {ai bi} {"a<int>::method()" "b<int>::method()"}
+    test_makem_1 {Aaiaai Bbibbi abi cdi} {
+	"A<a<int>, a<a<int> > >::method()"
+	"B<b<int>, b<b<int> > >::method()"
+	"a<b<int> >::method()"
+	"c<d<int> >::method()"
+    }
+}
+
+# Test class template containing a (non-templated) method called "method."
+
+proc_with_prefix template-class-with-method {} {
+
+    foreach {type type_list} \
+	[list \
+	     "" {aai abi cci cdi Aabicdi Aabiaai Aabiabi Babicdi} \
+	     "a" {aai abi} \
+	     "b" {} \
+	     "c" {cci cdi} \
+	     "A" {Aabicdi Aabiaai Aabiabi} \
+	     "B" {Babicdi} \
+	     "A<a, a>" {Aabiaai Aabiabi} \
+	     "A<a<b>, c>" {Aabicdi}\
+	     "A<a, d>" {} \
+	     "B<a, a>" {} \
+	     "B<a, b>" {} \
+	     "B<a, c>" {Babicdi}] \
+	{
+	    foreach cmd_prefix {"b" "b -function"} {
+		set c "$cmd_prefix "
+		if {$type != ""} {
+		    append c "${type}::"
+		}
+		append c "method"
+
+		if {[llength $type_list] > 0} {
+		    test_gdb_complete_unique $c "${c}()"
+		    check_bp_locations_match_list $c [makem $type_list]
+		} else {
+		    test_gdb_complete_none $c
+		}
+	    }
+	}
+}
+
 # Test completion of a const-overloaded funtion (const-overload).
 # Note that "const" appears after the function/method parameters.
 
@@ -947,6 +1370,10 @@ proc test_driver {} {
     overload-3
     template-overload
     template-ret-type
+    #test_makefoo
+    template-function-foo
+    #test_makem
+    template-class-with-method
     const-overload
     const-overload-quoted
     append-end-quote-char-when-unambiguous
diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.exp b/gdb/testsuite/gdb.linespec/cpls-ops.exp
index e8838c6f0f1..452b67c6fcf 100644
--- a/gdb/testsuite/gdb.linespec/cpls-ops.exp
+++ b/gdb/testsuite/gdb.linespec/cpls-ops.exp
@@ -248,20 +248,6 @@ proc test_operator_ambiguous {class_name opn cls} {
 	test_gdb_complete_multiple \
 	    "$cmd_prefix " "$linespec_noparams" "" $location_list
 
-	# Setting the breakpoint doesn't create a breakpoint location
-	# for the template, because immediately after
-	# "operator()/operator[]" we have the template parameters, not
-	# the parameter list.
-	set location_list \
-	    [list \
-		 "${class_name}::operator${opn}${cls}(int)" \
-		 "${class_name}::operator${opn}${cls}(long)"]
-	if {$opn == "("} {
-	    set location_list \
-		[concat \
-		     [list "${class_name}::operator${opn}${cls}()"] \
-		     $location_list]
-	}
 	check_bp_locations_match_list "$cmd_prefix $linespec_noparams" \
 	    $location_list
 	check_bp_locations_match_list "$cmd_prefix $linespec_noparams<int>" \
@@ -269,26 +255,33 @@ proc test_operator_ambiguous {class_name opn cls} {
 
 	# Test the template version.  Test both with and without
 	# return type.
-	test_gdb_complete_unique \
-	    "$cmd_prefix ${class_name}::operator${opn}${cls}<int>(in" \
-	    "$cmd_prefix ${class_name}::operator${opn}${cls}<int>(int*)"
-	check_bp_locations_match_list \
-	    "$cmd_prefix ${class_name}::operator${opn}${cls}<int>(int*)" \
-	    [list "${class_name}::operator${opn}${cls}<int>(int*)"]
-	test_gdb_complete_unique \
-	    "$cmd_prefix void ${class_name}::operator${opn}${cls}<int>(in" \
-	    "$cmd_prefix void ${class_name}::operator${opn}${cls}<int>(int*)"
-	check_bp_locations_match_list \
-	    "$cmd_prefix void ${class_name}::operator${opn}${cls}<int>(int*)" \
-	    [list "${class_name}::operator${opn}${cls}<int>(int*)"]
+	set f "${class_name}::operator"
+	foreach ws1 {"" " "} {
+	    foreach ws2 {"" " "} {
+		foreach ws3 {"" " "} {
+		    test_gdb_complete_unique \
+			"$cmd_prefix ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(in" \
+			"$cmd_prefix ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)"
+		    check_bp_locations_match_list \
+			"$cmd_prefix ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)" \
+			[list "${f}${opn}${cls}<int>(int*)"]
+		    test_gdb_complete_unique \
+			"$cmd_prefix void ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(in" \
+			"$cmd_prefix void ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)"
+		    check_bp_locations_match_list \
+			"$cmd_prefix void ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)" \
+			[list "${f}${opn}${cls}<int>(int*)"]
+		}
+	    }
+	}
 
 	# Add extra spaces.
 	test_gdb_complete_unique \
-	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( in" \
-	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int)"
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( lo" \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( long)"
 	check_bp_locations_match_list \
-	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int )" \
-	    [list "${class_name}::operator${opn}${cls}(int)"]
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( long )" \
+	    [list "${class_name}::operator${opn}${cls}(long)"]
     }
 }
 
diff --git a/gdb/testsuite/gdb.linespec/cpls.cc b/gdb/testsuite/gdb.linespec/cpls.cc
index 9da45e8fc0d..6b3ffd8bf07 100644
--- a/gdb/testsuite/gdb.linespec/cpls.cc
+++ b/gdb/testsuite/gdb.linespec/cpls.cc
@@ -146,7 +146,7 @@ namespace ns_overload3_test
   }
 }
 
-/* Code for the template-overload tests.  */
+/* Code for the template-function_foo (template parameter completion) tests.  */
 
 template <typename T>
 struct template_struct
@@ -163,6 +163,113 @@ T template_struct<T>::template_overload_fn (T t)
 template_struct<int> template_struct_int;
 template_struct<long> template_struct_long;
 
+/* Code for the template-parameter-overload test.  */
+
+template <typename T>
+void foo (T c) {}
+
+template <typename T1, typename T2>
+void foo (T1 a, T2 b) {}
+
+template <typename T>
+struct a
+{
+  void method () {}
+};
+
+template <typename T>
+struct b
+{
+  void method () {}
+};
+
+template <typename T>
+struct c
+{
+  void method () {}
+};
+
+template <typename T>
+struct d
+{
+  void method () {};
+};
+
+template <typename T1, typename T2>
+struct A
+{
+  void method () {}
+};
+
+template <typename T1, typename T2>
+struct B
+{
+  void method () {}
+};
+
+namespace n
+{
+  struct na {};
+  struct nb {};
+
+  template <typename T1, typename T2>
+  struct NA {};
+
+  template <typename T1, typename T2>
+  struct NB {};
+};
+
+static void
+template_function_foo ()
+{
+  a<a<int>> aa;
+  aa.method ();
+  a<b<int>> ab;
+  ab.method ();
+  c<c<int>> cc;
+  cc.method ();
+  c<d<int>> cd;
+  cd.method ();
+  foo (aa);
+  foo (ab);
+  foo (cc);
+  foo (cd);
+  foo (aa, ab);
+  foo (aa, cc);
+  foo (aa, cd);
+
+  A<a<b<int>>, c<d<int>>> Aabcd;
+  Aabcd.method ();
+  foo (Aabcd);
+
+  A<a<b<int>>, a<a<int>>> Aabaa;
+  Aabaa.method ();
+  foo (Aabaa);
+
+  A<a<b<int>>, a<b<int>>> Aabab;
+  Aabab.method ();
+  foo (Aabab);
+
+  B<a<b<int>>, c<d<int>>> Babcd;
+  Babcd.method ();
+  foo (Babcd);
+
+  foo (Aabcd, Babcd);
+  foo (Aabcd, Aabaa);
+  foo (Aabcd, Aabab);
+
+  n::na na;
+  n::nb nb;
+  foo (na, nb);
+  a<n::na> ana;
+  b<n::nb> bnb;
+  foo (ana, bnb);
+
+  n::NA<n::na, n::nb> NAnanb;
+  n::NB<n::na, n::nb> Nbnanb;
+  foo (NAnanb, Nbnanb);
+}
+
 /* Code for the template2-ret-type tests.  */
 
 template <typename T>
@@ -381,6 +488,7 @@ main ()
   template2_struct_inst.template2_fn<int, int> ();
   template_struct_int.template_overload_fn(0);
   template_struct_long.template_overload_fn(0);
+  template_function_foo ();
 
   return 0;
 }
diff --git a/gdb/utils.c b/gdb/utils.c
index c4d926cff84..d0fad8b2cb6 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2473,13 +2473,45 @@ skip_abi_tag (const char **name)
   return false;
 }
 
+/* If *NAME points at a template parameter list, skip it and return true.
+   Otherwise do nothing and return false.  */
+
+static bool
+skip_template_parameter_list (const char **name)
+{
+  const char *p = *name;
+
+  if (*p == '<')
+    {
+      const char *template_param_list_end = find_toplevel_char (p + 1, '>');
+
+      if (template_param_list_end == NULL)
+	return false;
+
+      p = template_param_list_end + 1;
+
+      /* Skip any whitespace that might occur after the closing of the
+	 parameter list, but only if it is the end of parameter list.  */
+      const char *q = p;
+      while (ISSPACE (*q))
+	++q;
+      if (*q == '>')
+	p = q;
+      *name = p;
+      return true;
+    }
+
+  return false;
+}
+
 /* See utils.h.  */
 
 int
 strncmp_iw_with_mode (const char *string1, const char *string2,
 		      size_t string2_len, strncmp_iw_mode mode,
 		      enum language language,
-		      completion_match_for_lcd *match_for_lcd)
+		      completion_match_for_lcd *match_for_lcd,
+		      bool ignore_template_params)
 {
   const char *string1_start = string1;
   const char *end_str2 = string2 + string2_len;
@@ -2529,6 +2561,48 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
 	    string1++;
 	}
 
+      /* Skip template parameters in STRING1 if STRING2 does not contain
+	 any.  E.g.:
+
+	 Case 1: User is looking for all functions named "foo".
+	 string1: foo <...> (...)
+	 string2: foo
+
+	 Case 2: User is looking for all methods named "foo" in all template
+	 class instantiations.
+	 string1: Foo<...>::foo <...> (...)
+	 string2: Foo::foo (...)
+
+	 Case 3: User is looking for a specific overload of a template
+	 function or method.
+	 string1: foo<...>
+	 string2: foo(...)
+
+	 Case 4: User is looking for a specific overload of a specific
+	 template instantiation.
+	 string1: foo<A> (...)
+	 string2: foo<B> (...)
+
+	 Case 5: User is looking wild parameter match.
+	 string1: foo<A<a<b<...> > > > (...)
+	 string2: foo<A
+      */
+      if (language == language_cplus && ignore_template_params
+	  && *string1 == '<' && *string2 != '<')
+	{
+	  /* Skip any parameter list in STRING1.  */
+	  const char *template_start = string1;
+
+	  if (skip_template_parameter_list (&string1))
+	    {
+	      /* Don't mark the parameter list ignored if the user didn't
+		 try to ignore it.  [Case #5 above]  */
+	      if (*string2 != '\0'
+		  && match_for_lcd != NULL && template_start != string1)
+		match_for_lcd->mark_ignored_range (template_start, string1);
+	    }
+	}
+
       if (*string1 == '\0' || string2 == end_str2)
 	break;
 
@@ -2637,6 +2711,12 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
 		    break;
 		  if (*string1 == '(' || *string2 == '(')
 		    break;
+
+		  /* If STRING1 or STRING2 starts with a template
+		     parameter list, break out of operator processing.  */
+		  skip_ws (string1, string2, end_str2);
+		  if (*string1 == '<' || *string2 == '<')
+		    break;
 		}
 
 	      continue;
diff --git a/gdb/utils.h b/gdb/utils.h
index 6f3a70213a4..c3ef28e2c27 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -63,11 +63,14 @@ enum class strncmp_iw_mode
 
    MATCH_FOR_LCD is passed down so that the function can mark parts of
    the symbol name as ignored for completion matching purposes (e.g.,
-   to handle abi tags).  */
+   to handle abi tags).  If IGNORE_TEMPLATE_PARAMS is true, all template
+   parameter lists will be ignored when language is C++.  */
+
 extern int strncmp_iw_with_mode
   (const char *string1, const char *string2, size_t string2_len,
    strncmp_iw_mode mode, enum language language,
-   completion_match_for_lcd *match_for_lcd = NULL);
+   completion_match_for_lcd *match_for_lcd = NULL,
+   bool ignore_template_params = false);
 
 /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
    differences in whitespace.  STRING2_LEN is STRING2's length.
-- 
2.31.1


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

* Re: [PATCH v3 3/3] Support template lookups in strncmp_iw_with_mode
  2021-12-09 19:17 ` [PATCH v3 3/3] Support template lookups in strncmp_iw_with_mode Keith Seitz
@ 2021-12-09 19:40   ` Eli Zaretskii
  2021-12-10 20:54     ` Keith Seitz
  0 siblings, 1 reply; 10+ messages in thread
From: Eli Zaretskii @ 2021-12-09 19:40 UTC (permalink / raw)
  To: Keith Seitz; +Cc: gdb-patches

> Date: Thu,  9 Dec 2021 11:17:50 -0800
> From: Keith Seitz via Gdb-patches <gdb-patches@sourceware.org>
> 
>  gdb/NEWS                                    |   4 +
>  gdb/cp-support.c                            |   8 +-
>  gdb/doc/gdb.texinfo                         |  46 +++
>  gdb/testsuite/gdb.cp/templates.cc           |  47 +++
>  gdb/testsuite/gdb.cp/templates.exp          |  67 +++
>  gdb/testsuite/gdb.linespec/cpcompletion.exp | 437 +++++++++++++++++++-
>  gdb/testsuite/gdb.linespec/cpls-ops.exp     |  53 ++-
>  gdb/testsuite/gdb.linespec/cpls.cc          | 110 ++++-
>  gdb/utils.c                                 |  82 +++-
>  gdb/utils.h                                 |   7 +-
>  10 files changed, 821 insertions(+), 40 deletions(-)

Thanks.

I find the documentation of this feature hard to understand.  I didn't
understand what this does until I've read all of the new text,
including the examples, twice.

I think the documentation could be a lot more self-explanatory if the
part about ignoring the template parameters were mentioned much
earlier.

> +* C++ developers may now ignore template parameter lists when specifying
> +  locations, e.g., while setting breakpoints.  This is analogous to how
> +  completion and symbol lookup handle overloaded functions or ABI tags.

This could be more clear if it described the GDB behavior, not what
GDB users can do due to changes in the GDB behavior.

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

* Re: [PATCH v3 3/3] Support template lookups in strncmp_iw_with_mode
  2021-12-09 19:40   ` Eli Zaretskii
@ 2021-12-10 20:54     ` Keith Seitz
  0 siblings, 0 replies; 10+ messages in thread
From: Keith Seitz @ 2021-12-10 20:54 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On 12/9/21 11:40, Eli Zaretskii wrote:
>> Date: Thu,  9 Dec 2021 11:17:50 -0800
>> From: Keith Seitz via Gdb-patches <gdb-patches@sourceware.org>
>>
>>   gdb/NEWS                                    |   4 +
>>   gdb/cp-support.c                            |   8 +-
>>   gdb/doc/gdb.texinfo                         |  46 +++
>>   gdb/testsuite/gdb.cp/templates.cc           |  47 +++
>>   gdb/testsuite/gdb.cp/templates.exp          |  67 +++
>>   gdb/testsuite/gdb.linespec/cpcompletion.exp | 437 +++++++++++++++++++-
>>   gdb/testsuite/gdb.linespec/cpls-ops.exp     |  53 ++-
>>   gdb/testsuite/gdb.linespec/cpls.cc          | 110 ++++-
>>   gdb/utils.c                                 |  82 +++-
>>   gdb/utils.h                                 |   7 +-
>>   10 files changed, 821 insertions(+), 40 deletions(-)
> 
> Thanks.
> 
> I find the documentation of this feature hard to understand.  I didn't
> understand what this does until I've read all of the new text,
> including the examples, twice.
> 
> I think the documentation could be a lot more self-explanatory if the
> part about ignoring the template parameters were mentioned much
> earlier.
> 
>> +* C++ developers may now ignore template parameter lists when specifying
>> +  locations, e.g., while setting breakpoints.  This is analogous to how
>> +  completion and symbol lookup handle overloaded functions or ABI tags.
> 
> This could be more clear if it described the GDB behavior, not what
> GDB users can do due to changes in the GDB behavior.
> 

Thank you for the comments. I will reassess/rewrite the documentation with
a fresh eye.

Keith


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

* Re: C++ template completion support v3
  2021-12-09 19:17 C++ template completion support v3 Keith Seitz
                   ` (2 preceding siblings ...)
  2021-12-09 19:17 ` [PATCH v3 3/3] Support template lookups in strncmp_iw_with_mode Keith Seitz
@ 2022-02-18 20:34 ` Tom Tromey
  2022-02-24 17:47   ` Keith Seitz
  3 siblings, 1 reply; 10+ messages in thread
From: Tom Tromey @ 2022-02-18 20:34 UTC (permalink / raw)
  To: Keith Seitz via Gdb-patches

>>>>> "Keith" == Keith Seitz via Gdb-patches <gdb-patches@sourceware.org> writes:

Keith> I would like to resurrect this patch series (after a very long hiatus).
Keith> There are minimal changes to the original series, last posted here:

Keith> https://sourceware.org/pipermail/gdb-patches/2020-April/167780.html

Keith> During that last series, Tom (Tromey) gave it the LGTM, but there was
Keith> a request to add unittests for strncmp_iw_with_mode. This new series
Keith> accommodates that request.

Keith> The second patch from the original submission ("Remove hard-coded
Keith> line number from templates.exp") is no longer necessary,
Keith> having been fixed by Andrew Burgess in November. 

Sorry about the delay on this.
I think this is OK.

thanks,
Tom

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

* Re: C++ template completion support v3
  2022-02-18 20:34 ` C++ template completion support v3 Tom Tromey
@ 2022-02-24 17:47   ` Keith Seitz
  2022-02-24 21:33     ` Tom Tromey
  0 siblings, 1 reply; 10+ messages in thread
From: Keith Seitz @ 2022-02-24 17:47 UTC (permalink / raw)
  To: Tom Tromey, Keith Seitz via Gdb-patches

On 2/18/22 12:34, Tom Tromey wrote:
> 
> Sorry about the delay on this.
> I think this is OK.

Haha. That makes two of us! ;-)

I will work with Eli to finalize the documentation
before pushing.

However, with gdb-12 on the horizon, would it be preferred
to wait on pushing this until after branching has occurred?

IMO, this is a pretty straightforward feature, but that's just
my $0.00002.

Keith


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

* Re: C++ template completion support v3
  2022-02-24 17:47   ` Keith Seitz
@ 2022-02-24 21:33     ` Tom Tromey
  2022-02-25  0:49       ` Keith Seitz
  0 siblings, 1 reply; 10+ messages in thread
From: Tom Tromey @ 2022-02-24 21:33 UTC (permalink / raw)
  To: Keith Seitz; +Cc: Tom Tromey, Keith Seitz via Gdb-patches

Keith> However, with gdb-12 on the horizon, would it be preferred
Keith> to wait on pushing this until after branching has occurred?

From what I recall it seemed fine for 12.

Tom

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

* Re: C++ template completion support v3
  2022-02-24 21:33     ` Tom Tromey
@ 2022-02-25  0:49       ` Keith Seitz
  0 siblings, 0 replies; 10+ messages in thread
From: Keith Seitz @ 2022-02-25  0:49 UTC (permalink / raw)
  To: Keith Seitz via Gdb-patches

On 2/24/22 13:33, Tom Tromey wrote:
> Keith> However, with gdb-12 on the horizon, would it be preferred
> Keith> to wait on pushing this until after branching has occurred?
> 
>  From what I recall it seemed fine for 12.
> 

I've pushed this after retesting. Thank you.

Keith


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

end of thread, other threads:[~2022-02-25  0:49 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-09 19:17 C++ template completion support v3 Keith Seitz
2021-12-09 19:17 ` [PATCH v3 1/3] Move find_toplevel_char to cp-support.[ch] Keith Seitz
2021-12-09 19:17 ` [PATCH v3 2/3] Unit tests for strncmp_iw_with_mode Keith Seitz
2021-12-09 19:17 ` [PATCH v3 3/3] Support template lookups in strncmp_iw_with_mode Keith Seitz
2021-12-09 19:40   ` Eli Zaretskii
2021-12-10 20:54     ` Keith Seitz
2022-02-18 20:34 ` C++ template completion support v3 Tom Tromey
2022-02-24 17:47   ` Keith Seitz
2022-02-24 21:33     ` Tom Tromey
2022-02-25  0:49       ` Keith Seitz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).