public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more
@ 2017-06-02 12:22 Pedro Alves
  2017-06-02 12:22 ` [PATCH 08/40] completion_list_add_name wrapper functions Pedro Alves
                   ` (40 more replies)
  0 siblings, 41 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:22 UTC (permalink / raw)
  To: gdb-patches

This series a long story behind it.  In sum, it:

 - teaches GDB to set breakpoints on all namespaces/scopes by default.
 - it adds a new option "break -qualified" to override the behavior.
 - adds a proper/smart linespec completer
 - gets rid of the need for quoting function names in tab completion

and much more, but that's the gist of it.

Most of the tests are close to the end of the series instead of being
added incrementally, because the way they're written, they only really
work when everything is blending nicely together.

Documentation changes are in the last patch.

A few months back I was looking at making GDB use gnulib's C++
namespace mode, because the default mode has gnulib define its
replacements using macros, like "#define open rpl_open".  In C++
that's a bad idea, given that using names like "open", "close",
etc. in classes and namespaces is the natural thing to do.  E.g.,
"struct target_ops::to_open" could be "class target::open".

After experimentation, I concluded that the best would be to wrap all
of GDB in:

 namespace gdb {
 }

and so I tried it:
 https://github.com/palves/gdb/commits/palves/cxx-gdb-namespace

however, when I actually got it working and started debugging that
GDB, I immediately got frustrated with GDB's C++ support.  I.e., the
dog food didn't taste so good.

The problem was that now you'd always need to prefix every function
name with "gdb::".  I.e., "b gdb::internal_error", etc., etc.  And it
gets annoying pretty fast.

I thought that GDB should be able to do better.  I thought that maybe
GDB could infer the namespace from local context, so you'd still be
able to do "b foo", and that would find "gdb::foo".  However, that
doesn't work well, because you want to be able to conveniently set
breakpoints before starting a program, when there's no frame yet.
And, running to main doesn't help, because main is not in any
namespace.  Also, the direction linespecs / setting breakpoints is
aiming, is in not relying in local context at all.  Instead, match the
most possible, and let users filter it down if they want.  I.e., e.g.,
"b foo" sets locations in all "foo" functions in the program in all
compilation units, no matter whether they're both extern or static
function, no matter where you're currently stopped in the program.
Likewise "b source.c:foo" looks at all source files named "source.c",
not just the current source if it happens to have that name.  You can
set set a breakpoint on a C++ symbol named "some_klass::method()",
even if the current frame's language is C, not C++.  Etc., etc.

The idea then occurred to me.  How about we make GDB ignore leading
namespaces when setting breakpoints?  I.e., just like "b foo" sets a
breakpoint on all "foo"s in the program, and you can zoom in on
specific "foo" functions by specifying a source file to scope the
symbol search, like "b source.c:foo", by default we'd make "b foo" set
a breakpoint on all functions and methods named "foo" too, like
"A::foo", "B::A::foo", "(anonymous namespace)::foo", and of course
global "foo".  And then if you scope the name, like "A::foo", GDB
would find both "A::foo", and "B::A::foo", but not the others.  Etc.,
etc.  E.g.,:

  (gdb) b function[TAB]
  A::function()
  B::A::function()
  function()
  (anonymous namespace)::function()
  (gdb) b function
  Breakpoint 1 at 0x4005ce: function. (4 locations)

  (gdb) b A::function[TAB]
  A::function()
  B::A::function()
  (gdb) b function
  Breakpoint 1 at 0x4005ce: function. (2 locations)

That sounded quite promising to me.  Turns out that that's what lldb
does too, so I started experimenting with doing the same in GDB.
Along the way, I realized that gdb's Ada support has always behaved
like that...  ada-lang.c calls it "wild matching" vs "full matching"
(see full_match and wild_match).  So it's not really a new idea for
GDB.  The problem is that Ada does that on its own little ada-lang.c
corner, out of view..  :-P

Actually, above I'm showing TAB completion.  But there's more to that.
Once it got it working for setting breakpoints, I realized that TAB
completion would need to be adjusted too.  TAB completion goes via
different symbol search code paths...  I required adjustment because
while "b foo[RET]" would find all functions named "foo", "b foo[TAB]"
would only show symbols that start with "foo"...

Fixing that required virtually rewriting how GDB interacts with
readline for completion.  The main issue is that you want this to
happen:

  (gdb) b func[TAB]      # expands input line to ...
  (gdb) b function(      # ... this
  (gdb) b function([TAB] # another tab now shows you the candidates
  A::B::function(int)
  A::C::function(long)

Notice how the input line above was expanded to "function(", but,
that's not actually the common leading prefix between all completion
candidates!  If we'd let readline compute what it calls the "lowest
common denominator", then this is what you'd see:

  (gdb) b func[TAB]      # expands input line to ...
  (gdb) b A::            # ... this...

which is obviously bogus.

Actually, notice how above I didn't quote "function(", like:

  (gdb) b 'function([TAB]
          ^ quote

That's because while working on this series, I got even more annoyed
with the fact that currently, you have overloads in your program, and
you want to set a breakpoint in one of them:

 void function(int);  // set breakpoint here.
 void function(long);

 (gdb) b func[TAB]
 (gdb) b function(       # ok, gdb completed as much as possible.
 (gdb) b function([TAB]  # show me the overloads, please.
 <_all_ symbols in the program are shown...>

E.g., when debugging GDB, that'd be:

 (gdb) b function([TAB]
 (anonymous namespace)::get_global()::global  pt_insn_get_offset@plt                       scm_new_port_table_entry
 asprintf                                     pt_pkt_alloc_decoder                         scm_new_port_table_entry@plt
 asprintf@plt                                 pt_pkt_alloc_decoder@plt                     scm_out_of_range
 bt_ctf_get_char_array                        pt_pkt_sync_forward                          scm_out_of_range@plt
 bt_ctf_get_char_array@plt                    pt_pkt_sync_forward@plt                      scm_putc
 bt_ctf_get_uint64                            pwrite                                       scm_putc@plt
 bt_ctf_get_uint64@plt                        pwrite@plt                                   scm_reverse_x
 bt_ctf_iter_read_event                       PyErr_Restore                                scm_reverse_x@plt
 bt_ctf_iter_read_event@plt                   PyErr_Restore@plt                            scm_set_port_filename_x
 <snip...>

Now that's a load of completely useless completions.

To get around that limitation, users need to quote the function name,
like:

 (gdb) b 'function([TAB]
 function(int)      function(long)
 (gdb) b 'function(i[TAB]
 (gdb) b 'function(int)' # now completes correctly!

Note that the quoting is only necessary for completion.  Creating the
breakpoint does not require the quoting:

 (gdb) b function(int) [RET]
 Breakpoint 1 at ....

This series eliminates the need for quoting, in both the explicit
locations completer, and the linespec completer.  Actually, the series
adds a real linespec completer because we didn't have a real one.

In the case above, we want the completer to figure out that it's
completing a function name that starts with "function(i".  It now
does.

On top of all that, the series fixes setting breakpoints on symbols
with [abi:cxx11] tags, and more...

Pedro Alves (40):
  Make gdb.base/dmsym.exp independent of "set language ada"
  Eliminate make_cleanup_obstack_free, introduce auto_obstack
  Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index
  Fix TAB-completion + .gdb_index slowness (generalize
    filename_seen_cache)
  command.h: Include scoped_restore_command.h
  Expression completer should not match explicit location options
  objfile_per_bfd_storage non-POD
  completion_list_add_name wrapper functions
  Rename make_symbol_completion_list_fn -> symbol_completer
  Clean up "completer_handle_brkchars" callback handling
  Introduce class completion_tracker & rewrite completion<->readline
    interaction
  "complete" command and completion word break characters
  Introduce strncmp_iw
  Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout
  Rewrite/enhance explicit locations completer, parse left->right
  Explicit locations -label completer
  Linespec lexing and C++ operators
  A smarter linespec completer
  Fix cp_find_first_component_aux bug
  Eliminate block_iter_name_*
  Use SYMBOL_MATCHES_SEARCH_NAME some more
  get_int_var_value
  Make language_def O(1)
  Per-language symbol name hashing algorithm
  Introduce lookup_name_info and generalize Ada's FULL/WILD name
    matching
  Optimize .gdb_index symbol name searching
  Make cp_remove_params return a unique_ptr
  lookup_name_info::make_ignore_params
  Simplify completion_list_add_name | remove sym_text / sym_text_len
  Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints
  Handle custom completion match prefix / LCD
  Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild
    matching]
  Make the linespec/location completer ignore data symbols
  Make strcmp_iw NOT ignore whitespace in the middle of tokens
  Comprehensive C++ linespec/completer tests
  Add comprehensive C++ operator linespec/location/completion tests
  Fix completing an empty string
  Use TOLOWER in SYMBOL_HASH_NEXT
  Breakpoints in symbols with ABI tags (PR c++/19436)
  Document breakpoints / linespec & co improvements (manual + NEWS)

 gdb/doc/gdb.texinfo                            |   26 +
 gdb/NEWS                                       |   36 +
 gdb/Makefile.in                                |    3 +
 gdb/ada-lang.c                                 |  786 +++++------
 gdb/ada-lang.h                                 |    2 +-
 gdb/ada-lex.l                                  |   22 +-
 gdb/ada-typeprint.c                            |    4 +-
 gdb/block.c                                    |  107 +-
 gdb/block.h                                    |   51 +-
 gdb/break-catch-syscall.c                      |   25 +-
 gdb/breakpoint.c                               |   23 +-
 gdb/buildsym.c                                 |   25 +-
 gdb/buildsym.h                                 |    4 +-
 gdb/c-exp.y                                    |   20 +-
 gdb/c-lang.c                                   |   46 +-
 gdb/c-typeprint.c                              |    2 +-
 gdb/cli/cli-cmds.c                             |   87 +-
 gdb/cli/cli-decode.c                           |   41 +-
 gdb/cli/cli-decode.h                           |   17 +-
 gdb/coffread.c                                 |    4 +-
 gdb/command.h                                  |   35 +-
 gdb/completer.c                                | 1786 +++++++++++++++++-------
 gdb/completer.h                                |  547 ++++++--
 gdb/corefile.c                                 |    5 +-
 gdb/cp-abi.c                                   |    5 +-
 gdb/cp-support.c                               |  588 +++++++-
 gdb/cp-support.h                               |   18 +-
 gdb/d-exp.y                                    |   11 +-
 gdb/d-lang.c                                   |    9 +-
 gdb/dbxread.c                                  |   12 +-
 gdb/defs.h                                     |    3 +-
 gdb/dictionary.c                               |  111 +-
 gdb/dictionary.h                               |   36 +-
 gdb/disasm.c                                   |    6 +-
 gdb/dwarf2loc.c                                |    7 +-
 gdb/dwarf2read.c                               |  919 ++++++++++--
 gdb/f-lang.c                                   |   22 +-
 gdb/filename-seen-cache.c                      |   73 +
 gdb/filename-seen-cache.h                      |   64 +
 gdb/gdb_obstack.h                              |   15 +
 gdb/gnu-v2-abi.c                               |    2 +-
 gdb/gnu-v3-abi.c                               |    2 +-
 gdb/go-exp.y                                   |    9 +-
 gdb/go-lang.c                                  |    9 +-
 gdb/guile/scm-cmd.c                            |   40 +-
 gdb/infrun.c                                   |   13 +-
 gdb/interps.c                                  |    8 +-
 gdb/interps.h                                  |    7 +-
 gdb/jit.c                                      |    6 +-
 gdb/language.c                                 |  301 ++--
 gdb/language.h                                 |   90 +-
 gdb/linespec.c                                 | 1074 ++++++++++++--
 gdb/linespec.h                                 |   29 +
 gdb/linux-tdep.c                               |    7 +-
 gdb/location.c                                 |  326 ++++-
 gdb/location.h                                 |   32 +-
 gdb/m2-lang.c                                  |    9 +-
 gdb/mdebugread.c                               |   23 +-
 gdb/minsyms.c                                  |  371 +++--
 gdb/minsyms.h                                  |    4 +-
 gdb/objc-lang.c                                |    8 +-
 gdb/objfiles.c                                 |   15 +-
 gdb/objfiles.h                                 |   39 +-
 gdb/opencl-lang.c                              |    8 +-
 gdb/p-lang.c                                   |   16 +-
 gdb/printcmd.c                                 |   12 +-
 gdb/psymtab.c                                  |  129 +-
 gdb/python/py-cmd.c                            |   52 +-
 gdb/rust-exp.y                                 |   15 +-
 gdb/rust-lang.c                                |   15 +-
 gdb/stabsread.h                                |    3 +-
 gdb/stack.c                                    |   18 +-
 gdb/symfile-debug.c                            |    6 +-
 gdb/symfile.c                                  |    4 +-
 gdb/symfile.h                                  |    4 +-
 gdb/symmisc.c                                  |    1 +
 gdb/symtab.c                                   |  704 +++++-----
 gdb/symtab.h                                   |  366 ++++-
 gdb/testsuite/gdb.ada/complete.exp             |   10 +
 gdb/testsuite/gdb.base/complete-empty.exp      |   46 +
 gdb/testsuite/gdb.base/completion.exp          |    8 +-
 gdb/testsuite/gdb.base/default.exp             |    2 +-
 gdb/testsuite/gdb.base/dmsym.c                 |    8 +-
 gdb/testsuite/gdb.base/dmsym.exp               |   39 +-
 gdb/testsuite/gdb.base/dmsym_main.c            |   10 +-
 gdb/testsuite/gdb.base/langs.exp               |    2 +-
 gdb/testsuite/gdb.base/printcmds.exp           |    6 +
 gdb/testsuite/gdb.cp/meth-typedefs.exp         |   34 +-
 gdb/testsuite/gdb.cp/namespace.exp             |    2 +-
 gdb/testsuite/gdb.linespec/base/one/thefile.cc |    5 +
 gdb/testsuite/gdb.linespec/base/two/thefile.cc |    5 +
 gdb/testsuite/gdb.linespec/cpcompletion.exp    |  959 +++++++++++++
 gdb/testsuite/gdb.linespec/cpls-abi-tag.cc     |   93 ++
 gdb/testsuite/gdb.linespec/cpls-abi-tag.exp    |  312 +++++
 gdb/testsuite/gdb.linespec/cpls-hyphen.cc      |   14 +
 gdb/testsuite/gdb.linespec/cpls-ops.cc         |  253 ++++
 gdb/testsuite/gdb.linespec/cpls-ops.exp        |  567 ++++++++
 gdb/testsuite/gdb.linespec/cpls.cc             |  386 +++++
 gdb/testsuite/gdb.linespec/cpls2.cc            |   46 +
 gdb/testsuite/gdb.linespec/explicit.exp        |  209 ++-
 gdb/testsuite/gdb.linespec/linespec.exp        |   93 +-
 gdb/testsuite/gdb.linespec/ls-errs.exp         |   27 +-
 gdb/testsuite/lib/completion-support.exp       |  513 +++++++
 gdb/top.c                                      |    2 +-
 gdb/tui/tui-layout.c                           |    5 +-
 gdb/tui/tui-regs.c                             |   11 +-
 gdb/tui/tui-win.c                              |   28 +-
 gdb/unittests/lookup_name_info-selftests.c     |  110 ++
 gdb/utils.c                                    |  382 ++++-
 gdb/utils.h                                    |   49 +-
 gdb/valprint.c                                 |   17 +-
 gdb/value.c                                    |   17 +-
 gdb/value.h                                    |    3 +-
 gdb/xcoffread.c                                |   11 +-
 114 files changed, 10826 insertions(+), 2838 deletions(-)
 create mode 100644 gdb/filename-seen-cache.c
 create mode 100644 gdb/filename-seen-cache.h
 create mode 100644 gdb/testsuite/gdb.base/complete-empty.exp
 create mode 100644 gdb/testsuite/gdb.linespec/cpcompletion.exp
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.exp
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-hyphen.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-ops.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-ops.exp
 create mode 100644 gdb/testsuite/gdb.linespec/cpls.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls2.cc
 create mode 100644 gdb/testsuite/lib/completion-support.exp
 create mode 100644 gdb/unittests/lookup_name_info-selftests.c

-- 
2.5.5

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

* [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (2 preceding siblings ...)
  2017-06-02 12:22 ` [PATCH 06/40] Expression completer should not match explicit location options Pedro Alves
@ 2017-06-02 12:22 ` Pedro Alves
  2017-06-26 13:47   ` Yao Qi
  2017-06-28 10:36   ` Yao Qi
  2017-06-02 12:22 ` [PATCH 01/40] Make gdb.base/dmsym.exp independent of "set language ada" Pedro Alves
                   ` (36 subsequent siblings)
  40 siblings, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:22 UTC (permalink / raw)
  To: gdb-patches

These changes in the parsers may not be obvious:

 -  obstack_init (&name_obstack);
 -  make_cleanup_obstack_free (&name_obstack);
 +  name_obstack.clear ();

Here, the 'name_obstack' variable is a global.  The change means that
the obstack's contents from a previous parse will stay around until
the next parsing starts.  I.e., memory won't be reclaimed until them.
I don't think that's a problem, these objects don't really grow much
at all.

The other option I tried was to add a separate type that is like
auto_obstack but manages an external obstack, just for those cases.  I
like the current approach better as that other approach adds more
boilerplate and yet another type to learn.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* c-exp.y (name_obstack): Now an auto_obstack.
	(yylex): Use auto_obstack::clear.
	(c_parse): Use auto_obstack::clear instead of reinitializing and
	freeing the obstack.
	* c-lang.c (evaluate_subexp_c): Use auto_obstack.
	* d-exp.y (name_obstack): Now an auto_obstack.
	(yylex): Use auto_obstack::clear.
	(d_parse): Use auto_obstack::clear instead of reinitializing and
	freeing the obstack.
	* dwarf2loc.c (fetch_const_value_from_synthetic_pointer): Use
	auto_obstack.
	* dwarf2read.c (create_addrmap_from_index)
	(dwarf2_build_psymtabs_hard)
	(update_enumeration_type_from_children, write_psymtabs_to_index):
	Likewise.
	* gdb_obstack.h (auto_obstack): New type.
	* go-exp.y (name_obstack): Now an auto_obstack.
	(build_packaged_name): Use auto_obstack::clear.
	(go_parse): Use auto_obstack::clear instead of reinitializing and
	freeing the obstack.
	* linux-tdep.c (linux_make_mappings_corefile_notes): Use
	auto_obstack.
	* printcmd.c (printf_wide_c_string, ui_printf): Use auto_obstack.
	* rust-exp.y (work_obstack): Now an auto_obstack.
	(rust_parse, rust_lex_tests): Use auto_obstack::clear instead of
	reinitializing and freeing the obstack.
	* utils.c (do_obstack_free, make_cleanup_obstack_free): Delete.
	(host_char_to_target): Use auto_obstack.
	* utils.h (make_cleanup_obstack_free): Delete declaration.
	* valprint.c (generic_emit_char, generic_printstr): Use
	auto_obstack.
---
 gdb/c-exp.y       | 10 ++++++----
 gdb/c-lang.c      |  7 +------
 gdb/d-exp.y       | 11 ++++++-----
 gdb/dwarf2loc.c   |  7 +------
 gdb/dwarf2read.c  | 37 +++++++------------------------------
 gdb/gdb_obstack.h | 15 +++++++++++++++
 gdb/go-exp.y      |  9 +++++----
 gdb/linux-tdep.c  |  7 +------
 gdb/printcmd.c    | 12 ++----------
 gdb/rust-exp.y    | 15 +++++++++------
 gdb/utils.c       | 24 +-----------------------
 gdb/utils.h       |  3 ---
 gdb/valprint.c    | 17 ++++-------------
 13 files changed, 58 insertions(+), 116 deletions(-)

diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 283b737..bdcd51f 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -2806,7 +2806,7 @@ static int popping;
 
 /* Temporary storage for c_lex; this holds symbol names as they are
    built up.  */
-static struct obstack name_obstack;
+auto_obstack name_obstack;
 
 /* Classify a NAME token.  The contents of the token are in `yylval'.
    Updates yylval and returns the new token type.  BLOCK is the block
@@ -3067,7 +3067,7 @@ yylex (void)
   current = *VEC_index (token_and_value, token_fifo, next_to_examine);
   ++next_to_examine;
 
-  obstack_free (&name_obstack, obstack_base (&name_obstack));
+  name_obstack.clear ();
   checkpoint = 0;
   if (current.token == FILENAME)
     search_block = current.value.bval;
@@ -3169,6 +3169,9 @@ c_parse (struct parser_state *par_state)
   gdb_assert (par_state != NULL);
   pstate = par_state;
 
+  /* Note that parsing (within yyparse) freely installs cleanups
+     assuming they'll be run here (below).  */
+
   back_to = make_cleanup (free_current_contents, &expression_macro_scope);
   make_cleanup_clear_parser_state (&pstate);
 
@@ -3197,8 +3200,7 @@ c_parse (struct parser_state *par_state)
 
   VEC_free (token_and_value, token_fifo);
   popping = 0;
-  obstack_init (&name_obstack);
-  make_cleanup_obstack_free (&name_obstack);
+  name_obstack.clear ();
 
   result = yyparse ();
   do_cleanups (back_to);
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index a6d533d..de8868b 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -570,15 +570,12 @@ evaluate_subexp_c (struct type *expect_type, struct expression *exp,
       {
 	int oplen, limit;
 	struct type *type;
-	struct obstack output;
-	struct cleanup *cleanup;
 	struct value *result;
 	c_string_type dest_type;
 	const char *dest_charset;
 	int satisfy_expected = 0;
 
-	obstack_init (&output);
-	cleanup = make_cleanup_obstack_free (&output);
+	auto_obstack output;
 
 	++*pos;
 	oplen = longest_to_int (exp->elts[*pos].longconst);
@@ -656,7 +653,6 @@ evaluate_subexp_c (struct type *expect_type, struct expression *exp,
 	      result = allocate_value (type);
 	    else
 	      result = value_cstring ("", 0, type);
-	    do_cleanups (cleanup);
 	    return result;
 	  }
 
@@ -702,7 +698,6 @@ evaluate_subexp_c (struct type *expect_type, struct expression *exp,
 				      obstack_object_size (&output),
 				      type);
 	  }
-	do_cleanups (cleanup);
 	return result;
       }
       break;
diff --git a/gdb/d-exp.y b/gdb/d-exp.y
index 62df737..d392a5c 100644
--- a/gdb/d-exp.y
+++ b/gdb/d-exp.y
@@ -1342,7 +1342,7 @@ static int popping;
 
 /* Temporary storage for yylex; this holds symbol names as they are
    built up.  */
-static struct obstack name_obstack;
+static auto_obstack name_obstack;
 
 /* Classify an IDENTIFIER token.  The contents of the token are in `yylval'.
    Updates yylval and returns the new token type.  BLOCK is the block
@@ -1480,7 +1480,7 @@ yylex (void)
      first try building up a name until we find the qualified module.  */
   if (current.token == UNKNOWN_NAME)
     {
-      obstack_free (&name_obstack, obstack_base (&name_obstack));
+      name_obstack.clear ();
       obstack_grow (&name_obstack, current.value.sval.ptr,
 		    current.value.sval.length);
 
@@ -1533,7 +1533,7 @@ yylex (void)
   if (current.token != TYPENAME && current.token != '.')
     goto do_pop;
 
-  obstack_free (&name_obstack, obstack_base (&name_obstack));
+  name_obstack.clear ();
   checkpoint = 0;
   if (current.token == '.')
     search_block = NULL;
@@ -1627,6 +1627,8 @@ d_parse (struct parser_state *par_state)
   gdb_assert (par_state != NULL);
   pstate = par_state;
 
+  /* Note that parsing (within yyparse) freely installs cleanups
+     assuming they're run here (below).  */
   back_to = make_cleanup (null_cleanup, NULL);
 
   scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
@@ -1639,8 +1641,7 @@ d_parse (struct parser_state *par_state)
 
   VEC_free (token_and_value, token_fifo);
   popping = 0;
-  obstack_init (&name_obstack);
-  make_cleanup_obstack_free (&name_obstack);
+  name_obstack.clear ();
 
   result = yyparse ();
   do_cleanups (back_to);
diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c
index 127167d..3f652a2 100644
--- a/gdb/dwarf2loc.c
+++ b/gdb/dwarf2loc.c
@@ -2125,13 +2125,10 @@ fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
 					  struct type *type)
 {
   struct value *result = NULL;
-  struct obstack temp_obstack;
-  struct cleanup *cleanup;
   const gdb_byte *bytes;
   LONGEST len;
 
-  obstack_init (&temp_obstack);
-  cleanup = make_cleanup_obstack_free (&temp_obstack);
+  auto_obstack temp_obstack;
   bytes = dwarf2_fetch_constant_bytes (die, per_cu, &temp_obstack, &len);
 
   if (bytes != NULL)
@@ -2148,8 +2145,6 @@ fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
   else
     result = allocate_optimized_out_value (TYPE_TARGET_TYPE (type));
 
-  do_cleanups (cleanup);
-
   return result;
 }
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index b58d0fc..cb33fc9 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -3057,13 +3057,11 @@ create_addrmap_from_index (struct objfile *objfile, struct mapped_index *index)
 {
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
   const gdb_byte *iter, *end;
-  struct obstack temp_obstack;
   struct addrmap *mutable_map;
-  struct cleanup *cleanup;
   CORE_ADDR baseaddr;
 
-  obstack_init (&temp_obstack);
-  cleanup = make_cleanup_obstack_free (&temp_obstack);
+  auto_obstack temp_obstack;
+
   mutable_map = addrmap_create_mutable (&temp_obstack);
 
   iter = index->address_table;
@@ -3104,7 +3102,6 @@ create_addrmap_from_index (struct objfile *objfile, struct mapped_index *index)
 
   objfile->psymtabs_addrmap = addrmap_create_fixed (mutable_map,
 						    &objfile->objfile_obstack);
-  do_cleanups (cleanup);
 }
 
 /* The hash function for strings in the mapped index.  This is the same as
@@ -6623,7 +6620,6 @@ static void
 dwarf2_build_psymtabs_hard (struct objfile *objfile)
 {
   struct cleanup *back_to, *addrmap_cleanup;
-  struct obstack temp_obstack;
   int i;
 
   if (dwarf_read_debug)
@@ -6646,8 +6642,7 @@ dwarf2_build_psymtabs_hard (struct objfile *objfile)
 
   /* Create a temporary address map on a temporary obstack.  We later
      copy this to the final obstack.  */
-  obstack_init (&temp_obstack);
-  make_cleanup_obstack_free (&temp_obstack);
+  auto_obstack temp_obstack;
   objfile->psymtabs_addrmap = addrmap_create_mutable (&temp_obstack);
   addrmap_cleanup = make_cleanup (psymtabs_addrmap_cleanup, objfile);
 
@@ -13793,15 +13788,12 @@ update_enumeration_type_from_children (struct die_info *die,
 				       struct type *type,
 				       struct dwarf2_cu *cu)
 {
-  struct obstack obstack;
   struct die_info *child_die;
   int unsigned_enum = 1;
   int flag_enum = 1;
   ULONGEST mask = 0;
-  struct cleanup *old_chain;
 
-  obstack_init (&obstack);
-  old_chain = make_cleanup_obstack_free (&obstack);
+  auto_obstack obstack;
 
   for (child_die = die->child;
        child_die != NULL && child_die->tag;
@@ -13846,8 +13838,6 @@ update_enumeration_type_from_children (struct die_info *die,
     TYPE_UNSIGNED (type) = 1;
   if (flag_enum)
     TYPE_FLAG_ENUM (type) = 1;
-
-  do_cleanups (old_chain);
 }
 
 /* Given a DW_AT_enumeration_type die, set its type.  We do not
@@ -23870,8 +23860,6 @@ write_psymtabs_to_index (struct objfile *objfile, const char *dir)
 {
   struct cleanup *cleanup;
   char *filename;
-  struct obstack contents, addr_obstack, constant_pool, symtab_obstack;
-  struct obstack cu_list, types_cu_list;
   int i;
   FILE *out_file;
   struct mapped_symtab *symtab;
@@ -23904,14 +23892,7 @@ write_psymtabs_to_index (struct objfile *objfile, const char *dir)
   symtab = create_mapped_symtab ();
   make_cleanup (cleanup_mapped_symtab, symtab);
 
-  obstack_init (&addr_obstack);
-  make_cleanup_obstack_free (&addr_obstack);
-
-  obstack_init (&cu_list);
-  make_cleanup_obstack_free (&cu_list);
-
-  obstack_init (&types_cu_list);
-  make_cleanup_obstack_free (&types_cu_list);
+  auto_obstack addr_obstack, cu_list, types_cu_list;
 
   htab_up psyms_seen (htab_create_alloc (100, htab_hash_pointer,
 					 htab_eq_pointer,
@@ -23987,14 +23968,10 @@ write_psymtabs_to_index (struct objfile *objfile, const char *dir)
      lists.  */
   uniquify_cu_indices (symtab);
 
-  obstack_init (&constant_pool);
-  make_cleanup_obstack_free (&constant_pool);
-  obstack_init (&symtab_obstack);
-  make_cleanup_obstack_free (&symtab_obstack);
+  auto_obstack constant_pool, symtab_obstack;
   write_hash_table (symtab, &symtab_obstack, &constant_pool);
 
-  obstack_init (&contents);
-  make_cleanup_obstack_free (&contents);
+  auto_obstack contents;
   size_of_contents = 6 * sizeof (offset_type);
   total_len = size_of_contents;
 
diff --git a/gdb/gdb_obstack.h b/gdb/gdb_obstack.h
index 3f34d96..b241c82 100644
--- a/gdb/gdb_obstack.h
+++ b/gdb/gdb_obstack.h
@@ -63,4 +63,19 @@ extern char *obconcat (struct obstack *obstackp, ...) ATTRIBUTE_SENTINEL;
 
 extern char *obstack_strdup (struct obstack *obstackp, const char *string);
 
+/* An obstack that frees itself on scope exit.  */
+struct auto_obstack : obstack
+{
+  auto_obstack ()
+  { obstack_init (this); }
+
+  ~auto_obstack ()
+  { obstack_free (this, NULL); }
+
+  /* Free all memory in the obstack but leave it valid for further
+     allocation.  */
+  void clear ()
+  { obstack_free (this, obstack_base (this)); }
+};
+
 #endif
diff --git a/gdb/go-exp.y b/gdb/go-exp.y
index 057e227..f2f3596 100644
--- a/gdb/go-exp.y
+++ b/gdb/go-exp.y
@@ -1297,7 +1297,7 @@ static int popping;
 
 /* Temporary storage for yylex; this holds symbol names as they are
    built up.  */
-static struct obstack name_obstack;
+static auto_obstack name_obstack;
 
 /* Build "package.name" in name_obstack.
    For convenience of the caller, the name is NUL-terminated,
@@ -1309,7 +1309,7 @@ build_packaged_name (const char *package, int package_len,
 {
   struct stoken result;
 
-  obstack_free (&name_obstack, obstack_base (&name_obstack));
+  name_obstack.clear ();
   obstack_grow (&name_obstack, package, package_len);
   obstack_grow_str (&name_obstack, ".");
   obstack_grow (&name_obstack, name, name_len);
@@ -1567,6 +1567,8 @@ go_parse (struct parser_state *par_state)
   gdb_assert (par_state != NULL);
   pstate = par_state;
 
+  /* Note that parsing (within yyparse) freely installs cleanups
+     assuming they'll be run here (below).  */
   back_to = make_cleanup (null_cleanup, NULL);
 
   scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
@@ -1579,8 +1581,7 @@ go_parse (struct parser_state *par_state)
 
   VEC_free (token_and_value, token_fifo);
   popping = 0;
-  obstack_init (&name_obstack);
-  make_cleanup_obstack_free (&name_obstack);
+  name_obstack.clear ();
 
   result = yyparse ();
   do_cleanups (back_to);
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 016aadf..db6db35 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -1503,16 +1503,12 @@ linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
 				    char *note_data, int *note_size)
 {
   struct cleanup *cleanup;
-  struct obstack data_obstack, filename_obstack;
   struct linux_make_mappings_data mapping_data;
   struct type *long_type
     = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch), 0, "long");
   gdb_byte buf[sizeof (ULONGEST)];
 
-  obstack_init (&data_obstack);
-  cleanup = make_cleanup_obstack_free (&data_obstack);
-  obstack_init (&filename_obstack);
-  make_cleanup_obstack_free (&filename_obstack);
+  auto_obstack data_obstack, filename_obstack;
 
   mapping_data.file_count = 0;
   mapping_data.data_obstack = &data_obstack;
@@ -1545,7 +1541,6 @@ linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
 				      obstack_object_size (&data_obstack));
     }
 
-  do_cleanups (cleanup);
   return note_data;
 }
 
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 02d6e1c..fd6e03c 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2305,8 +2305,6 @@ printf_wide_c_string (struct ui_file *stream, const char *format,
 					 "wchar_t", NULL, 0);
   int wcwidth = TYPE_LENGTH (wctype);
   gdb_byte *buf = (gdb_byte *) alloca (wcwidth);
-  struct obstack output;
-  struct cleanup *inner_cleanup;
 
   tem = value_as_address (value);
 
@@ -2325,8 +2323,7 @@ printf_wide_c_string (struct ui_file *stream, const char *format,
     read_memory (tem, str, j);
   memset (&str[j], 0, wcwidth);
 
-  obstack_init (&output);
-  inner_cleanup = make_cleanup_obstack_free (&output);
+  auto_obstack output;
 
   convert_between_encodings (target_wide_charset (gdbarch),
 			     host_charset (),
@@ -2335,7 +2332,6 @@ printf_wide_c_string (struct ui_file *stream, const char *format,
   obstack_grow_str0 (&output, "");
 
   fprintf_filtered (stream, format, obstack_base (&output));
-  do_cleanups (inner_cleanup);
 }
 
 /* Subroutine of ui_printf to simplify it.
@@ -2581,8 +2577,6 @@ ui_printf (const char *arg, struct ui_file *stream)
 	      struct type *wctype = lookup_typename (current_language, gdbarch,
 						     "wchar_t", NULL, 0);
 	      struct type *valtype;
-	      struct obstack output;
-	      struct cleanup *inner_cleanup;
 	      const gdb_byte *bytes;
 
 	      valtype = value_type (val_args[i]);
@@ -2592,8 +2586,7 @@ ui_printf (const char *arg, struct ui_file *stream)
 
 	      bytes = value_contents (val_args[i]);
 
-	      obstack_init (&output);
-	      inner_cleanup = make_cleanup_obstack_free (&output);
+	      auto_obstack output;
 
 	      convert_between_encodings (target_wide_charset (gdbarch),
 					 host_charset (),
@@ -2604,7 +2597,6 @@ ui_printf (const char *arg, struct ui_file *stream)
 
 	      fprintf_filtered (stream, current_substring,
                                 obstack_base (&output));
-	      do_cleanups (inner_cleanup);
 	    }
 	    break;
 	  case double_arg:
diff --git a/gdb/rust-exp.y b/gdb/rust-exp.y
index 64b7c55..c7361bc 100644
--- a/gdb/rust-exp.y
+++ b/gdb/rust-exp.y
@@ -185,7 +185,7 @@ static int unit_testing;
 
 /* Obstack for data temporarily allocated during parsing.  */
 
-static struct obstack work_obstack;
+static auto_obstack work_obstack;
 
 /* Result of parsing.  Points into work_obstack.  */
 
@@ -2446,13 +2446,17 @@ int
 rust_parse (struct parser_state *state)
 {
   int result;
-  struct cleanup *cleanup;
 
-  obstack_init (&work_obstack);
-  cleanup = make_cleanup_obstack_free (&work_obstack);
+  work_obstack.clear ();
+
   rust_ast = NULL;
 
   pstate = state;
+
+  /* Note that parsing (within rustyyparse) freely installs cleanups
+     assuming they're run here (below).  */
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+
   result = rustyyparse ();
 
   if (!result || (parse_completion && rust_ast != NULL))
@@ -2631,7 +2635,7 @@ rust_lex_tests (void)
 {
   int i;
 
-  obstack_init (&work_obstack);
+  work_obstack.clear ();
   unit_testing = 1;
 
   rust_lex_test_one ("", 0);
@@ -2722,7 +2726,6 @@ rust_lex_tests (void)
   rust_lex_test_completion ();
   rust_lex_test_push_back ();
 
-  obstack_free (&work_obstack, NULL);
   unit_testing = 0;
 }
 
diff --git a/gdb/utils.c b/gdb/utils.c
index b4332f8..00b1bbb 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -169,24 +169,6 @@ make_cleanup_fclose (FILE *file)
   return make_cleanup (do_fclose_cleanup, file);
 }
 
-/* Helper function which does the work for make_cleanup_obstack_free.  */
-
-static void
-do_obstack_free (void *arg)
-{
-  struct obstack *ob = (struct obstack *) arg;
-
-  obstack_free (ob, NULL);
-}
-
-/* Return a new cleanup that frees OBSTACK.  */
-
-struct cleanup *
-make_cleanup_obstack_free (struct obstack *obstack)
-{
-  return make_cleanup (do_obstack_free, obstack);
-}
-
 /* Helper function for make_cleanup_ui_out_redirect_pop.  */
 
 static void
@@ -1332,13 +1314,10 @@ query (const char *ctlstr, ...)
 static int
 host_char_to_target (struct gdbarch *gdbarch, int c, int *target_c)
 {
-  struct obstack host_data;
   char the_char = c;
-  struct cleanup *cleanups;
   int result = 0;
 
-  obstack_init (&host_data);
-  cleanups = make_cleanup_obstack_free (&host_data);
+  auto_obstack host_data;
 
   convert_between_encodings (target_charset (gdbarch), host_charset (),
 			     (gdb_byte *) &the_char, 1, 1,
@@ -1350,7 +1329,6 @@ host_char_to_target (struct gdbarch *gdbarch, int c, int *target_c)
       *target_c = *(char *) obstack_base (&host_data);
     }
 
-  do_cleanups (cleanups);
   return result;
 }
 
diff --git a/gdb/utils.h b/gdb/utils.h
index f3e8007..3347c23 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -78,9 +78,6 @@ extern struct cleanup *(make_cleanup_free_section_addr_info
 
 extern struct cleanup *make_cleanup_fclose (FILE *file);
 
-struct obstack;
-extern struct cleanup *make_cleanup_obstack_free (struct obstack *obstack);
-
 extern struct cleanup *make_cleanup_restore_integer (int *variable);
 extern struct cleanup *make_cleanup_restore_uinteger (unsigned int *variable);
 
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 6937dab..44810b9 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -2432,8 +2432,6 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
 {
   enum bfd_endian byte_order
     = gdbarch_byte_order (get_type_arch (type));
-  struct obstack wchar_buf, output;
-  struct cleanup *cleanups;
   gdb_byte *buf;
   int need_escape = 0;
 
@@ -2443,8 +2441,7 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
   wchar_iterator iter (buf, TYPE_LENGTH (type), encoding, TYPE_LENGTH (type));
 
   /* This holds the printable form of the wchar_t data.  */
-  obstack_init (&wchar_buf);
-  cleanups = make_cleanup_obstack_free (&wchar_buf);
+  auto_obstack wchar_buf;
 
   while (1)
     {
@@ -2491,8 +2488,7 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
     }
 
   /* The output in the host encoding.  */
-  obstack_init (&output);
-  make_cleanup_obstack_free (&output);
+  auto_obstack output;
 
   convert_between_encodings (INTERMEDIATE_ENCODING, host_charset (),
 			     (gdb_byte *) obstack_base (&wchar_buf),
@@ -2501,8 +2497,6 @@ generic_emit_char (int c, struct type *type, struct ui_file *stream,
   obstack_1grow (&output, '\0');
 
   fputs_filtered ((const char *) obstack_base (&output), stream);
-
-  do_cleanups (cleanups);
 }
 
 /* Return the repeat count of the next character/byte in ITER,
@@ -2761,7 +2755,6 @@ generic_printstr (struct ui_file *stream, struct type *type,
   enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type));
   unsigned int i;
   int width = TYPE_LENGTH (type);
-  struct obstack wchar_buf, output;
   struct cleanup *cleanup;
   int finished = 0;
   struct converted_character *last;
@@ -2833,8 +2826,7 @@ generic_printstr (struct ui_file *stream, struct type *type,
 
   /* WCHAR_BUF is the obstack we use to represent the string in
      wchar_t form.  */
-  obstack_init (&wchar_buf);
-  make_cleanup_obstack_free (&wchar_buf);
+  auto_obstack wchar_buf;
 
   /* Print the output string to the obstack.  */
   print_converted_chars_to_obstack (&wchar_buf, converted_chars, quote_char,
@@ -2844,8 +2836,7 @@ generic_printstr (struct ui_file *stream, struct type *type,
     obstack_grow_wstr (&wchar_buf, LCST ("..."));
 
   /* OUTPUT is where we collect `char's for printing.  */
-  obstack_init (&output);
-  make_cleanup_obstack_free (&output);
+  auto_obstack output;
 
   convert_between_encodings (INTERMEDIATE_ENCODING, host_charset (),
 			     (gdb_byte *) obstack_base (&wchar_buf),
-- 
2.5.5

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

* [PATCH 14/40] Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (4 preceding siblings ...)
  2017-06-02 12:22 ` [PATCH 01/40] Make gdb.base/dmsym.exp independent of "set language ada" Pedro Alves
@ 2017-06-02 12:22 ` Pedro Alves
  2017-07-14 18:04   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS) Pedro Alves
                   ` (34 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:22 UTC (permalink / raw)
  To: gdb-patches

I ran into LENGTH_OF_OPERATOR in cp-support.c and wanted to use it
elsewhere, so I moved it to cp-support.h.  Since there's already
CP_ANONYMOUS_NAMESPACE_STR/CP_ANONYMOUS_NAMESPACE_LEN there, I
followed the same naming pattern for the new symbols.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* c-exp.y (operator_stoken): Use CP_OPERATOR_LEN and
	CP_OPERATOR_STR.
	* c-typeprint.c (is_type_conversion_operator): Use
	CP_OPERATOR_STR.
	* cp-support.c (LENGTH_OF_OPERATOR): Delete.
	(cp_find_first_component_aux): Use CP_OPERATOR_STR and
	CP_OPERATOR_LEN.
	* cp-support.h (CP_OPERATOR_STR, CP_OPERATOR_LEN): New.
	* gnu-v2-abi.c (gnuv2_is_operator_name): Use CP_OPERATOR_STR.
	* gnu-v3-abi.c (gnuv3_is_operator_name): Use CP_OPERATOR_STR.
	* linespec.c (linespec_lexer_lex_string): Use CP_OPERATOR_LEN and
	CP_OPERATOR_STR.
	* location.c: Include "cp-support.h".
	(explicit_location_lex_one): Use CP_OPERATOR_LEN and
	CP_OPERATOR_STR.
	* symtab.c (operator_chars): Use CP_OPERATOR_STR and
	CP_OPERATOR_LEN.
---
 gdb/c-exp.y       | 5 ++---
 gdb/c-typeprint.c | 2 +-
 gdb/cp-support.c  | 9 ++-------
 gdb/cp-support.h  | 8 ++++++++
 gdb/gnu-v2-abi.c  | 2 +-
 gdb/gnu-v3-abi.c  | 2 +-
 gdb/linespec.c    | 5 ++---
 gdb/location.c    | 5 +++--
 gdb/symtab.c      | 4 ++--
 9 files changed, 22 insertions(+), 20 deletions(-)

diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index bdcd51f..24a2fbd 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -1625,13 +1625,12 @@ write_destructor_name (struct parser_state *par_state, struct stoken token)
 static struct stoken
 operator_stoken (const char *op)
 {
-  static const char *operator_string = "operator";
   struct stoken st = { NULL, 0 };
   char *buf;
 
-  st.length = strlen (operator_string) + strlen (op);
+  st.length = CP_OPERATOR_LEN + strlen (op);
   buf = (char *) malloc (st.length + 1);
-  strcpy (buf, operator_string);
+  strcpy (buf, CP_OPERATOR_STR);
   strcat (buf, op);
   st.ptr = buf;
 
diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c
index 9e197f5..890888b 100644
--- a/gdb/c-typeprint.c
+++ b/gdb/c-typeprint.c
@@ -547,7 +547,7 @@ is_type_conversion_operator (struct type *type, int i, int j)
      some other way, feel free to rewrite this function.  */
   const char *name = TYPE_FN_FIELDLIST_NAME (type, i);
 
-  if (!startswith (name, "operator"))
+  if (!startswith (name, CP_OPERATOR_STR))
     return 0;
 
   name += 8;
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 5704466..122fadd 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -931,10 +931,6 @@ cp_find_first_component (const char *name)
    the recursion easier, it also stops if it reaches an unexpected ')'
    or '>' if the value of PERMISSIVE is nonzero.  */
 
-/* Let's optimize away calls to strlen("operator").  */
-
-#define LENGTH_OF_OPERATOR 8
-
 static unsigned int
 cp_find_first_component_aux (const char *name, int permissive)
 {
@@ -1006,10 +1002,9 @@ cp_find_first_component_aux (const char *name, int permissive)
 	case 'o':
 	  /* Operator names can screw up the recursion.  */
 	  if (operator_possible
-	      && strncmp (name + index, "operator",
-			  LENGTH_OF_OPERATOR) == 0)
+	      && startswith (name + index, CP_OPERATOR_STR))
 	    {
-	      index += LENGTH_OF_OPERATOR;
+	      index += CP_OPERATOR_LEN;
 	      while (ISSPACE(name[index]))
 		++index;
 	      switch (name[index])
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 9054bf6..37b281f 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -46,6 +46,14 @@ struct using_direct;
 
 #define CP_ANONYMOUS_NAMESPACE_LEN 21
 
+/* A string representing the start of an operator name.  */
+
+#define CP_OPERATOR_STR "operator"
+
+/* The length of CP_OPERATOR_STR.  */
+
+#define CP_OPERATOR_LEN 8
+
 /* The result of parsing a name.  */
 
 struct demangle_parse_info
diff --git a/gdb/gnu-v2-abi.c b/gdb/gnu-v2-abi.c
index 0af684f..91c4201 100644
--- a/gdb/gnu-v2-abi.c
+++ b/gdb/gnu-v2-abi.c
@@ -68,7 +68,7 @@ gnuv2_is_vtable_name (const char *name)
 static int
 gnuv2_is_operator_name (const char *name)
 {
-  return startswith (name, "operator");
+  return startswith (name, CP_OPERATOR_STR);
 }
 
 \f
diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c
index 0090990..f5d3d13 100644
--- a/gdb/gnu-v3-abi.c
+++ b/gdb/gnu-v3-abi.c
@@ -46,7 +46,7 @@ gnuv3_is_vtable_name (const char *name)
 static int
 gnuv3_is_operator_name (const char *name)
 {
-  return startswith (name, "operator");
+  return startswith (name, CP_OPERATOR_STR);
 }
 
 
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 4c076fe..25ebdca 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -688,10 +688,9 @@ linespec_lexer_lex_string (linespec_parser *parser)
 	    {
 	      if ((PARSER_STATE (parser)->language->la_language
 		   == language_cplus)
-		  && (PARSER_STREAM (parser) - start) > 8
-		  /* strlen ("operator") */)
+		  && (PARSER_STREAM (parser) - start) > CP_OPERATOR_LEN)
 		{
-		  const char *p = strstr (start, "operator");
+		  const char *p = strstr (start, CP_OPERATOR_STR);
 
 		  if (p != NULL && is_operator_name (p))
 		    {
diff --git a/gdb/location.c b/gdb/location.c
index 8796320..d711d7b 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -24,6 +24,7 @@
 #include "linespec.h"
 #include "cli/cli-utils.h"
 #include "probe.h"
+#include "cp-support.h"
 
 #include <ctype.h>
 #include <string.h>
@@ -473,8 +474,8 @@ explicit_location_lex_one (const char **inp,
 	{
 	  /* Special case: C++ operator,.  */
 	  if (language->la_language == language_cplus
-	      && strncmp (*inp, "operator", 8) == 0)
-	    (*inp) += 8;
+	      && startswith (*inp, CP_OPERATOR_STR))
+	    (*inp) += CP_OPERATOR_LEN;
 	  ++(*inp);
 	}
     }
diff --git a/gdb/symtab.c b/gdb/symtab.c
index cd78a16..91e8b90 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3837,9 +3837,9 @@ static const char *
 operator_chars (const char *p, const char **end)
 {
   *end = "";
-  if (!startswith (p, "operator"))
+  if (!startswith (p, CP_OPERATOR_STR))
     return *end;
-  p += 8;
+  p += CP_OPERATOR_LEN;
 
   /* Don't get faked out by `operator' being part of a longer
      identifier.  */
-- 
2.5.5

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

* [PATCH 06/40] Expression completer should not match explicit location options
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
  2017-06-02 12:22 ` [PATCH 08/40] completion_list_add_name wrapper functions Pedro Alves
  2017-06-02 12:22 ` [PATCH 03/40] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index Pedro Alves
@ 2017-06-02 12:22 ` Pedro Alves
  2017-06-29  8:29   ` Yao Qi
  2017-06-02 12:22 ` [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack Pedro Alves
                   ` (37 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:22 UTC (permalink / raw)
  To: gdb-patches

Currently, the expression completer matches explicit location options,
which would only make sense for commands that work with linespecs, not
expressions.

I.e., currently, this:
 "p -functi"

Completes to:
 "p -function "

This patch fixes that, and adds regression tests.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.c (expression_completer): Call
	linespec_location_completer instead of location_completer.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/printcmds.exp: Add tests.
---
 gdb/completer.c                      | 2 +-
 gdb/testsuite/gdb.base/printcmds.exp | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/gdb/completer.c b/gdb/completer.c
index 6acf115..ee587fb 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -643,7 +643,7 @@ expression_completer (struct cmd_list_element *ignore,
     ;
 
   /* Not ideal but it is what we used to do before...  */
-  return location_completer (ignore, p, word);
+  return linespec_location_completer (ignore, text, word);
 }
 
 /* See definition in completer.h.  */
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index d949b30..323ca73 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -931,6 +931,12 @@ gdb_test "ptype \"abc\"" " = char \\\[4\\\]"
 gdb_test "print \$cvar = \"abc\"" " = \"abc\""
 gdb_test "print sizeof (\$cvar)" " = 4"
 
+# GDB used to complete the explicit location options even when
+# printing expressions.
+gdb_test_no_output "complete p -function"
+gdb_test_no_output "complete p -line"
+gdb_test_no_output "complete p -source"
+
 gdb_file_cmd ${binfile}
 
 gdb_test "print \$pc" "No registers\\." "print \$pc (with file)"
-- 
2.5.5

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

* [PATCH 03/40] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
  2017-06-02 12:22 ` [PATCH 08/40] completion_list_add_name wrapper functions Pedro Alves
@ 2017-06-02 12:22 ` Pedro Alves
  2017-07-13 20:28   ` Keith Seitz
  2017-06-02 12:22 ` [PATCH 06/40] Expression completer should not match explicit location options Pedro Alves
                   ` (38 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:22 UTC (permalink / raw)
  To: gdb-patches

This is the same patch as posted at
<https://sourceware.org/ml/gdb-patches/2017-02/msg00644.html>, with
the test at
<https://sourceware.org/ml/gdb-patches/2017-02/msg00687.html> squashed
in.

This patch fixes:

 -FAIL: gdb.base/completion.exp: tab complete break break.c:ma (timeout)
 -FAIL: gdb.base/completion.exp: complete break break.c:ma
 +PASS: gdb.base/completion.exp: tab complete break break.c:ma
 +PASS: gdb.base/completion.exp: delete breakpoint for tab complete break break.c:ma
 +PASS: gdb.base/completion.exp: complete break break.c:ma

When run with --target_board=dwarf4-gdb-index.

The issue here is that make_file_symbol_completion_list_1, used when
completing a symbol restricted to a given source file, uses
lookup_symtab to look up the symtab with the given name, and search
for matching symbols inside.  This assumes that there's only one
symtab for the given source file.  This is an incorrect assumption
with (for example) -fdebug-types-section, where we'll have an extra
extra symtab containing the types.  lookup_symtab finds that symtab,
and inside that symtab there are no functions...

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* symtab.c (make_file_symbol_completion_list_1): Iterate over
	symtabs matching all symtabs with SRCFILE as file name instead of
	only considering the first hit, with lookup_symtab.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.linespec/base/one/thefile.cc (z1): New function.
	* gdb.linespec/base/two/thefile.cc (z2): New function.
	* gdb.linespec/linespec.exp: Add tests.
---
 gdb/symtab.c                                   | 30 +++------
 gdb/testsuite/gdb.linespec/base/one/thefile.cc |  5 ++
 gdb/testsuite/gdb.linespec/base/two/thefile.cc |  5 ++
 gdb/testsuite/gdb.linespec/linespec.exp        | 93 +++++++++++++++++++++++++-
 4 files changed, 108 insertions(+), 25 deletions(-)

diff --git a/gdb/symtab.c b/gdb/symtab.c
index 22d81fa..69f3bc2 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -5385,7 +5385,6 @@ static VEC (char_ptr) *
 make_file_symbol_completion_list_1 (const char *text, const char *word,
 				    const char *srcfile)
 {
-  struct symtab *s;
   /* The symbol we are completing on.  Points in same buffer as text.  */
   const char *sym_text;
   /* Length of sym_text.  */
@@ -5436,28 +5435,15 @@ make_file_symbol_completion_list_1 (const char *text, const char *word,
 
   sym_text_len = strlen (sym_text);
 
-  /* Find the symtab for SRCFILE (this loads it if it was not yet read
-     in).  */
-  s = lookup_symtab (srcfile);
-  if (s == NULL)
+  /* Go through symtabs for SRCFILE and check the externs and statics
+     for symbols which match.  */
+  iterate_over_symtabs (srcfile, [&] (symtab *s)
     {
-      /* Maybe they typed the file with leading directories, while the
-	 symbol tables record only its basename.  */
-      const char *tail = lbasename (srcfile);
-
-      if (tail > srcfile)
-	s = lookup_symtab (tail);
-    }
-
-  /* If we have no symtab for that file, return an empty list.  */
-  if (s == NULL)
-    return (return_val);
-
-  /* Go through this symtab and check the externs and statics for
-     symbols which match.  */
-  add_symtab_completions (SYMTAB_COMPUNIT (s),
-			  sym_text, sym_text_len,
-			  text, word, TYPE_CODE_UNDEF);
+      add_symtab_completions (SYMTAB_COMPUNIT (s),
+			      sym_text, sym_text_len,
+			      text, word, TYPE_CODE_UNDEF);
+      return false;
+    });
 
   return (return_val);
 }
diff --git a/gdb/testsuite/gdb.linespec/base/one/thefile.cc b/gdb/testsuite/gdb.linespec/base/one/thefile.cc
index 0417b7a..34bc547 100644
--- a/gdb/testsuite/gdb.linespec/base/one/thefile.cc
+++ b/gdb/testsuite/gdb.linespec/base/one/thefile.cc
@@ -23,3 +23,8 @@ int NameSpace::overload(int x)
 {
   return x + 23;
 }
+
+int z1 ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/base/two/thefile.cc b/gdb/testsuite/gdb.linespec/base/two/thefile.cc
index 88188a5..264ae97 100644
--- a/gdb/testsuite/gdb.linespec/base/two/thefile.cc
+++ b/gdb/testsuite/gdb.linespec/base/two/thefile.cc
@@ -24,3 +24,8 @@ int NameSpace::overload(double x)
 {
   return (int) x - 23;
 }
+
+int z2 ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp
index ccb73c8..3a7bae5 100644
--- a/gdb/testsuite/gdb.linespec/linespec.exp
+++ b/gdb/testsuite/gdb.linespec/linespec.exp
@@ -43,6 +43,9 @@ if {$l1 != $l2} {
     error "somebody incompatibly modified the source files needed by linespec.exp"
 }
 
+gdb_test_no_output "set breakpoint pending off" \
+    "disable pending breakpoints for linespec tests"
+
 # Copying files to a remote host loses the directory prefix during
 # compilation.
 if { [is_remote host] } {
@@ -55,6 +58,93 @@ if { [is_remote host] } {
     gdb_test "clear one/thefile.cc:$l1" \
         "Deleted breakpoint $decimal *" \
         "clear breakpoint using dir/file:line"
+
+    if { [readline_is_used] } {
+	# There are functions name twodup in both source files.  Both
+	# should be found if we restrict the linespec to the ambiguous
+	# "thefile.cc" source filename.  Check both completion and
+	# setting the breakpoint.
+	set tst "complete unique function name in two source files"
+	send_gdb "break thefile.cc:t\t"
+	gdb_test_multiple "" $tst {
+	    -re "break thefile.cc:twodup\\(\\) " {
+		pass $tst
+
+		send_gdb "\n"
+		gdb_test "" \
+		    "Breakpoint $decimal at $hex: thefile.cc:twodup\\(\\). \[(\]2 locations\[)\]" \
+		    "set break at unique function name in two source files"
+	    }
+	}
+
+	# Check both completing and setting a breakpoint on a linespec
+	# with a source component, where there's more than one source
+	# file with the same basename.  We should find the functions
+	# in all matching sources -- one/thefile.cc and
+	# two/thefile.cc.  The "one" file has "z1()", while the "two"
+	# file has "z2()".
+	set tst "complete non-unique function name in two source files"
+	send_gdb "break thefile.cc:z\t"
+	gdb_test_multiple "" $tst {
+	    -re "break thefile.cc:z\\\x07" {
+		send_gdb "\t"
+		gdb_test_multiple "" $tst {
+		    -re "\r\nz1\\(\\)\[ \t\]+z2\\(\\)\[ \t\]+\r\n$gdb_prompt " {
+			pass $tst
+
+			send_gdb "\n"
+			gdb_test "" \
+			    "Function \"z\" not defined in \"thefile.cc\"." \
+			    "set break at non-unique function name in two source files"
+		    }
+		}
+	    }
+	}
+
+	# Now check that disambiguating the source path makes GDB only
+	# match the symbols in that file.  "z" should now have a
+	# unique completion to "z1()", and setting the breakpoint
+	# should find only one location.
+	set tst "complete unique function name in disambiguated source file"
+	send_gdb "break one/thefile.cc:z\t"
+	gdb_test_multiple "" $tst {
+	    -re "break one/thefile.cc:z1\\(\\) " {
+		pass $tst
+
+		send_gdb "\n"
+		gdb_test "" \
+		    "Breakpoint $decimal at $hex: file .*thefile.cc, line \[^\r\n\]*" \
+		    "set break at unique function name in disambiguated source file"
+		}
+	}
+
+	# Check that using a non-existing source path does not confuse
+	# gdb.  It should not match any symbol.
+	set dir_file "one/thefile.cc"
+	set non_existing "/some/non-existing/absolute/path/prefix/$dir_file"
+	set non_existing_re [string_to_regexp $non_existing]
+
+	set tst "complete functions in non-existing absolute path"
+	send_gdb "break $non_existing:\t"
+	gdb_test_multiple "" $tst {
+	    -re "break $non_existing_re:\\\x07" {
+		send_gdb "\t\t"
+		gdb_test_multiple "" $tst {
+		    -re "^\\\x07\\\x07" {
+			pass $tst
+
+			# There's a function called twodup in each of
+			# the thefile.cc files.  Make sure none is
+			# picked.
+			send_gdb "twodup\n"
+			gdb_test "" \
+			    "No source file named $non_existing_re." \
+			    "set break in function in non-existing absolute path"
+		    }
+		}
+	    }
+	}
+    }
 }
 
 gdb_test "break thefile.cc:$l1" \
@@ -73,9 +163,6 @@ gdb_test "break dupname:label" \
 # not the locations.
 gdb_test "complete condition " "condition $decimal\r\ncondition $decimal\r\ncondition $decimal"
 
-gdb_test_no_output "set breakpoint pending off" \
-    "disable pending breakpoints for linespec tests"
-
 # This is PR breakpoints/12856.
 gdb_test "break lspec.cc:nosuchfunction" \
     "Function \"nosuchfunction\" not defined in \"lspec.cc\"." \
-- 
2.5.5

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

* [PATCH 01/40] Make gdb.base/dmsym.exp independent of "set language ada"
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (3 preceding siblings ...)
  2017-06-02 12:22 ` [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack Pedro Alves
@ 2017-06-02 12:22 ` Pedro Alves
  2017-07-18 19:42   ` Simon Marchi
  2017-06-02 12:22 ` [PATCH 14/40] Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout Pedro Alves
                   ` (35 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:22 UTC (permalink / raw)
  To: gdb-patches

This test is using "set language ada" expecting that to cause GDB to
do Ada symbol name matching.  That won't work when GDB uses the
symbol's language to decide which symbol matching algorithm to use,
because the test's symbols are C symbols.

So generalize the test a bit to not rely on Ada name matching rules.

Confirmed that by undoing the original fix the test was written for,
the test still fails.

gdb/testsuite/ChangeLog:

	* gdb.base/dmsym.c (pck__foo__bar__minsym): Rename to ...
	(test_minsym): ... this, and make static.
	(get_pck__foo__bar__minsym): Rename to ...
	(get_test_minsym): ... this.
	* gdb.base/dmsym.exp (): Remove "set language ada" call.  Adjust
	symbol names and comments.
	* gdb.base/dmsym_main.c (get_pck__foo__bar__minsym): Rename to ...
	(get_test_minsym): ... this.
	(pck__foo__bar__minsym__2): Rename to ...
	(test_minsym): ... this.
	(main): Adjust.
---
 gdb/testsuite/gdb.base/dmsym.c      |  8 ++++----
 gdb/testsuite/gdb.base/dmsym.exp    | 39 +++++++++++++------------------------
 gdb/testsuite/gdb.base/dmsym_main.c | 10 +++++-----
 3 files changed, 23 insertions(+), 34 deletions(-)

diff --git a/gdb/testsuite/gdb.base/dmsym.c b/gdb/testsuite/gdb.base/dmsym.c
index f358b51..dccea23 100644
--- a/gdb/testsuite/gdb.base/dmsym.c
+++ b/gdb/testsuite/gdb.base/dmsym.c
@@ -15,11 +15,11 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-int pck__foo__bar__minsym = 123;
+static int test_minsym = 123;
 
 int
-get_pck__foo__bar__minsym (void)
+get_test_minsym (void)
 {
-  pck__foo__bar__minsym++;
-  return pck__foo__bar__minsym;
+  test_minsym++;
+  return test_minsym;
 }
diff --git a/gdb/testsuite/gdb.base/dmsym.exp b/gdb/testsuite/gdb.base/dmsym.exp
index a318080..191a319 100644
--- a/gdb/testsuite/gdb.base/dmsym.exp
+++ b/gdb/testsuite/gdb.base/dmsym.exp
@@ -44,42 +44,31 @@ clean_restart ${testfile}
 set num "\[0-9\]+"
 set addr "0x\[0-9a-zA-Z\]+"
 
-# Although the test program is written in C, the original problem
-# occurs only when the language is Ada. The use of a C program is
-# only a convenience to be able to exercise the original problem
-# without requiring an Ada compiler. In the meantime, temporarily
-# force the language to Ada.
-
-gdb_test_no_output "set lang ada"
-
-# Verify that setting a breakpoint on `pck__foo__bar__minsym' only
-# results in one location found (function pck__foo__bar__minsym__2).
-# A mistake would be to also insert a breakpoint where
-# pck__foo__bar__minsym is defined.  Despite the fact that there is
-# no debugging info available, this is a data symbol and thus should
-# not be used for breakpoint purposes.
-
-gdb_test "break pck__foo__bar__minsym" \
+# Verify that setting a breakpoint on `test_minsym' only results in
+# one location found.  A mistake would be to also insert a breakpoint
+# in the test_minsym data symbol in dmsym.c.  Despite the fact that
+# there is no debugging info available, this is a data symbol and thus
+# should not be used for breakpoint purposes.
+
+gdb_test "break test_minsym" \
          "Breakpoint $num at $addr.: file .*dmsym_main\\.c, line $num\\."
 
 # However, verify that the `info line' command, on the other hand,
 # finds both locations.
 
-gdb_test "info line pck__foo__bar__minsym" \
-         "Line $num of \".*dmsym_main\\.c\" .*\r\nNo line number information available for address $addr <pck__foo__bar__minsym>"
-
-gdb_test_no_output "set lang auto"
+gdb_test "info line test_minsym" \
+         "Line $num of \".*dmsym_main\\.c\" .*\r\nNo line number information available for address $addr <test_minsym>"
 
-# Now, run the program until we get past the call to
-# pck__foo__bar__minsym__2. Except when using hardware breakpoints,
-# inferior behavior is going to be affected if a breakpoint was
-# incorrectly inserted at pck__foo__bar__minsym.
+# Now, run the program until we get past the call to test_minsym.
+# Except when using hardware breakpoints, inferior behavior is going
+# to be affected if a breakpoint was incorrectly inserted at
+# test_minsym.
 
 gdb_breakpoint dmsym_main.c:[gdb_get_line_number "BREAK" dmsym_main.c]
 
 gdb_run_cmd
 gdb_test "" \
-         "Breakpoint $num, pck__foo__bar__minsym__2 \\(\\) at.*" \
+         "Breakpoint $num, test_minsym \\(\\) at.*" \
          "run until breakpoint at BREAK"
 
 gdb_test "continue" \
diff --git a/gdb/testsuite/gdb.base/dmsym_main.c b/gdb/testsuite/gdb.base/dmsym_main.c
index 99589b8..0338dc7 100644
--- a/gdb/testsuite/gdb.base/dmsym_main.c
+++ b/gdb/testsuite/gdb.base/dmsym_main.c
@@ -15,18 +15,18 @@
    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 get_pck__foo__bar__minsym (void);
+extern int get_test_minsym (void);
 
-int
-pck__foo__bar__minsym__2 (void)
+static int
+test_minsym (void)
 {
-  return get_pck__foo__bar__minsym ();
+  return get_test_minsym ();
 }
 
 int
 main (void)
 {
-  int val = pck__foo__bar__minsym__2 ();
+  int val = test_minsym ();
 
   if (val != 124) /* BREAK */
     return 1;
-- 
2.5.5

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

* [PATCH 08/40] completion_list_add_name wrapper functions
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
@ 2017-06-02 12:22 ` Pedro Alves
  2017-06-27 12:56   ` Yao Qi
  2017-06-02 12:22 ` [PATCH 03/40] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index Pedro Alves
                   ` (39 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:22 UTC (permalink / raw)
  To: gdb-patches

I've had to adjust these macros numerous times while working on this
series, adding/removing parameters while experimenting with designs.
At some point, I got fed up with fighting the preprocessor, and
decided to make them proper functions.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* symtab.c (COMPLETION_LIST_ADD_SYMBOL)
	(MCOMPLETION_LIST_ADD_SYMBOL): Delete macros, replace with ...
	(completion_list_add_symbol, completion_list_add_msymbol):
	... these new functions.
	(add_symtab_completions)
	(default_make_symbol_completion_list_break_on_1): Adjust.
---
 gdb/symtab.c | 40 ++++++++++++++++++++++++++--------------
 1 file changed, 26 insertions(+), 14 deletions(-)

diff --git a/gdb/symtab.c b/gdb/symtab.c
index 4414d71..183164d 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4779,18 +4779,8 @@ do_free_completion_list (void *list)
   free_completion_list ((VEC (char_ptr) **) list);
 }
 
-/* Helper routine for make_symbol_completion_list.  */
-
 static VEC (char_ptr) *return_val;
 
-#define COMPLETION_LIST_ADD_SYMBOL(symbol, sym_text, len, text, word) \
-      completion_list_add_name \
-	(SYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
-
-#define MCOMPLETION_LIST_ADD_SYMBOL(symbol, sym_text, len, text, word) \
-      completion_list_add_name \
-	(MSYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
-
 /* Tracker for how many unique completions have been generated.  Used
    to terminate completion list generation early if the list has grown
    to a size so large as to be useless.  This helps avoid GDB seeming
@@ -4860,6 +4850,28 @@ completion_list_add_name (const char *symname,
   }
 }
 
+/* completion_list_add_name wrapper for struct symbol.  */
+
+static void
+completion_list_add_symbol (symbol *sym,
+			    const char *sym_text, int sym_text_len,
+			    const char *text, const char *word)
+{
+  completion_list_add_name (SYMBOL_NATURAL_NAME (sym),
+			    sym_text, sym_text_len, text, word);
+}
+
+/* completion_list_add_name wrapper for struct minimal_symbol.  */
+
+static void
+completion_list_add_msymbol (minimal_symbol *sym,
+			     const char *sym_text, int sym_text_len,
+			     const char *text, const char *word)
+{
+  completion_list_add_name (MSYMBOL_NATURAL_NAME (sym),
+			    sym_text, sym_text_len, text, word);
+}
+
 /* ObjC: In case we are completing on a selector, look as the msymbol
    again and feed all the selectors into the mill.  */
 
@@ -5010,7 +5022,7 @@ add_symtab_completions (struct compunit_symtab *cust,
 	  if (code == TYPE_CODE_UNDEF
 	      || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
-	    COMPLETION_LIST_ADD_SYMBOL (sym,
+	    completion_list_add_symbol (sym,
 					sym_text, sym_text_len,
 					text, word);
 	}
@@ -5121,7 +5133,7 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
       ALL_MSYMBOLS (objfile, msymbol)
 	{
 	  QUIT;
-	  MCOMPLETION_LIST_ADD_SYMBOL (msymbol, sym_text, sym_text_len, text,
+	  completion_list_add_msymbol (msymbol, sym_text, sym_text_len, text,
 				       word);
 
 	  completion_list_objc_symbol (msymbol, sym_text, sym_text_len, text,
@@ -5168,14 +5180,14 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
 	  {
 	    if (code == TYPE_CODE_UNDEF)
 	      {
-		COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
+		completion_list_add_symbol (sym, sym_text, sym_text_len, text,
 					    word);
 		completion_list_add_fields (sym, sym_text, sym_text_len, text,
 					    word);
 	      }
 	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
-	      COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
+	      completion_list_add_symbol (sym, sym_text, sym_text_len, text,
 					  word);
 	  }
 
-- 
2.5.5

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

* [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436)
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (15 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 18/40] A smarter linespec completer Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-09 19:34   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 27/40] Make cp_remove_params return a unique_ptr Pedro Alves
                   ` (23 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

Trying to set a breakpoint in a function with an ABI tag does not work
currently.  E.g., debugging gdb itself, we see this with the
"string_printf" function:

 (top-gdb) b string_print                               [TAB]
 (top-gdb) b string_printf[abi:cxx11](char const*, ...) [RET]
 No source file named string_printf[abi.
 Make breakpoint pending on future shared library load? (y or [n])

Quoting doesn't help:
 (top-gdb) b 'string_printf[abi:cxx11]'(char const*, ...)
 malformed linespec error: unexpected string, "(char const*, ...)"
 (top-gdb) b 'string_printf[abi:cxx11](char const*, ...)'
 No source file named string_printf[abi.
 Make breakpoint pending on future shared library load? (y or [n]) n

This patch fixes this, and takes it a bit further.

The actual symbol name as demangled by libiberty's demangler is really

 string_printf[abi:cxx11](char const*, ...)

however, this patch makes it possible to set the breakpoint with

 string_printf(char const*, ...)

too.  I.e., ignoring the ABI tag.

And to match, it teaches the completer to complete the symbol name
without the ABI tag, i.e.,

  "string_pri<TAB>"  -> "string_printf(char const*, ...)"

If however, you really want to break on a symbol with the tag, then
you simply start writing the tag, and GDB will preserve it, like:

  "string_printf[a<TAB>"  -> "string_printf[abi:cxx11](char const*, ...)"

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.h (completion_match_for_lcd) <match,
	mark_ignored_range>: New methods.
	<finish>: Consider ignored ranges.
	<clear>: Clear ignored ranges.
	<m_ignored_ranges, m_finished_storage>: New fields.
	* cp-support.c (cp_search_name_hash): Ignore ABI tags.
	(cp_symbol_name_matches_1, cp_fq_symbol_name_matches): Pass the
	completion_match_for_lcd pointer to strncmp_iw_with_mode.
	(test_cp_symbol_name_cmp): Add [abi:...] tags unit tests.
	* language.c (default_symbol_name_matcher): Pass the
	completion_match_for_lcd pointer to strncmp_iw_with_mode.
	* linespec.c (linespec_lexer_lex_string): Don't tokenize ABI tags.
	* utils.c (skip_abi_tag): New function.
	(strncmp_iw_with_mode): Add completion_match_for_lcd parameter.
	Handle ABI tags.
	* utils.h (strncmp_iw_with_mode): Add completion_match_for_lcd
	parameter.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.linespec/cpls-abi-tag.cc: New file.
	* gdb.linespec/cpls-abi-tag.exp: New file.
---
 gdb/completer.h                             |  62 +++++-
 gdb/cp-support.c                            |  53 ++++-
 gdb/language.c                              |   5 +-
 gdb/linespec.c                              |   5 +
 gdb/testsuite/gdb.linespec/cpls-abi-tag.cc  |  93 +++++++++
 gdb/testsuite/gdb.linespec/cpls-abi-tag.exp | 312 ++++++++++++++++++++++++++++
 gdb/utils.c                                 |  96 ++++++++-
 gdb/utils.h                                 |  14 +-
 8 files changed, 622 insertions(+), 18 deletions(-)
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.exp

diff --git a/gdb/completer.h b/gdb/completer.h
index db781ab..16b1593 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -122,27 +122,81 @@ private:
    "b push_ba" on a C++ program usually completes to
    std::vector<...>::push_back, std::string::push_back etc.  In such
    case, the symbol comparison routine will set the LCD match to point
-   into the "push_back" substring within the symbol's name string.  */
+   into the "push_back" substring within the symbol's name string.
+   Also, in some cases, the symbol comparison routine will want to
+   ignore parts of the symbol name for LCD purposes, such as for
+   example symbols with abi tags in C++.  In such cases, the symbol
+   comparison routine will set MARK_IGNORED_RANGE to mark the ignored
+   substrings of the matched string.  The resulting LCD string with
+   the ignored parts stripped out is computed at the end of a
+   completion match sequence iff we had a positive match.  */
 class completion_match_for_lcd
 {
 public:
+  /* Get the resulting LCD, after a successful match.  */
+  const char *match ()
+  { return m_match; }
+
   /* Set the match for LCD.  See m_match's description.  */
   void set_match (const char *match)
   { m_match = match; }
 
-  /* Get the resulting LCD, after a successful match.  */
+  /* Mark the range between [BEGIN, END) as ignored.  */
+  void mark_ignored_range (const char *begin, const char *end)
+  { m_ignored_ranges.emplace_back (begin, end); }
+
+  /* Get the resulting LCD, after a successful match.  If there are
+     ignored ranges, then this builds a new string with the ignored
+     parts removed (and stores it internally).  As such, the result of
+     this call is only good for the current completion match
+     sequence.  */
   const char *finish ()
-  { return m_match; }
+  {
+    if (m_ignored_ranges.empty ())
+      return m_match;
+    else
+      {
+	m_finished_storage.clear ();
+
+	const char *prev = m_match;
+	for (const auto &range : m_ignored_ranges)
+	  {
+	    m_finished_storage.append (prev, range.first);
+	    prev = range.second;
+	  }
+	m_finished_storage.append (prev);
+
+	return m_finished_storage.c_str ();
+      }
+  }
 
   /* Prepare for another completion matching sequence.  */
   void clear ()
-  { m_match = NULL; }
+  {
+    m_match = NULL;
+    m_ignored_ranges.clear ();
+  }
 
 private:
   /* The completion match result for LCD.  This is usually either a
      pointer into to a substring within a symbol's name, or to the
      storage of the pairing completion_match object.  */
   const char *m_match;
+
+  /* The ignored substring ranges within M_MATCH.  E.g., if we were
+     looking for completion matches for C++ functions starting with
+       "functio"
+     and successfully match:
+       "function[abi:cxx11](int)"
+     the ignored ranges vector will contain an entry that delimits the
+     "[abi:cxx11]" substring, such that calling finish() results in:
+       "function(int)"
+   */
+  std::vector<std::pair<const char *, const char *>> m_ignored_ranges;
+
+  /* Storage used by the finish() method, if it has to compute a new
+     string.  */
+  std::string m_finished_storage;
 };
 
 /* Convenience aggregate holding info returned by the symbol name
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 4c353c5..0494a41 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1660,7 +1660,23 @@ cp_search_name_hash (const char *search_name)
   if (prefix_len != 0)
     search_name += prefix_len + 2;
 
-  return default_search_name_hash (search_name);
+  unsigned int hash = 0;
+  for (const char *string = search_name; *string != '\0'; ++string)
+    {
+      string = skip_spaces_const (string);
+
+      if (*string == '(')
+	break;
+
+      /* Ignore ABI tags such as "[abi:cxx11].  */
+      if (*string == '['
+	  && startswith (string + 1, "abi:")
+	  && string[5] != ':')
+	break;
+
+      hash = SYMBOL_HASH_NEXT (hash, *string);
+    }
+  return hash;
 }
 
 /* Helper for cp_symbol_name_matches (i.e., symbol_name_matcher_ftype
@@ -1705,11 +1721,13 @@ cp_symbol_name_matches_1 (const char *symbol_search_name,
 			  completion_match_result *comp_match_res)
 {
   const char *sname = symbol_search_name;
+  completion_match_for_lcd *match_for_lcd
+    = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL);
 
   while (true)
     {
       if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len,
-				mode, language_cplus) == 0)
+				mode, language_cplus, match_for_lcd) == 0)
 	{
 	  if (comp_match_res != NULL)
 	    {
@@ -1740,14 +1758,15 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
 {
   /* Get the demangled name.  */
   const std::string &name = lookup_name.cplus ().lookup_name ();
-
+  completion_match_for_lcd *match_for_lcd
+    = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL);
   strncmp_iw_mode mode = (lookup_name.completion_mode ()
 			  ? strncmp_iw_mode::NORMAL
 			  : strncmp_iw_mode::MATCH_PARAMS);
 
   if (strncmp_iw_with_mode (symbol_search_name,
 			    name.c_str (), name.size (),
-			    mode, language_cplus) == 0)
+			    mode, language_cplus, match_for_lcd) == 0)
     {
       if (comp_match_res != NULL)
 	{
@@ -1964,6 +1983,32 @@ test_cp_symbol_name_cmp ()
 		 "function(int)");
   CHECK_NOT_MATCH_C ("function()", "bar::function");
   CHECK_NOT_MATCH_C ("foo::function()", "::function");
+
+  /* Test ABI tag matching/ignoring.  */
+
+  /* If the symbol name has an ABI tag, but the lookup name doesn't,
+     then the ABI tag in the symbol name is ignored.  */
+  CHECK_MATCH_C ("function[abi:foo]()", "function");
+  CHECK_MATCH_C ("function[abi:foo](int)", "function");
+  CHECK_MATCH_C ("function[abi:foo]()", "function ()");
+  CHECK_NOT_MATCH_C ("function[abi:foo]()", "function (int)");
+
+  CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo]");
+  CHECK_MATCH_C ("function[abi:foo](int)", "function[abi:foo]");
+  CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo] ()");
+  CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function");
+  CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function");
+  CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo]");
+  CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function[abi:foo]");
+  CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] ()");
+  CHECK_NOT_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] (int)");
+
+  CHECK_MATCH_C ("function  [abi:foo][abi:bar] ( )", "function [abi:foo]");
+
+  /* If the symbol name does not have an ABI tag, while the lookup
+     name has one, then there's no match.  */
+  CHECK_NOT_MATCH_C ("function()", "function[abi:foo]()");
+  CHECK_NOT_MATCH_C ("function()", "function[abi:foo]");
 }
 
 /* If non-NULL, return STR wrapped in quotes.  Otherwise, return a
diff --git a/gdb/language.c b/gdb/language.c
index 377d748..6332c82 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -717,13 +717,14 @@ default_symbol_name_matcher (const char *symbol_search_name,
 			     completion_match_result *comp_match_res)
 {
   const std::string &name = lookup_name.name ();
-
+  completion_match_for_lcd *match_for_lcd
+    = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL);
   strncmp_iw_mode mode = (lookup_name.completion_mode ()
 			  ? strncmp_iw_mode::NORMAL
 			  : strncmp_iw_mode::MATCH_PARAMS);
 
   if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
-			    mode, language_minimal) == 0)
+			    mode, language_minimal, match_for_lcd) == 0)
     {
       if (comp_match_res != NULL)
 	{
diff --git a/gdb/linespec.c b/gdb/linespec.c
index d17dcfa..d593cd6 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -737,6 +737,11 @@ linespec_lexer_lex_string (linespec_parser *parser)
 	      if (PARSER_STREAM (parser)[1] == ':')
 		++(PARSER_STREAM (parser));
 
+	      /* Do not tokenize ABI tags such as "[abi:cxx11]".  */
+	      else if (PARSER_STREAM (parser) - start > 4
+		       && startswith (PARSER_STREAM (parser) - 4, "[abi"))
+		++(PARSER_STREAM (parser));
+
 	      /* Do not tokenify if the input length so far is one
 		 (i.e, a single-letter drive name) and the next character
 		 is a directory separator.  This allows Windows-style
diff --git a/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc b/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc
new file mode 100644
index 0000000..3916eda
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc
@@ -0,0 +1,93 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 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/>.  */
+
+#define ABI1 __attribute__ ((abi_tag ("tag1")))
+#define ABI2 __attribute__ ((abi_tag ("tag2")))
+#define ABI3 __attribute__ ((abi_tag ("tag3")))
+
+void ABI1
+test_abi_tag_function (int)
+{
+}
+
+void ABI1
+test_abi_tag_ovld_function ()
+{
+}
+
+void ABI1
+test_abi_tag_ovld_function (int)
+{
+}
+
+/* Code for the overload functions, different ABI tag test.  */
+
+void
+test_abi_tag_ovld2_function ()
+{
+}
+
+void ABI1
+test_abi_tag_ovld2_function (short)
+{
+}
+
+void ABI2
+test_abi_tag_ovld2_function (int)
+{
+}
+
+void ABI2
+test_abi_tag_ovld2_function (long)
+{
+}
+
+struct ABI1 test_abi_tag_struct
+{
+  ABI2 test_abi_tag_struct ();
+  ABI2 ~test_abi_tag_struct ();
+};
+
+test_abi_tag_struct::test_abi_tag_struct ()
+{}
+
+test_abi_tag_struct::~test_abi_tag_struct ()
+{}
+
+ABI3 test_abi_tag_struct s;
+
+/* Code for the abi-tag in parameters test.  */
+
+struct ABI2 abi_tag_param_struct1
+{};
+
+struct ABI2 abi_tag_param_struct2
+{};
+
+void
+test_abi_tag_in_params (abi_tag_param_struct1)
+{}
+
+void
+test_abi_tag_in_params (abi_tag_param_struct1, abi_tag_param_struct2)
+{}
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp b/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp
new file mode 100644
index 0000000..f4280c2
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp
@@ -0,0 +1,312 @@
+# Copyright 2017 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/>.
+
+# This file is part of the gdb testsuite.
+
+load_lib completion-support.exp
+
+standard_testfile cpls-abi-tag.cc
+
+if {[prepare_for_testing "failed to prepare" $testfile \
+	 [list $srcfile] {c++ debug}]} {
+    return -1
+}
+
+gdb_test_no_output "set max-completions unlimited"
+
+set timeout 5
+
+# Check that the explicit location completer manages to find the next
+# option name after a "-function function" option.  A useful test when
+# the -function options's argument is a C++ operator, which can
+# include characters like '-'.
+
+proc check_explicit_skips_function_argument {function} {
+    test_gdb_complete_unique \
+	"b -function $function -sour" \
+	"b -function $function -source"
+}
+
+# Helper function for the operator new/new[] tests.  CLASS_NAME is the
+# name of the class that contains the operator we're testing.
+# BRACKETS is set to [] if testing operator new[], and to empty if
+# testing operator new.
+
+proc_with_prefix test_abi_tag {} {
+    with_test_prefix "completion" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    # Complete all prefixes between "_funcio" and the full
+	    # prototype.  The ABI tag is not considered for actual
+	    # completion.
+
+	    with_test_prefix "skip tag" {
+		# set location "test_abi_tag_function\[abi:tag1\](int)"
+		set location "test_abi_tag_function(int)"
+		set line "$cmd_prefix $location"
+		set start [index_after "_functio" $line]
+		test_complete_prefix_range $line $start
+	    }
+
+	    # Now the same, but start completing at the [.  In that case,
+	    # GDB considers the ABI tag as part of actual completion.
+	    with_test_prefix "at tag" {
+		set location "test_abi_tag_function\[abi:tag1\](int)"
+		set line "$cmd_prefix $location"
+		set start [index_after "_function" $line]
+		test_complete_prefix_range $line $start
+	    }
+
+	    # Same, but with extra spaces.  Note that the original spaces in
+	    # the input line are preserved after completion.
+
+	    with_test_prefix "spaces" {
+		test_gdb_complete_unique \
+		    "$cmd_prefix test_abi_tag_function \[abi:tag1\] (" \
+		    "$cmd_prefix test_abi_tag_function \[abi:tag1\] (int)"
+
+		test_gdb_complete_unique \
+		    "$cmd_prefix test_abi_tag_function \[abi:tag1\] ( int " \
+		    "$cmd_prefix test_abi_tag_function \[abi:tag1\] ( int )"
+	    }
+	}
+    }
+
+    with_test_prefix "set breakpoints" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    # Test setting breakpoints.  If the symbol name has an ABI
+	    # tag, but the input linespec doesn't, then the ABI tag in the
+	    # symbol name is ignored.
+	    set linespec_list {
+		"test_abi_tag_function"
+		"test_abi_tag_function[abi:tag1]"
+		"test_abi_tag_function[abi:tag1](int)"
+		"test_abi_tag_function [abi:tag1]"
+		"test_abi_tag_function [abi:tag1] ( int )"
+		"test_abi_tag_function(int)"
+		"test_abi_tag_function (int)"
+		"test_abi_tag_function ( int )"
+	    }
+	    foreach linespec $linespec_list {
+		check_bp_locations_match_list \
+		    "$cmd_prefix $linespec" [list $location]
+	    }
+	}
+    }
+
+    with_test_prefix "set breakpoints wrong ABI tag" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    # Test setting breakpoints with the wrong ABI tag.  Should
+	    # fail to create the breakpoints.  Completion should not find
+	    # any match either.
+	    set linespec_list {
+		"test_abi_tag_function[abi:tag2]"
+		"test_abi_tag_function[abi:tag2](int)"
+		"test_abi_tag_function [abi:tag2]"
+		"test_abi_tag_function [abi:tag2] ( int )"
+	    }
+	    foreach linespec $linespec_list {
+		check_setting_bp_fails "$cmd_prefix $linespec"
+		test_gdb_complete_none "$cmd_prefix $linespec"
+	    }
+
+	}
+    }
+
+    # Test completion of overloaded functions with ABI tags.
+    with_test_prefix "completion of overloaded functions" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    set completion_list {
+		"test_abi_tag_ovld_function[abi:tag1]()"
+		"test_abi_tag_ovld_function[abi:tag1](int)"
+	    }
+
+	    # If the input string does not include the ABI tag, then
+	    # actual completion ignores it.
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_ovld_f" "unction(" \
+		$completion_list
+
+	    # Otherwise, it's considered.
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_ovld_function\[" "abi:tag1\](" \
+		$completion_list
+
+	}
+    }
+
+    # Test setting breakpoints on overloaded functions with ABI tags.
+    with_test_prefix "breakpoints on overloaded functions" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    set completion_list {
+		"test_abi_tag_ovld_function[abi:tag1]()"
+		"test_abi_tag_ovld_function[abi:tag1](int)"
+	    }
+	    set location_list {
+		"test_abi_tag_ovld_function"
+		"test_abi_tag_ovld_function[abi:tag1]"
+	    }
+	    foreach linespec $location_list {
+		check_bp_locations_match_list \
+		    "$cmd_prefix $linespec" $completion_list
+	    }
+
+	}
+    }
+
+    with_test_prefix "completion of overloaded functions different abi" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    # Test completion of overloaded functions with ABI tags.
+	    set completion_list {
+		"test_abi_tag_ovld2_function()"
+		"test_abi_tag_ovld2_function[abi:tag1](short)"
+		"test_abi_tag_ovld2_function[abi:tag2](int)"
+		"test_abi_tag_ovld2_function[abi:tag2](long)"
+	    }
+
+	    # If the input string does not include the ABI tag, then
+	    # actual completion ignores it.
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_ovld2_f" "unction(" \
+		$completion_list
+
+	    # Otherwise, it's considered.  Match stops at the part of
+	    # the tag that diverges, and the completion list only
+	    # shows matches with ABI tags.
+	    set completion_list {
+		"test_abi_tag_ovld2_function[abi:tag1](short)"
+		"test_abi_tag_ovld2_function[abi:tag2](int)"
+		"test_abi_tag_ovld2_function[abi:tag2](long)"
+	    }
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_ovld2_function\[" "abi:tag" \
+		$completion_list
+
+	    # If you disambiguate, matches include only locations for
+	    # the specified tag.
+	    set completion_list {
+		"test_abi_tag_ovld2_function[abi:tag2](int)"
+		"test_abi_tag_ovld2_function[abi:tag2](long)"
+	    }
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_ovld2_function\[abi:tag2" "\](" \
+		$completion_list
+
+	    test_gdb_complete_unique \
+		"$cmd_prefix test_abi_tag_ovld2_function\[abi:tag1" \
+		"$cmd_prefix test_abi_tag_ovld2_function\[abi:tag1\](short)"
+	}
+    }
+
+    with_test_prefix "completion of struct prefixes with tags" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    # Test completion of methods of structs with ABI tags.
+	    set completion_list {
+		"test_abi_tag_struct[abi:tag1]::test_abi_tag_struct[abi:tag2]()"
+		"test_abi_tag_struct[abi:tag1]::~test_abi_tag_struct[abi:tag2]()"
+	    }
+
+	    # If the input string does not include the ABI tag, then
+	    # actual completion ignores it.
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_struc" "t::" \
+		$completion_list
+
+	    # Otherwise, it's considered.
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_struct\[" "abi:tag1\]::" \
+		$completion_list
+
+	    # Mix and match different abi tag positions.
+	    test_gdb_complete_unique \
+		"$cmd_prefix test_abi_tag_struct::t" \
+		"$cmd_prefix test_abi_tag_struct::test_abi_tag_struct()"
+
+	    test_gdb_complete_unique \
+		"$cmd_prefix test_abi_tag_struct\[abi:tag1\]::t" \
+		"$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct()"
+
+	    test_gdb_complete_unique \
+		"$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct\[" \
+		"$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct\[abi:tag2\]()"
+	}
+    }
+
+    with_test_prefix "abi tag in parameters" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    # Complete all prefixes between "_funcio" and the full
+	    # prototype.  The ABI tag is not considered for actual
+	    # completion.
+
+	    set completion_list {
+		"test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2])"
+		"test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2], abi_tag_param_struct2[abi:tag2])"
+	    }
+	    # If the input string does not include the ABI tag, then
+	    # actual completion ignores it.
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_in_para" "ms(abi_tag_param_struct1" \
+		$completion_list
+
+	    # If OTOH the input string includes the ABI tag, then it
+	    # is considered.
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "test_abi_tag_in_params(abi_tag_param_struct1\[ab" "i:tag2\]"\
+		$completion_list
+
+	    set location_list {
+		"test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2], abi_tag_param_struct2[abi:tag2])"
+	    }
+
+	    set tags {"" "\[abi:tag2\]"}
+	    foreach tag1 $tags {
+		foreach tag2 $tags {
+		    set linespec "test_abi_tag_in_params(abi_tag_param_struct1${tag1}, abi_tag_param_struct2${tag2})"
+		    check_bp_locations_match_list \
+			"$cmd_prefix $linespec" $location_list
+		}
+	    }
+	}
+    }
+
+    # Check that the explicit location completer manages to find the
+    # option name after -function, when the -function's argument is a
+    # function with an ABI tag.
+    check_explicit_skips_function_argument \
+	"test_abi_tag_function\[abi:unknown\](int)"
+}
+
+test_abi_tag
+return
+
+# The testcase driver.  Calls all test procedures.
+
+proc test_driver {} {
+    operator-delete
+    operator-delete\[\]
+    operator-new
+    operator-new\[\]
+    operator()-unique
+    operator()-ambiguous
+    operator\[\]-unique
+    operator\[\]-ambiguous
+    ops-valid-ambiguous
+    ops-valid-unique
+    ops-invalid
+    conversion-operator
+    assignment-operator
+    arrow-operator
+}
+
+test_driver
diff --git a/gdb/utils.c b/gdb/utils.c
index 484c1ef..78578b3 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2504,12 +2504,40 @@ cp_is_operator (const char *string, const char *start)
 	  && !valid_identifier_name_char (string[CP_OPERATOR_LEN]));
 }
 
+/* If *NAME points at an ABI tag, skip it and return true.  Otherwise
+   leave *NAME unmodified and return false.  (see GCC's abi_tag
+   attribute), such names are demangled as e.g.,
+   "function[abi:cxx11]()".  */
+
+static bool
+skip_abi_tag (const char **name)
+{
+  const char *p = *name;
+
+  if (startswith (p, "[abi:"))
+    {
+      p += 5;
+
+      while (valid_identifier_name_char (*p))
+	p++;
+
+      if (*p == ']')
+	{
+	  p++;
+	  *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)
+		      enum language language,
+		      completion_match_for_lcd *match_for_lcd)
 {
   const char *string1_start = string1;
   const char *end_str2 = string2 + string2_len;
@@ -2528,6 +2556,37 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
 	  skip_spaces = false;
 	}
 
+      /* Skip [abi:cxx11] tags in the symbol name if the lookup name
+	 doesn't include them.  E.g.:
+
+	 string1: function[abi:cxx1](int)
+	 string2: function
+
+	 string1: function[abi:cxx1](int)
+	 string2: function(int)
+
+	 string1: Struct[abi:cxx1]::function()
+	 string2: Struct::function()
+
+	 string1: function(Struct[abi:cxx1], int)
+	 string2: function(Struct, int)
+      */
+      if (string2 == end_str2
+	  || (*string2 != '[' && !valid_identifier_name_char (*string2)))
+	{
+	  const char *abi_start = string1;
+
+	  /* There can be more than one tag.  */
+	  while (*string1 == '[' && skip_abi_tag (&string1))
+	    ;
+
+	  if (match_for_lcd != NULL && abi_start != string1)
+	    match_for_lcd->mark_ignored_range (abi_start, string1);
+
+	  while (isspace (*string1))
+	    string1++;
+	}
+
       if (*string1 == '\0' || string2 == end_str2)
 	break;
 
@@ -2661,7 +2720,40 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
   if (string2 == end_str2)
     {
       if (mode == strncmp_iw_mode::NORMAL)
-	return 0;
+	{
+	  /* Strip abi tag markers from the matched symbol name.
+	     Usually the ABI marker will be found on function name
+	     (automatically added because the function returns an
+	     object marked with an ABI tag).  However, it's also
+	     possible to see a marker in one of the function
+	     parameters, for example.
+
+	     string2 (lookup name):
+	       func
+	     symbol name:
+	       function(some_struct[abi:cxx11], int)
+
+	     and for completion LCD computation we want to say that
+	     the match was for:
+	       function(some_struct, int)
+	  */
+	  if (match_for_lcd != NULL)
+	    {
+	      while ((string1 = strstr (string1, "[abi:")) != NULL)
+		{
+		  const char *abi_start = string1;
+
+		  /* There can be more than one tag.  */
+		  while (skip_abi_tag (&string1) && *string1 == '[')
+		    ;
+
+		  if (abi_start != string1)
+		    match_for_lcd->mark_ignored_range (abi_start, string1);
+		}
+	    }
+
+	  return 0;
+	}
       else
 	return (*string1 != '\0' && *string1 != '(');
     }
diff --git a/gdb/utils.h b/gdb/utils.h
index 4ce263e..426e4f1 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -52,12 +52,14 @@ enum class strncmp_iw_mode
 
 /* Helper for strcmp_iw and strncmp_iw.  Exported so that languages
    can implement both NORMAL and MATCH_PARAMS variants in a single
-   function and defer part of the work to strncmp_iw_with_mode.  */
-extern int strncmp_iw_with_mode (const char *string1,
-				 const char *string2,
-				 size_t string2_len,
-				 strncmp_iw_mode mode,
-				 enum language language);
+   function and defer part of the work to strncmp_iw_with_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).  */
+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);
 
 /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
    differences in whitespace.  STRING2_LEN is STRING2's length.
-- 
2.5.5

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

* [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (5 preceding siblings ...)
  2017-06-02 12:22 ` [PATCH 14/40] Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-06-02 13:01   ` Eli Zaretskii
  2017-06-02 12:23 ` [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests Pedro Alves
                   ` (33 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

	* NEWS: Mention breakpoints, linespecs and explicit locations
	improvements.

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Linespec Locations): Document how "function" is
	interpreted in C++.
	(Explicit Locations): Document how "-function" is interpreted in
	C++.  Document -qualified.
---
 gdb/doc/gdb.texinfo | 26 ++++++++++++++++++++++++++
 gdb/NEWS            | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9fb70f6..59469e0 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7845,6 +7845,16 @@ name of @file{/build/trunk/gcc/expr.c}, but not
 Specifies the line that begins the body of the function @var{function}.
 For example, in C, this is the line with the open brace.
 
+For C@t{++}, @var{function} is interpreted as specifying all functions
+named @var{function} ignoring missing leading specifiers (namespaces
+and classes).
+
+For example, assuming a program with symbols named @code{A::B::func}
+and @code{B::func}, both commands @code{break func} and @code{break
+B::func} set a breakpoint on both symbols.  To override this, you can
+use the explicit location option @code{-qualified}.  @xref{Explicit
+Locations}.
+
 @item @var{function}:@var{label}
 Specifies the line where @var{label} appears in @var{function}.
 
@@ -7909,6 +7919,22 @@ on function locations unmodified by other options (such as @code{-label}
 or @code{-line}) refer to the line that begins the body of the function.
 In C, for example, this is the line with the open brace.
 
+For C@t{++}, @var{function} is interpreted as specifying all functions
+named @var{function} ignoring missing leading specifiers (namespaces
+and classes).
+
+For example, assuming a program with symbols named @code{A::B::func}
+and @code{B::func}, both commands @code{break -function func} and
+@code{break -function B::func} set a breakpoint on both symbols.
+
+@item -qualified @var{function}
+Like @code{-function}, but the value specifies a fully qualified name
+of a function.
+
+For example, assuming a C@t{++} program with symbols named
+@code{A::B::func} and @code{B::func}, the @code{break -qualified
+B::func} command sets a breakpoint on @code{B::func}, only.
+
 @item -label @var{label}
 The value specifies the name of a label.  When the function
 name is not specified, the label is searched in the function of the currently
diff --git a/gdb/NEWS b/gdb/NEWS
index 112aa2f..6f9ade0 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,42 @@
 
 *** Changes since GDB 8.0
 
+* Breakpoints / linespecs / explicit locations improvements
+
+  ** Breakpoints on C++ functions change
+
+    By default, breakpoints on functions are now interpreted as
+    specifying all functions with the given name ignoring missing
+    leading specifiers (namespaces and classes).
+
+    For example, assuming a C++ program with symbols named:
+
+      A::B::func
+      B::func
+
+    both commands "break func" and "break B::func" set a breakpoint on
+    both symbols.  The explicit location option "-function" was
+    changed accordingly.
+
+    To override this, you can specify an explicitly fully qualified
+    name, usign the new explicit location option "-qualified".  For
+    example, using the same C++ program, the "break -qualified
+    B::func" command sets a breakpoint on "B::func", only.
+
+  ** GDB now has a much improved linespec and explicit locations TAB
+     completion support, that better understands what you're
+     completing and offers better suggestions.
+
+  ** GDB can now complete function parameters in linespecs and
+     explicit locations, even without quoting.  When setting
+     breakpoints, quoting around functions names to help with
+     TAB-completion is generally no longer necessary.
+
+  ** GDB can now TAB-complete label symbol names.
+
+  ** GDB can now set breakpoints functions marked with [abi:cxx11]
+     tags.
+
 *** Changes in GDB 8.0
 
 * GDB now supports access to the PKU register on GNU/Linux. The register is
-- 
2.5.5

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

* [PATCH 18/40] A smarter linespec completer
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (14 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 19/40] Fix cp_find_first_component_aux bug Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-07-15  0:07   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436) Pedro Alves
                   ` (24 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

Continuing the theme of the explicit locations patch, this patch gets
rid of the need for quoting function names in linespec TAB completion.
To recap, when you have overloads in your program, and you want to set
a breakpoint in one of them:

 void function(int);  // set breakpoint here.
 void function(long);

 (gdb) b function(i[TAB]
 <all the symbols in the program that start with "i" are uselessly shown...>

This patch gets rid of the need for quoting by switching the linespec
completer to use the custom completion word point mechanism added in
the previous explicit location patch (extending it as needed), to
correctly determine the right completion word point.  In the case
above, we want the completer to figure out that it's completing a
function name that starts with "function(i", and it now does.

We also want the completer to know when it's potentially completing a
source file name, for:

(gdb) break source.[TAB] -> source.c:
(gdb) break source.c:  # Type line number or function name now

And we want it to know to complete label names, which it doesn't today:

(gdb) break function:lab[TAB]

etc., etc.

So what we want is for completion to grok the input string as closely
to how the linespec parser groks it.

With that in mind, the solution suggests itself - make the linespec
completer use the same parsing code as normal linespec parsing.

That's what the patch does.  The old completer is replaced by one that
reuses the actual linespec parser as much as possible.  This (ideally)
eliminate differences between what completion understands and actually
setting breakpoints understands by design.

The completer now offers sensible completion candidates depending on
which component of the linespec is being completed, source filename,
function, line number, expression, and (a new addition), labels.  For
example, when completing the function part, we now show the full name
of the method as completion candidates, instead of showing whatever
comes after what readline considered the word break character:

 (gdb) break klass::method[TAB]
 klass:method1(int)
 klass:method2()

If input is past the function, then we now offer keyword condidates:

  (gdb) b function(int) [TAB]
  if      task    thread

If input is past a keyword, we offer expression completion, which is
different from linespec completion:

  (gdb) b main if 1 + glo[TAB]
  global

(e.g., completes on types, struct data fields, etc.)

As mentioned, this teaches the linespec completer about completing
label symbols too:

  (gdb) b source.c:function:lab[TAB]

A nice convenience is that when completion uniquely matches a source
name, gdb adds the ":" automatically for you:

  (gdb) b filenam[TAB]
  (gdb) b filename.c:  # ':' auto-added, cursor right after it.

It's the little details.  :-)

I worked on this patch in parallel with writing the (big) testcase
added closer to the end of the series, which exercises many many
tricky cases around quoting and whitespace insertion placement.  In
general, I think it now all Just Words.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.c (complete_source_filenames): New function.
	(complete_address_and_linespec_locations): New function.
	(location_completer): Use complete_address_and_linespec_locations.
	(completion_tracker::build_completion_result): Honor the tracker's
	request to suppress append.
	* completer.h (completion_tracker::suppress_append_ws)
	(completion_tracker::set_suppress_append_ws): New methods.
	(completion_tracker::m_suppress_append_ws): New field.
	(complete_source_filenames): New declaration.
	* linespec.c (linespec_complete_what): New.
	(struct ls_parser) <complete_what, completion_word,
	completion_quote_char, completion_quote_end, completion_tracker>:
	New fields.
	(string_find_incomplete_keyword_at_end): New.
	(linespec_lexer_lex_string): Record quote char.  If in completion
	mode, don't throw.
	(linespec_lexer_consume_token): Advance the completion word point.
	(linespec_lexer_peek_token): Save/restore completion info.
	(save_stream_and_consume_token): New.
	(set_completion_after_number): New.
	(linespec_parse_basic): Set what to complete next depending on
	token.  Handle function and label completions specially.
	(parse_linespec): Disable objc shortcut in completion mode.  Set
	what to complete next depending on token type.  Skip keyword if in
	completion mode.
	(complete_linespec_component, linespec_complete): New.
	* linespec.h (linespec_complete): Declare.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

        * gdb.linespec/ls-errs.exp: Don't sent tab characters, now that
        the completer works.
---
 gdb/completer.c                        |  76 +++--
 gdb/completer.h                        |  23 ++
 gdb/linespec.c                         | 595 +++++++++++++++++++++++++++++++--
 gdb/linespec.h                         |   5 +
 gdb/testsuite/gdb.linespec/ls-errs.exp |   9 +-
 5 files changed, 647 insertions(+), 61 deletions(-)

diff --git a/gdb/completer.c b/gdb/completer.c
index c7986e6..44a60ad 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -45,9 +45,6 @@
 
 #include "completer.h"
 
-static void complete_expression (completion_tracker &tracker,
-				 const char *text, const char *word);
-
 /* Misc state that needs to be tracked across several different
    readline completer entry point calls, all related to a single
    completion invocation.  */
@@ -564,8 +561,47 @@ complete_files_symbols (completion_tracker &tracker,
     }
 }
 
+/* Return a list of all source files whose names begin with matching
+   TEXT.  */
+
+completion_list
+complete_source_filenames (const char *text)
+{
+  size_t text_len = strlen (text);
+
+  /* If text includes characters which cannot appear in a file name,
+     the user cannot be asking for completion on files.  */
+  if (strcspn (text,
+	       gdb_completer_file_name_break_characters)
+      == text_len)
+    return make_source_files_completion_list (text, text);
+
+  return {};
+}
+
+/* Complete address and linespec locations.  */
+
+static void
+complete_address_and_linespec_locations (completion_tracker &tracker,
+					 const char *text)
+{
+  if (*text == '*')
+    {
+      tracker.advance_custom_word_point_by (1);
+      text++;
+      const char *word
+	= advance_to_expression_complete_word_point (tracker, text);
+      complete_expression (tracker, text, word);
+    }
+  else
+    {
+      linespec_complete (tracker, text);
+    }
+}
+
 /* The explicit location options.  Note that indexes into this array
    must match the explicit_location_match_type enumerators.  */
+
 static const char *const explicit_options[] =
   {
     "-source",
@@ -807,7 +843,7 @@ complete_explicit_location (completion_tracker &tracker,
 void
 location_completer (struct cmd_list_element *ignore,
 		    completion_tracker &tracker,
-		    const char *text, const char *word_entry)
+		    const char *text, const char * /* word */)
 {
   int found_probe_option = -1;
 
@@ -878,27 +914,7 @@ location_completer (struct cmd_list_element *ignore,
   else
     {
       /* This is an address or linespec location.  */
-      if (*text == '*')
-	{
-	  tracker.advance_custom_word_point_by (1);
-	  text++;
-	  const char *word
-	    = advance_to_expression_complete_word_point (tracker, text);
-	  complete_expression (tracker, text, word);
-	}
-      else
-	{
-	  /* Fall back to the old linespec completer, for now.  */
-
-	  if (word_entry == NULL)
-	    {
-	     /* We're in the handle_brkchars phase.  */
-	      tracker.set_use_custom_word_point (false);
-	      return;
-	    }
-
-	  complete_files_symbols (tracker, text, word_entry);
-	}
+      complete_address_and_linespec_locations (tracker, text);
     }
 
   /* Add matches for option names, if either:
@@ -994,7 +1010,7 @@ add_struct_fields (struct type *type, completion_list &output,
    names, but some language parsers also have support for completing
    field names.  */
 
-static void
+void
 complete_expression (completion_tracker &tracker,
 		     const char *text, const char *word)
 {
@@ -1945,10 +1961,12 @@ completion_tracker::build_completion_result (const char *text,
 				buf, (char *) NULL);
       match_list[1] = NULL;
 
-      /* If we already have a space at the end of the match, tell
-	 readline to skip appending another.  */
+      /* If the tracker wants to, or we already have a space at the
+	 end of the match, tell readline to skip appending
+	 another.  */
       bool completion_suppress_append
-	= (match_list[0][strlen (match_list[0]) - 1] == ' ');
+	= (suppress_append_ws ()
+	   || match_list[0][strlen (match_list[0]) - 1] == ' ');
 
       return completion_result (match_list, 1, completion_suppress_append);
     }
diff --git a/gdb/completer.h b/gdb/completer.h
index eab9c69..fbfe4d5 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -192,6 +192,16 @@ public:
   /* Advance the custom word point by LEN.  */
   void advance_custom_word_point_by (size_t len);
 
+  /* Whether to tell readline to skip appending a whitespace after the
+     completion.  See m_suppress_append_ws.  */
+  bool suppress_append_ws () const
+  { return m_suppress_append_ws; }
+
+  /* Set whether to tell readline to skip appending a whitespace after
+     the completion.  See m_suppress_append_ws.  */
+  void set_suppress_append_ws (bool suppress)
+  { m_suppress_append_ws = suppress; }
+
   /* Return true if we only have one completion, and it matches
      exactly the completion word.  I.e., completing results in what we
      already have.  */
@@ -255,6 +265,14 @@ private:
      completable words.  */
   int m_custom_word_point = 0;
 
+  /* If true, tell readline to skip appending a whitespace after the
+     completion.  Automatically set if we have a unique completion
+     that already has a space at the end.  Completer may also
+     explicitly set this.  E.g., the linespec completer sets when when
+     the completion ends with the ":" separator between filename and
+     function name.  */
+  bool m_suppress_append_ws = false;
+
   /* Our idea of lowest common denominator to hand over to readline.
      See intro.  */
   char *m_lowest_common_denominator = NULL;
@@ -347,6 +365,11 @@ extern completer_handle_brkchars_ftype *
 
 /* Exported to linespec.c */
 
+extern completion_list complete_source_filenames (const char *text);
+
+extern void complete_expression (completion_tracker &tracker,
+				 const char *text, const char *word);
+
 extern const char *skip_quoted_chars (const char *, const char *,
 				      const char *);
 
diff --git a/gdb/linespec.c b/gdb/linespec.c
index f24cca2..c993c67 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -46,6 +46,28 @@
 #include "location.h"
 #include "common/function-view.h"
 
+/* An enumeration of the various things a user might attempt to
+   complete for a linespec location.  */
+
+enum class linespec_complete_what
+{
+  /* Nothing, no possible completion.  */
+  NOTHING,
+
+  /* A function/method name.  */
+  FUNCTION,
+
+  /* A label symbol.  E.g., break file.c:function:LABEL.  */
+  LABEL,
+
+  /* An expression.  E.g., "break foo if EXPR", or "break *EXPR".  */
+  EXPRESSION,
+
+  /* A linespec keyword ("if"/"thread"/"task").
+     E.g., "break func threa<tab>".  */
+  KEYWORD,
+};
+
 typedef struct symbol *symbolp;
 DEF_VEC_P (symbolp);
 
@@ -271,6 +293,29 @@ struct ls_parser
   /* The result of the parse.  */
   struct linespec result;
 #define PARSER_RESULT(PPTR) (&(PPTR)->result)
+
+  /* What the parser believes the current word point should complete
+     to.  */
+  linespec_complete_what complete_what;
+
+  /* The completion word point.  The parser advances this as is skips
+     tokens.  At some point the input string will end or parsing will
+     fail, and then we attempt completion at the captured completion
+     word point, interpreting the string at completion_word as
+     COMPLETE_WHAT.  */
+  const char *completion_word;
+
+  /* If the current token was a quoted string, then this is the
+     quoting character (either " or '.).  */
+  int completion_quote_char;
+
+  /* If the current token was a quoted string, then this points at the
+     end of the quoted string.  */
+  const char *completion_quote_end;
+
+  /* If parsing for completion, then this points at the completion
+     tracker.  Otherwise, this is NULL.  */
+  struct completion_tracker *completion_tracker;
 };
 typedef struct ls_parser linespec_parser;
 
@@ -543,6 +588,30 @@ find_parameter_list_end (const char *input)
   return p;
 }
 
+/* If the [STRING, STRING_LEN) string ends with what looks like a
+   keyword, return the keyword start offset in STRING.  Return -1
+   otherwise.  */
+
+size_t
+string_find_incomplete_keyword_at_end (const char * const *keywords,
+				       const char *string, size_t string_len)
+{
+  const char *end = string + string_len;
+  const char *p = end;
+
+  while (p > string && *p != ' ')
+    --p;
+  if (p > string)
+    {
+      p++;
+      size_t len = end - p;
+      for (size_t i = 0; keywords[i] != NULL; ++i)
+	if (strncmp (keywords[i], p, len) == 0)
+	  return p - string;
+    }
+
+  return -1;
+}
 
 /* Lex a string from the input in PARSER.  */
 
@@ -590,13 +659,31 @@ linespec_lexer_lex_string (linespec_parser *parser)
       /* Skip to the ending quote.  */
       end = skip_quote_char (PARSER_STREAM (parser), quote_char);
 
-      /* Error if the input did not terminate properly.  */
-      if (end == NULL)
-	error (_("unmatched quote"));
+      /* This helps the completer mode decide whether we have a
+	 complete string.  */
+      parser->completion_quote_char = quote_char;
+      parser->completion_quote_end = end;
 
-      /* Skip over the ending quote and mark the length of the string.  */
-      PARSER_STREAM (parser) = (char *) ++end;
-      LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start;
+      /* Error if the input did not terminate properly, unless in
+	 completion mode.  */
+      if (end == NULL)
+	{
+	  if (parser->completion_tracker == NULL)
+	    error (_("unmatched quote"));
+
+	  /* In completion mode, we'll try to complete the incomplete
+	     token.  */
+	  token.type = LSTOKEN_STRING;
+	  while (*PARSER_STREAM (parser) != '\0')
+	    PARSER_STREAM (parser)++;
+	  LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 1 - start;
+	}
+      else
+	{
+	  /* Skip over the ending quote and mark the length of the string.  */
+	  PARSER_STREAM (parser) = (char *) ++end;
+	  LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start;
+	}
     }
   else
     {
@@ -834,13 +921,48 @@ linespec_lexer_lex_one (linespec_parser *parser)
 }
 
 /* Consume the current token and return the next token in PARSER's
-   input stream.  */
+   input stream.  Also advance the completion word for completion
+   mode.  */
 
 static linespec_token
 linespec_lexer_consume_token (linespec_parser *parser)
 {
+  gdb_assert (parser->lexer.current.type != LSTOKEN_EOI);
+
+  bool advance_word = (parser->lexer.current.type != LSTOKEN_STRING
+		       || *PARSER_STREAM (parser) != '\0');
+
+  /* If we're moving past a string to some other token, it must be the
+     quote was terminated.  */
+  if (parser->completion_quote_char)
+    {
+      gdb_assert (parser->lexer.current.type == LSTOKEN_STRING);
+
+      /* If the string was the last (non-EOI) token, we're past the
+	 quote, but remember that for later.  */
+      if (*PARSER_STREAM (parser) != '\0')
+	{
+	  parser->completion_quote_char = '\0';
+	  parser->completion_quote_end = NULL;;
+	}
+    }
+
   parser->lexer.current.type = LSTOKEN_CONSUMED;
-  return linespec_lexer_lex_one (parser);
+  linespec_lexer_lex_one (parser);
+
+  if (parser->lexer.current.type == LSTOKEN_STRING)
+    {
+      /* Advance the completion word past a potential initial
+	 quote-char.  */
+      parser->completion_word = LS_TOKEN_STOKEN (parser->lexer.current).ptr;
+    }
+  else if (advance_word)
+    {
+      /* Advance the completion word past any whitespace.  */
+      parser->completion_word = PARSER_STREAM (parser);
+    }
+
+  return parser->lexer.current;
 }
 
 /* Return the next token without consuming the current token.  */
@@ -851,10 +973,16 @@ linespec_lexer_peek_token (linespec_parser *parser)
   linespec_token next;
   const char *saved_stream = PARSER_STREAM (parser);
   linespec_token saved_token = parser->lexer.current;
+  int saved_completion_quote_char = parser->completion_quote_char;
+  const char *saved_completion_quote_end = parser->completion_quote_end;
+  const char *saved_completion_word = parser->completion_word;
 
   next = linespec_lexer_consume_token (parser);
   PARSER_STREAM (parser) = saved_stream;
   parser->lexer.current = saved_token;
+  parser->completion_quote_char = saved_completion_quote_char;
+  parser->completion_quote_end = saved_completion_quote_end;
+  parser->completion_word = saved_completion_word;
   return next;
 }
 
@@ -1598,6 +1726,17 @@ source_file_not_found_error (const char *name)
   throw_error (NOT_FOUND_ERROR, _("No source file named %s."), name);
 }
 
+/* Unless at EIO, save the current stream position as completion word
+   point, and consume the next token.  */
+
+static linespec_token
+save_stream_and_consume_token (linespec_parser *parser)
+{
+  if (linespec_lexer_peek_token (parser).type != LSTOKEN_EOI)
+    parser->completion_word = PARSER_STREAM (parser);
+  return linespec_lexer_consume_token (parser);
+}
+
 /* See description in linespec.h.  */
 
 struct line_offset
@@ -1625,6 +1764,26 @@ linespec_parse_line_offset (const char *string)
   return line_offset;
 }
 
+/* In completion mode, if the user is still typing the number, there's
+   no possible completion to offer.  But if there's already input past
+   the number, setup to expect NEXT.  */
+
+static void
+set_completion_after_number (linespec_parser *parser,
+			     linespec_complete_what next)
+{
+  if (*PARSER_STREAM (parser) == ' ')
+    {
+      parser->completion_word = skip_spaces_const (PARSER_STREAM (parser) + 1);
+      parser->complete_what = next;
+    }
+  else
+    {
+      parser->completion_word = PARSER_STREAM (parser);
+      parser->complete_what = linespec_complete_what::NOTHING;
+    }
+}
+
 /* Parse the basic_spec in PARSER's input.  */
 
 static void
@@ -1640,11 +1799,20 @@ linespec_parse_basic (linespec_parser *parser)
   token = linespec_lexer_lex_one (parser);
 
   /* If it is EOI or KEYWORD, issue an error.  */
-  if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI)
-    unexpected_linespec_error (parser);
+  if (token.type == LSTOKEN_KEYWORD)
+    {
+      parser->complete_what = linespec_complete_what::NOTHING;
+      unexpected_linespec_error (parser);
+    }
+  else if (token.type == LSTOKEN_EOI)
+    {
+      unexpected_linespec_error (parser);
+    }
   /* If it is a LSTOKEN_NUMBER, we have an offset.  */
   else if (token.type == LSTOKEN_NUMBER)
     {
+      set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
       /* Record the line offset and get the next token.  */
       name = copy_token_string (token);
       cleanup = make_cleanup (xfree, name);
@@ -1656,7 +1824,10 @@ linespec_parse_basic (linespec_parser *parser)
 
       /* If the next token is a comma, stop parsing and return.  */
       if (token.type == LSTOKEN_COMMA)
-	return;
+	{
+	  parser->complete_what = linespec_complete_what::NOTHING;
+	  return;
+	}
 
       /* If the next token is anything but EOI or KEYWORD, issue
 	 an error.  */
@@ -1669,12 +1840,58 @@ linespec_parse_basic (linespec_parser *parser)
 
   /* Next token must be LSTOKEN_STRING.  */
   if (token.type != LSTOKEN_STRING)
-    unexpected_linespec_error (parser);
+    {
+      parser->complete_what = linespec_complete_what::NOTHING;
+      unexpected_linespec_error (parser);
+    }
 
   /* The current token will contain the name of a function, method,
      or label.  */
-  name  = copy_token_string (token);
-  cleanup = make_cleanup (xfree, name);
+  name = copy_token_string (token);
+  cleanup = make_cleanup (free_current_contents, &name);
+
+  if (parser->completion_tracker != NULL)
+    {
+      /* If the function name ends with a ":", then this may be an
+	 incomplete "::" scope operator instead of a label separator.
+	 E.g.,
+	   "b klass:<tab>"
+	 which should expand to:
+	   "b klass::method()"
+
+	 Do a tentative completion assuming the later.  If we find
+	 completions, advance the stream past the colon token and make
+	 it part of the function name/token.  */
+
+      if (!parser->completion_quote_char
+	  && strcmp (PARSER_STREAM (parser), ":") == 0)
+	{
+	  completion_tracker tmp_tracker;
+	  const char *source_filename
+	    = PARSER_EXPLICIT (parser)->source_filename;
+
+	  linespec_complete_function (tmp_tracker,
+				      parser->completion_word,
+				      source_filename);
+
+	  if (tmp_tracker.have_completions ())
+	    {
+	      PARSER_STREAM (parser)++;
+	      LS_TOKEN_STOKEN (token).length++;
+
+	      xfree (name);
+	      name = savestring (parser->completion_word,
+				 (PARSER_STREAM (parser)
+				  - parser->completion_word));
+	    }
+	}
+
+      PARSER_EXPLICIT (parser)->function_name = name;
+      discard_cleanups (cleanup);
+    }
+  else
+    {
+      /* XXX Reindent before pushing.  */
 
   /* Try looking it up as a function/method.  */
   find_linespec_symbols (PARSER_STATE (parser),
@@ -1734,11 +1951,19 @@ linespec_parse_basic (linespec_parser *parser)
 	  return;
 	}
     }
+    }
+
+  int previous_qc = parser->completion_quote_char;
 
   /* Get the next token.  */
   token = linespec_lexer_consume_token (parser);
 
-  if (token.type == LSTOKEN_COLON)
+  if (token.type == LSTOKEN_EOI)
+    {
+      if (previous_qc && !parser->completion_quote_char)
+	parser->complete_what = linespec_complete_what::KEYWORD;
+    }
+  else if (token.type == LSTOKEN_COLON)
     {
       /* User specified a label or a lineno.  */
       token = linespec_lexer_consume_token (parser);
@@ -1747,17 +1972,56 @@ linespec_parse_basic (linespec_parser *parser)
 	{
 	  /* User specified an offset.  Record the line offset and
 	     get the next token.  */
+	  set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
 	  name = copy_token_string (token);
 	  cleanup = make_cleanup (xfree, name);
 	  PARSER_EXPLICIT (parser)->line_offset
 	    = linespec_parse_line_offset (name);
 	  do_cleanups (cleanup);
 
-	  /* Ge the next token.  */
+	  /* Get the next token.  */
 	  token = linespec_lexer_consume_token (parser);
 	}
+      else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+	{
+	  parser->complete_what = linespec_complete_what::LABEL;
+	}
       else if (token.type == LSTOKEN_STRING)
 	{
+	  parser->complete_what = linespec_complete_what::LABEL;
+
+	  /* If we have text after the label separated by whitespace
+	     (e.g., "b func():lab i<tab>"), don't consider it part of
+	     the label.  In completion mode that should complete to
+	     "if", in normal mode, the 'i' should be treated as
+	     garbage.  */
+	  if (parser->completion_quote_char == '\0')
+	    {
+	      const char *ptr = LS_TOKEN_STOKEN (token).ptr;
+	      for (size_t i = 0; i < LS_TOKEN_STOKEN (token).length; i++)
+		{
+		  if (ptr[i] == ' ')
+		    {
+		      LS_TOKEN_STOKEN (token).length = i;
+		      PARSER_STREAM (parser) = skip_spaces_const (ptr + i + 1);
+		      break;
+		    }
+		}
+	    }
+
+	  if (parser->completion_tracker != NULL)
+	    {
+	      if (PARSER_STREAM (parser)[-1] == ' ')
+		{
+		  parser->completion_word = PARSER_STREAM (parser);
+		  parser->complete_what = linespec_complete_what::KEYWORD;
+		}
+	    }
+	  else
+	    {
+	      /* XXX Reindent before pushing.  */
+
 	  /* Grab a copy of the label's name and look it up.  */
 	  name = copy_token_string (token);
 	  cleanup = make_cleanup (xfree, name);
@@ -1780,8 +2044,10 @@ linespec_parse_basic (linespec_parser *parser)
 				     name);
 	    }
 
+	    }
+
 	  /* Check for a line offset.  */
-	  token = linespec_lexer_consume_token (parser);
+	  token = save_stream_and_consume_token (parser);
 	  if (token.type == LSTOKEN_COLON)
 	    {
 	      /* Get the next token.  */
@@ -2271,11 +2537,15 @@ parse_linespec (linespec_parser *parser, const char *arg)
   struct gdb_exception file_exception = exception_none;
   struct cleanup *cleanup;
 
+  values.nelts = 0;
+  values.sals = NULL;
+
   /* A special case to start.  It has become quite popular for
      IDEs to work around bugs in the previous parser by quoting
      the entire linespec, so we attempt to deal with this nicely.  */
   parser->is_quote_enclosed = 0;
-  if (!is_ada_operator (arg)
+  if (parser->completion_tracker == NULL
+      && !is_ada_operator (arg)
       && strchr (linespec_quote_characters, *arg) != NULL)
     {
       const char *end;
@@ -2292,20 +2562,34 @@ parse_linespec (linespec_parser *parser, const char *arg)
 
   parser->lexer.saved_arg = arg;
   parser->lexer.stream = arg;
+  parser->completion_word = arg;
+  parser->complete_what = linespec_complete_what::FUNCTION;
 
   /* Initialize the default symtab and line offset.  */
   initialize_defaults (&PARSER_STATE (parser)->default_symtab,
 		       &PARSER_STATE (parser)->default_line);
 
   /* Objective-C shortcut.  */
-  values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
-  if (values.sals != NULL)
-    return values;
+  if (parser->completion_tracker == NULL)
+    {
+      values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
+      if (values.sals != NULL)
+	return values;
+    }
+  else
+    {
+      /* "-"/"+" is either an objc selector, or a number.  There's
+	 nothing to complete the latter to, so just let the caller
+	 complete on functions, which finds objc selectors, if there's
+	 any.  */
+      if ((arg[0] == '-' || arg[0] == '+') && arg[1] == '\0')
+	return {};
+    }
 
   /* Start parsing.  */
 
   /* Get the first token.  */
-  token = linespec_lexer_lex_one (parser);
+  token = linespec_lexer_consume_token (parser);
 
   /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER.  */
   if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$')
@@ -2313,7 +2597,8 @@ parse_linespec (linespec_parser *parser, const char *arg)
       char *var;
 
       /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
-      VEC_safe_push (symtab_ptr, PARSER_RESULT (parser)->file_symtabs, NULL);
+      if (parser->completion_tracker == NULL)
+	VEC_safe_push (symtab_ptr, PARSER_RESULT (parser)->file_symtabs, NULL);
 
       /* User specified a convenience variable or history value.  */
       var = copy_token_string (token);
@@ -2332,8 +2617,16 @@ parse_linespec (linespec_parser *parser, const char *arg)
 	  goto convert_to_sals;
 	}
     }
+  else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+    {
+      /* Let the default linespec_complete_what::FUNCTION kick in.  */
+      unexpected_linespec_error (parser);
+    }
   else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER)
-    unexpected_linespec_error (parser);
+    {
+      parser->complete_what = linespec_complete_what::NOTHING;
+      unexpected_linespec_error (parser);
+    }
 
   /* Shortcut: If the next token is not LSTOKEN_COLON, we know that
      this token cannot represent a filename.  */
@@ -2381,8 +2674,9 @@ parse_linespec (linespec_parser *parser, const char *arg)
 	}
     }
   /* If the next token is not EOI, KEYWORD, or COMMA, issue an error.  */
-  else if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD
-	   && token.type != LSTOKEN_COMMA)
+  else if (parser->completion_tracker == NULL
+	   && (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD
+	       && token.type != LSTOKEN_COMMA))
     {
       /* TOKEN is the _next_ token, not the one currently in the parser.
 	 Consuming the token will give the correct error message.  */
@@ -2398,7 +2692,8 @@ parse_linespec (linespec_parser *parser, const char *arg)
   /* Parse the rest of the linespec.  */
   linespec_parse_basic (parser);
 
-  if (PARSER_RESULT (parser)->function_symbols == NULL
+  if (parser->completion_tracker == NULL
+      && PARSER_RESULT (parser)->function_symbols == NULL
       && PARSER_RESULT (parser)->labels.label_symbols == NULL
       && PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN
       && PARSER_RESULT (parser)->minimal_symbols == NULL)
@@ -2419,11 +2714,21 @@ parse_linespec (linespec_parser *parser, const char *arg)
      if necessary.  */
   token = linespec_lexer_lex_one (parser);
   if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD)
-    PARSER_STREAM (parser) = LS_TOKEN_STOKEN (token).ptr;
+    unexpected_linespec_error (parser);
+  else if (token.type == LSTOKEN_KEYWORD)
+    {
+      /* Setup the completion word past the keyword.  Lexing never
+	 advances past a keyword automatically, so skip it
+	 manually.  */
+      parser->completion_word
+	= skip_spaces_const (skip_to_space_const (PARSER_STREAM (parser)));
+      parser->complete_what = linespec_complete_what::EXPRESSION;
+    }
 
   /* Convert the data in PARSER_RESULT to SALs.  */
-  values = convert_linespec_to_sals (PARSER_STATE (parser),
-				     PARSER_RESULT (parser));
+  if (parser->completion_tracker == NULL)
+    values = convert_linespec_to_sals (PARSER_STATE (parser),
+				       PARSER_RESULT (parser));
 
   return values;
 }
@@ -2561,6 +2866,62 @@ linespec_complete_function (completion_tracker &tracker,
     collect_symbol_completion_matches (tracker, mode, function, function);
 }
 
+/* Helper for complete_linespec to simplify it.  SOURCE_FILENAME is
+   only meaningful if COMPONENT is FUNCTION.  */
+
+static void
+complete_linespec_component (linespec_parser *parser,
+			     completion_tracker &tracker,
+			     const char *text,
+			     linespec_complete_what component,
+			     const char *source_filename)
+{
+  if (component == linespec_complete_what::KEYWORD)
+    {
+      complete_on_enum (tracker, linespec_keywords, text, text);
+    }
+  else if (component == linespec_complete_what::EXPRESSION)
+    {
+      const char *word
+	= advance_to_expression_complete_word_point (tracker, text);
+      complete_expression (tracker, text, word);
+    }
+  else if (component == linespec_complete_what::FUNCTION)
+    {
+      completion_list fn_list;
+
+      linespec_complete_function (tracker, text, source_filename);
+      if (source_filename == NULL)
+	fn_list = complete_source_filenames (text);
+
+      /* If we only have a single filename completion, append a ':' for
+	 the user, since that's the only thing that can usefully follow
+	 the filename.  */
+      if (fn_list.size () == 1 && !tracker.have_completions ())
+	{
+	  char *fn = fn_list[0].release ();
+
+	  /* If we also need to append a quote char, it needs to be
+	     appended before the ':'.  Append it now, and make ':' the
+	     new "quote" char.  */
+	  if (tracker.quote_char ())
+	    {
+	      char quote_char_str[2] = { tracker.quote_char () };
+
+	      fn = reconcat (fn, fn, quote_char_str, (char *) NULL);
+	      tracker.set_quote_char (':');
+	    }
+	  else
+	    fn = reconcat (fn, fn, ":", (char *) NULL);
+	  fn_list[0].reset (fn);
+
+	  /* Tell readline to skip appending a space.  */
+	  tracker.set_suppress_append_ws (true);
+	}
+      tracker.add_completions (std::move (fn_list));
+    }
+}
+
 /* Helper for linespec_complete_label.  Find labels that match
    LABEL_NAME in the function symbols listed in the PARSER, and add
    them to the tracker.  */
@@ -2624,6 +2985,180 @@ linespec_complete_label (completion_tracker &tracker,
   do_cleanups (cleanup);
 }
 
+/* See description in linespec.h.  */
+
+void
+linespec_complete (completion_tracker &tracker, const char *text)
+{
+  linespec_parser parser;
+  struct cleanup *cleanup;
+  const char *orig = text;
+
+  linespec_parser_new (&parser, 0, current_language, NULL, NULL, 0, NULL);
+  cleanup = make_cleanup (linespec_parser_delete, &parser);
+  parser.lexer.saved_arg = text;
+  PARSER_STREAM (&parser) = text;
+
+  parser.completion_tracker = &tracker;
+  PARSER_STATE (&parser)->is_linespec = 1;
+
+  /* Parse as much as possible.  parser.completion_word will hold
+     furthest completion point we managed to parse to.  */
+  TRY
+    {
+      parse_linespec (&parser, text);
+    }
+  CATCH (except, RETURN_MASK_ERROR)
+    {
+    }
+  END_CATCH
+
+  if (parser.completion_quote_char != '\0'
+      && parser.completion_quote_end != NULL
+      && parser.completion_quote_end[1] == '\0')
+    {
+      /* If completing a quoted string with the cursor right at
+	 terminating quote char, complete the completion word without
+	 interpretation, so that readline advances the cursor one
+	 whitespace past the quote, even if there's no match.  This
+	 makes these cases behave the same:
+
+	   before: "b function()"
+	   after:  "b function() "
+
+	   before: "b 'function()'"
+	   after:  "b 'function()' "
+
+	 and trusts the user in this case:
+
+	   before: "b 'not_loaded_function_yet()'"
+	   after:  "b 'not_loaded_function_yet()' "
+      */
+      parser.complete_what = linespec_complete_what::NOTHING;
+      parser.completion_quote_char = '\0';
+
+      gdb::unique_xmalloc_ptr<char> text_copy
+	(xstrdup (parser.completion_word));
+      tracker.add_completion (std::move (text_copy));
+    }
+
+  tracker.set_quote_char (parser.completion_quote_char);
+
+  if (parser.complete_what == linespec_complete_what::LABEL)
+    {
+      parser.complete_what = linespec_complete_what::NOTHING;
+
+      const char *func_name = PARSER_EXPLICIT (&parser)->function_name;
+
+      VEC (symbolp) *function_symbols;
+      VEC (bound_minimal_symbol_d) *minimal_symbols;
+      find_linespec_symbols (PARSER_STATE (&parser),
+			     PARSER_RESULT (&parser)->file_symtabs,
+			     func_name,
+			     &function_symbols, &minimal_symbols);
+
+      PARSER_RESULT (&parser)->function_symbols = function_symbols;
+      PARSER_RESULT (&parser)->minimal_symbols = minimal_symbols;
+
+      complete_label (tracker, &parser, parser.completion_word);
+    }
+  else if (parser.complete_what == linespec_complete_what::FUNCTION)
+    {
+      /* While parsing/lexing, we didn't know whether the completion
+	 word completes to a unique function name already or not.  E.g.:
+	   "b function() <tab>"
+	 may need to complete either to:
+	   "b function() const"
+	 or to:
+	   "b function() if/thread/task"
+
+	 Or, this:
+	   "b foo t"
+	 may need to complete either to:
+	   "b foo template_fun<T>()"
+	 with "foo" being the template function's return type, or to:
+	   "b foo thread/task"
+
+	 Address that by completing assuming function, and seeing if
+	 we find a function completion that matches exactly
+	 "function()". If so, then we advance the completion word past
+	 the function and switch to KEYWORD completion mode.  */
+
+      const char *text = parser.completion_word;
+      const char *word = parser.completion_word;
+
+      complete_linespec_component (&parser, tracker,
+				   parser.completion_word,
+				   linespec_complete_what::FUNCTION,
+				   PARSER_EXPLICIT (&parser)->source_filename);
+
+      parser.complete_what = linespec_complete_what::NOTHING;
+
+      if (tracker.quote_char ())
+	{
+	  /* The function/file name was not close-quoted, so this
+	     can't be a keyword.  */
+	}
+      else if (!tracker.have_completions ())
+	{
+	  size_t key_start;
+	  size_t wordlen = strlen (parser.completion_word);
+
+	  key_start
+	    = string_find_incomplete_keyword_at_end (linespec_keywords,
+						     parser.completion_word,
+						     wordlen);
+
+	  if (key_start != -1
+	      || (wordlen > 0
+		  && parser.completion_word[wordlen - 1] == ' '))
+	    {
+	      parser.completion_word += key_start;
+	      parser.complete_what = linespec_complete_what::KEYWORD;
+	    }
+	}
+      else if (tracker.completes_to_completion_word (word))
+	{
+	  /* Skip the function and complete on keywords.  */
+	  parser.completion_word += strlen (word);
+	  parser.complete_what = linespec_complete_what::KEYWORD;
+	  tracker.discard_completions ();
+	}
+    }
+
+  tracker.advance_custom_word_point_by (parser.completion_word - orig);
+
+  complete_linespec_component (&parser, tracker,
+			       parser.completion_word,
+			       parser.complete_what,
+			       PARSER_EXPLICIT (&parser)->source_filename);
+
+  /* If we're past the "filename:function:label:offset" linespec, and
+     didn't find any match, then assume the user might want to create
+     a pending breakpoint anyway and offer the keyword
+     completions.  */
+  if (!parser.completion_quote_char
+      && (parser.complete_what == linespec_complete_what::FUNCTION
+	  || parser.complete_what == linespec_complete_what::LABEL
+	  || parser.complete_what == linespec_complete_what::NOTHING)
+      && !tracker.have_completions ())
+    {
+      const char *end
+	= parser.completion_word + strlen (parser.completion_word);
+
+      if (end > orig && end[-1] == ' ')
+	{
+	  tracker.advance_custom_word_point_by (end - parser.completion_word);
+
+	  complete_linespec_component (&parser, tracker, end,
+				       linespec_complete_what::KEYWORD,
+				       NULL);
+	}
+    }
+
+  do_cleanups (cleanup);
+}
+
 /* A helper function for decode_line_full and decode_line_1 to
    turn LOCATION into symtabs_and_lines.  */
 
diff --git a/gdb/linespec.h b/gdb/linespec.h
index f16bb81..27d237a 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -186,6 +186,11 @@ extern void linespec_lex_to_end (char **stringp);
 
 extern const char * const linespec_keywords[];
 
+/* Complete a linespec.  */
+
+extern void linespec_complete (completion_tracker &tracker,
+			       const char *text);
+
 /* Complete a function symbol, in linespec mode.  If SOURCE_FILENAME
    is non-NULL, limits completion to the list of functions defined in
    source files that match SOURCE_FILENAME.  */
diff --git a/gdb/testsuite/gdb.linespec/ls-errs.exp b/gdb/testsuite/gdb.linespec/ls-errs.exp
index 7942f1f..f55cd0e8 100644
--- a/gdb/testsuite/gdb.linespec/ls-errs.exp
+++ b/gdb/testsuite/gdb.linespec/ls-errs.exp
@@ -96,8 +96,13 @@ proc do_test {lang} {
     }
 
     # Some commonly used whitespace tests around ':'.
-    set spaces [list ":" ": " " :" " : " "\t:  " "  :\t" "\t:\t" \
-		     " \t:\t " "\t  \t:\t  \t  \t"]
+    set spaces [list \
+		    ":" \
+		    ": " \
+		    " :" \
+		    " : " \
+		    "  :  " \
+		   ]
 
     # A list of invalid offsets.
     set invalid_offsets [list -100 +500 1000]
-- 
2.5.5

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

* [PATCH 19/40] Fix cp_find_first_component_aux bug
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (13 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 35/40] Comprehensive C++ linespec/completer tests Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-07-17 19:17   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 18/40] A smarter linespec completer Pedro Alves
                   ` (25 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

Valgrind catches an out-of-bounds read here:

 $ gdb ./testsuite/outputs/gdb.cp/method2/method2
 (gdb) start
 [...]
 Temporary breakpoint 1, main (argc=1, argv=0x7fffffffd958) at src/gdb/testsuite/gdb.cp/method2.cc:26
 26        return 0;
 (gdb) b A::operator
 ==26907== Invalid read of size 1
 ==26907==    at 0x75C0AE: cp_find_first_component_aux(char const*, int) (cp-support.c:951)
 ==26907==    by 0x75C084: cp_find_first_component(char const*) (cp-support.c:925)
 ==26907==    by 0x75C3DA: cp_entire_prefix_len(char const*) (cp-support.c:1089)
 ==26907==    by 0x758B16: cp_lookup_symbol_in_namespace(char const*, char const*, block const*, domain_enum_tag, int) (cp-namespace.c:314)
 ==26907==    by 0x75972A: lookup_namespace_scope(language_defn const*, char const*, block const*, domain_enum_tag, char const*, int) (cp-namespace.c:739)
 ==26907==    by 0x7597CB: cp_lookup_symbol_nonlocal(language_defn const*, char const*, block const*, domain_enum_tag) (cp-namespace.c:768)
 ==26907==    by 0x8C1137: lookup_symbol_aux(char const*, block const*, domain_enum_tag, language, field_of_this_result*) (symtab.c:2016)
 ==26907==    by 0x8C098A: lookup_symbol_in_language(char const*, block const*, domain_enum_tag, language, field_of_this_result*) (symtab.c:1824)
 ==26907==    by 0x8C0A04: lookup_symbol(char const*, block const*, domain_enum_tag, field_of_this_result*) (symtab.c:1836)
 ==26907==    by 0x82CBE1: find_label_symbols(linespec_state*, VEC_symbolp*, VEC_symbolp**, char const*) (linespec.c:3390)
 ==26907==    by 0x828FB5: linespec_parse_basic(ls_parser*) (linespec.c:1620)
 ==26907==    by 0x82A78F: parse_linespec(ls_parser*, char const*) (linespec.c:2307)
 ==26907==  Address 0x910f97c is 0 bytes after a block of size 12 alloc'd
 ==26907==    at 0x4C28BF6: malloc (vg_replace_malloc.c:299)
 ==26907==    by 0x74E737: xmalloc (common-utils.c:43)
 ==26907==    by 0x74EAF4: savestring(char const*, unsigned long) (common-utils.c:179)
 ==26907==    by 0x826CEF: copy_token_string(ls_token) (linespec.c:488)
 ==26907==    by 0x828EF6: linespec_parse_basic(ls_parser*) (linespec.c:1599)
 ==26907==    by 0x82A78F: parse_linespec(ls_parser*, char const*) (linespec.c:2307)
 ==26907==    by 0x82AE27: event_location_to_sals(ls_parser*, event_location const*) (linespec.c:2469)
 ==26907==    by 0x82B1CE: decode_line_full(event_location const*, int, program_space*, symtab*, int, linespec_result*, char const*, char const*) (linespec.c:2557)
 ==26907==    by 0x720C8A: parse_breakpoint_sals(event_location const*, linespec_result*) (breakpoint.c:9550)
 ==26907==    by 0x72A2F7: create_sals_from_location_default(event_location const*, linespec_result*, bptype) (breakpoint.c:14484)
 ==26907==    by 0x727F86: bkpt_create_sals_from_location(event_location const*, linespec_result*, bptype) (breakpoint.c:13219)
 ==26907==    by 0x72146D: create_breakpoint(gdbarch*, event_location const*, char*, int, char*, int, int, bptype, int, auto_boolean, breakpoint_ops const*, int, int, int, unsigned int) (breakpoint.c:9759)

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cp-support.c (cp_find_first_component_aux): Add missing case for
	end of string.
---
 gdb/cp-support.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 122fadd..df9a563 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1009,6 +1009,8 @@ cp_find_first_component_aux (const char *name, int permissive)
 		++index;
 	      switch (name[index])
 		{
+		case '\0':
+		  return index;
 		  /* Skip over one less than the appropriate number of
 		     characters: the for loop will skip over the last
 		     one.  */
-- 
2.5.5

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

* [PATCH 27/40] Make cp_remove_params return a unique_ptr
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (16 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436) Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-08 20:35   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Pedro Alves
                   ` (22 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

I'm going to add a variant of cp_remove_params in a following patch,
and while at it, it seems nice to eliminate a few related cleanups as
preparatory work.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cp-support.c (cp_remove_params): Return a gdb::unique_xmalloc_ptr.
	Use bool.
	(overload_list_add_symbol): Adjust to use gdb::unique_xmalloc_ptr.
	* cp-support.h (cp_remove_params): Now returns a
	gdb::unique_xmalloc_ptr.
	* dwarf2read.c (find_slot_in_mapped_hash): Now returns bool.
	Adjust to use gdb::unique_xmalloc_ptr.
	(dw2_expand_symtabs_matching_symbol): Adjust to use
	gdb::unique_xmalloc_ptr.
	* psymtab.c (psymtab_search_name): Now returns a
	gdb::unique_xmalloc_ptr.
	(lookup_partial_symbol): Adjust to use gdb::unique_xmalloc_ptr.
	* stack.c (find_frame_funname): Adjust to use
	gdb::unique_xmalloc_ptr.
---
 gdb/cp-support.c | 19 +++++++------------
 gdb/cp-support.h |  2 +-
 gdb/dwarf2read.c | 23 ++++++++---------------
 gdb/psymtab.c    | 31 ++++++++++---------------------
 gdb/stack.c      | 18 ++++++++----------
 5 files changed, 34 insertions(+), 59 deletions(-)

diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 95e7cb8..0095c6d 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -837,10 +837,10 @@ cp_func_name (const char *full_name)
    (optionally) a return type.  Return the name of the function without
    parameters or return type, or NULL if we can not parse the name.  */
 
-char *
+gdb::unique_xmalloc_ptr<char>
 cp_remove_params (const char *demangled_name)
 {
-  int done = 0;
+  bool done = false;
   struct demangle_component *ret_comp;
   std::unique_ptr<demangle_parse_info> info;
   char *ret = NULL;
@@ -867,7 +867,7 @@ cp_remove_params (const char *demangled_name)
         ret_comp = d_left (ret_comp);
         break;
       default:
-	done = 1;
+	done = true;
 	break;
       }
 
@@ -875,7 +875,7 @@ cp_remove_params (const char *demangled_name)
   if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME)
     ret = cp_comp_to_string (d_left (ret_comp), 10);
 
-  return ret;
+  return gdb::unique_xmalloc_ptr<char> (ret);
 }
 
 /* Here are some random pieces of trivia to keep in mind while trying
@@ -1102,7 +1102,7 @@ overload_list_add_symbol (struct symbol *sym,
 {
   int newsize;
   int i;
-  char *sym_name;
+  gdb::unique_xmalloc_ptr<char> sym_name;
 
   /* If there is no type information, we can't do anything, so
      skip.  */
@@ -1121,13 +1121,8 @@ overload_list_add_symbol (struct symbol *sym,
     return;
 
   /* skip symbols that cannot match */
-  if (strcmp (sym_name, oload_name) != 0)
-    {
-      xfree (sym_name);
-      return;
-    }
-
-  xfree (sym_name);
+  if (strcmp (sym_name.get (), oload_name) != 0)
+    return;
 
   /* We have a match for an overload instance, so add SYM to the
      current list of overload instances */
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 3a42cd6..dd42415 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -95,7 +95,7 @@ extern unsigned int cp_entire_prefix_len (const char *name);
 
 extern char *cp_func_name (const char *full_name);
 
-extern char *cp_remove_params (const char *demangled_name);
+extern gdb::unique_xmalloc_ptr<char> cp_remove_params (const char *qualified);
 
 extern struct symbol **make_symbol_overload_list (const char *,
 						  const char *);
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index e955131..dfa79aa 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -3168,16 +3168,17 @@ mapped_index_string_hash (int index_version, const void *p)
 
 /* Find a slot in the mapped index INDEX for the object named NAME.
    If NAME is found, set *VEC_OUT to point to the CU vector in the
-   constant pool and return 1.  If NAME cannot be found, return 0.  */
+   constant pool and return true.  If NAME cannot be found, return
+   false.  */
 
-static int
+static bool
 find_slot_in_mapped_hash (struct mapped_index *index, const char *name,
 			  offset_type **vec_out)
 {
-  struct cleanup *back_to = make_cleanup (null_cleanup, 0);
   offset_type hash;
   offset_type slot, step;
   int (*cmp) (const char *, const char *);
+  gdb::unique_xmalloc_ptr<char> without_params;
 
   if (current_language->la_language == language_cplus
       || current_language->la_language == language_fortran
@@ -3188,13 +3189,9 @@ find_slot_in_mapped_hash (struct mapped_index *index, const char *name,
 
       if (strchr (name, '(') != NULL)
 	{
-	  char *without_params = cp_remove_params (name);
-
+	  without_params = cp_remove_params (name);
 	  if (without_params != NULL)
-	    {
-	      make_cleanup (xfree, without_params);
-	      name = without_params;
-	    }
+	    name = without_params.get ();
 	}
     }
 
@@ -3216,18 +3213,14 @@ find_slot_in_mapped_hash (struct mapped_index *index, const char *name,
       offset_type i = 2 * slot;
       const char *str;
       if (index->symbol_table[i] == 0 && index->symbol_table[i + 1] == 0)
-	{
-	  do_cleanups (back_to);
-	  return 0;
-	}
+	return false;
 
       str = index->constant_pool + MAYBE_SWAP (index->symbol_table[i]);
       if (!cmp (name, str))
 	{
 	  *vec_out = (offset_type *) (index->constant_pool
 				      + MAYBE_SWAP (index->symbol_table[i + 1]));
-	  do_cleanups (back_to);
-	  return 1;
+	  return true;
 	}
 
       slot = (slot + step) & (index->symbol_table_slots - 1);
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index b853266..1a1929d 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -642,11 +642,9 @@ match_partial_symbol (struct objfile *objfile,
    not contain any method/function instance information (since this would
    force reading type information while reading psymtabs).  Therefore,
    if NAME contains overload information, it must be stripped before searching
-   psymtabs.
+   psymtabs.  */
 
-   The caller is responsible for freeing the return result.  */
-
-static char *
+static gdb::unique_xmalloc_ptr<char>
 psymtab_search_name (const char *name)
 {
   switch (current_language->la_language)
@@ -655,7 +653,7 @@ psymtab_search_name (const char *name)
       {
 	if (strchr (name, '('))
 	  {
-	    char *ret = cp_remove_params (name);
+	    gdb::unique_xmalloc_ptr<char> ret = cp_remove_params (name);
 
 	    if (ret)
 	      return ret;
@@ -667,7 +665,7 @@ psymtab_search_name (const char *name)
       break;
     }
 
-  return xstrdup (name);
+  return gdb::unique_xmalloc_ptr<char> (xstrdup (name));
 }
 
 /* Look, in partial_symtab PST, for symbol whose natural name is NAME.
@@ -682,16 +680,14 @@ lookup_partial_symbol (struct objfile *objfile,
   struct partial_symbol **top, **real_top, **bottom, **center;
   int length = (global ? pst->n_global_syms : pst->n_static_syms);
   int do_linear_search = 1;
-  char *search_name;
-  struct cleanup *cleanup;
 
   if (length == 0)
     return NULL;
 
-  search_name = psymtab_search_name (name);
-  cleanup = make_cleanup (xfree, search_name);
+  gdb::unique_xmalloc_ptr<char> search_name = psymtab_search_name (name);
 
-  lookup_name_info lookup_name (search_name, symbol_name_match_type::FULL);
+  lookup_name_info lookup_name (search_name.get (),
+				symbol_name_match_type::FULL);
 
   start = (global ?
 	   objfile->global_psymbols.list + pst->globals_offset :
@@ -717,7 +713,7 @@ lookup_partial_symbol (struct objfile *objfile,
 	    internal_error (__FILE__, __LINE__,
 			    _("failed internal consistency check"));
 	  if (strcmp_iw_ordered (SYMBOL_SEARCH_NAME (*center),
-				 search_name) >= 0)
+				 search_name.get ()) >= 0)
 	    {
 	      top = center;
 	    }
@@ -742,10 +738,7 @@ lookup_partial_symbol (struct objfile *objfile,
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
 				     SYMBOL_DOMAIN (*top), domain))
-	    {
-	      do_cleanups (cleanup);
-	      return *top;
-	    }
+	    return *top;
 	  top++;
 	}
     }
@@ -760,14 +753,10 @@ lookup_partial_symbol (struct objfile *objfile,
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*psym),
 				     SYMBOL_DOMAIN (*psym), domain)
 	      && SYMBOL_MATCHES_SEARCH_NAME (*psym, lookup_name))
-	    {
-	      do_cleanups (cleanup);
-	      return *psym;
-	    }
+	    return *psym;
 	}
     }
 
-  do_cleanups (cleanup);
   return NULL;
 }
 
diff --git a/gdb/stack.c b/gdb/stack.c
index 7f8a51c..cff4351 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1106,15 +1106,15 @@ find_frame_funname (struct frame_info *frame, char **funname,
 		 stored in the symbol table, but we stored a version
 		 with DMGL_PARAMS turned on, and here we don't want to
 		 display parameters.  So remove the parameters.  */
-	      char *func_only = cp_remove_params (print_name);
+	      gdb::unique_xmalloc_ptr<char> func_only
+		= cp_remove_params (print_name);
 
 	      if (func_only)
-		*funname = func_only;
+		*funname = func_only.release ();
 	    }
 
-	  /* If we didn't hit the C++ case above, set *funname here.
-	     This approach is taken to avoid having to install a
-	     cleanup in case cp_remove_params can throw.  */
+	  /* If we didn't hit the C++ case above, set *funname
+	     here.  */
 	  if (*funname == NULL)
 	    *funname = xstrdup (print_name);
 	}
@@ -1415,6 +1415,7 @@ frame_info (char *addr_exp, int from_tty)
   /* Initialize it to avoid "may be used uninitialized" warning.  */
   CORE_ADDR caller_pc = 0;
   int caller_pc_p = 0;
+  gdb::unique_xmalloc_ptr<char> func_only;
 
   fi = parse_frame_specification (addr_exp, &selected_frame_p);
   gdbarch = get_frame_arch (fi);
@@ -1448,13 +1449,10 @@ frame_info (char *addr_exp, int from_tty)
 	     stored in the symbol table, but we stored a version
 	     with DMGL_PARAMS turned on, and here we don't want to
 	     display parameters.  So remove the parameters.  */
-	  char *func_only = cp_remove_params (funname);
+	  func_only = cp_remove_params (funname);
 
 	  if (func_only)
-	    {
-	      funname = func_only;
-	      make_cleanup (xfree, func_only);
-	    }
+	    funname = func_only.get ();
 	}
     }
   else if (frame_pc_p)
-- 
2.5.5

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

* [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (8 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 37/40] Fix completing an empty string Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-06-28 21:40   ` Yao Qi
  2017-07-13 20:46   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 34/40] Make strcmp_iw NOT ignore whitespace in the middle of tokens Pedro Alves
                   ` (30 subsequent siblings)
  40 siblings, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

"make_symbol_completion_list_fn" is odly named when you look at a list
of "standard" completers, like the Python/Guild completer lists
adjusted by this patch.  Rename / move it to completers.h/c, for
consistency.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.c (symbol_completer): New function, based on
	make_symbol_completion_list_fn.
	* completer.h (symbol_completer): New declaration.
	* guile/scm-cmd.c (cmdscm_completers): Adjust.
	* python/py-cmd.c (completers): Adjust.
	* symtab.c (make_symbol_completion_list_fn): Delete.
	* symtab.h  (make_symbol_completion_list_fn): Delete.
	* cli/cli-decode.c (add_cmd): Adjust.
---
 gdb/cli/cli-decode.c |  2 +-
 gdb/completer.c      |  9 +++++++++
 gdb/completer.h      |  3 +++
 gdb/guile/scm-cmd.c  |  2 +-
 gdb/python/py-cmd.c  |  2 +-
 gdb/symtab.c         | 10 ----------
 gdb/symtab.h         |  3 ---
 7 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index d386d02..f982028 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -244,7 +244,7 @@ add_cmd (const char *name, enum command_class theclass, cmd_cfunc_ftype *fun,
   c->allow_unknown = 0;
   c->prefix = NULL;
   c->abbrev_flag = 0;
-  set_cmd_completer (c, make_symbol_completion_list_fn);
+  set_cmd_completer (c, symbol_completer);
   c->completer_handle_brkchars = NULL;
   c->destroyer = NULL;
   c->type = not_set_cmd;
diff --git a/gdb/completer.c b/gdb/completer.c
index ee587fb..4ab2388 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -671,6 +671,15 @@ set_gdb_completion_word_break_characters (completer_ftype *fn)
   set_rl_completer_word_break_characters (break_chars);
 }
 
+/* Complete on symbols.  */
+
+VEC (char_ptr) *
+symbol_completer (struct cmd_list_element *ignore,
+		  const char *text, const char *word)
+{
+  return make_symbol_completion_list (text, word);
+}
+
 /* Here are some useful test cases for completion.  FIXME: These
    should be put in the test suite.  They should be tested with both
    M-? and TAB.
diff --git a/gdb/completer.h b/gdb/completer.h
index 2aa1987..0a40eaa 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -87,6 +87,9 @@ extern VEC (char_ptr) *expression_completer (struct cmd_list_element *,
 extern VEC (char_ptr) *location_completer (struct cmd_list_element *,
 					   const char *, const char *);
 
+extern VEC (char_ptr) *symbol_completer (struct cmd_list_element *,
+					 const char *, const char *);
+
 extern VEC (char_ptr) *command_completer (struct cmd_list_element *,
 					  const char *, const char *);
 
diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c
index ae5d145..0c6419d 100644
--- a/gdb/guile/scm-cmd.c
+++ b/gdb/guile/scm-cmd.c
@@ -114,7 +114,7 @@ static const struct cmdscm_completer cmdscm_completers[] =
   { "COMPLETE_FILENAME", filename_completer },
   { "COMPLETE_LOCATION", location_completer },
   { "COMPLETE_COMMAND", command_completer },
-  { "COMPLETE_SYMBOL", make_symbol_completion_list_fn },
+  { "COMPLETE_SYMBOL", symbol_completer },
   { "COMPLETE_EXPRESSION", expression_completer },
 };
 
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 6203211..72ea577 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -44,7 +44,7 @@ static const struct cmdpy_completer completers[] =
   { "COMPLETE_FILENAME", filename_completer },
   { "COMPLETE_LOCATION", location_completer },
   { "COMPLETE_COMMAND", command_completer },
-  { "COMPLETE_SYMBOL", make_symbol_completion_list_fn },
+  { "COMPLETE_SYMBOL", symbol_completer },
   { "COMPLETE_EXPRESSION", expression_completer },
 };
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 183164d..09c9411b 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -5310,16 +5310,6 @@ make_symbol_completion_type (const char *text, const char *word,
   return current_language->la_make_symbol_completion_list (text, word, code);
 }
 
-/* Like make_symbol_completion_list, but suitable for use as a
-   completion function.  */
-
-VEC (char_ptr) *
-make_symbol_completion_list_fn (struct cmd_list_element *ignore,
-				const char *text, const char *word)
-{
-  return make_symbol_completion_list (text, word);
-}
-
 /* Like make_symbol_completion_list, but returns a list of symbols
    defined in a source file FILE.  */
 
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 341deca..5901cf9 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1507,9 +1507,6 @@ extern VEC (char_ptr) *default_make_symbol_completion_list (const char *,
 extern VEC (char_ptr) *make_symbol_completion_list (const char *, const char *);
 extern VEC (char_ptr) *make_symbol_completion_type (const char *, const char *,
 						    enum type_code);
-extern VEC (char_ptr) *make_symbol_completion_list_fn (struct cmd_list_element *,
-						       const char *,
-						       const char *);
 
 extern VEC (char_ptr) *make_file_symbol_completion_list (const char *,
 							 const char *,
-- 
2.5.5

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

* [PATCH 13/40] Introduce strncmp_iw
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (11 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 10/40] Clean up "completer_handle_brkchars" callback handling Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-06-29  8:42   ` Yao Qi
  2017-06-02 12:23 ` [PATCH 35/40] Comprehensive C++ linespec/completer tests Pedro Alves
                   ` (27 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

The explicit locations completer patch will need a strncmp_iw
function, that to strcmp_iw like strncmp is to strcmp.  This patch
implements it.

(Unit tests added a bit further down in this series will exercise
this.)

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* utils.c (enum class strncmp_iw_mode): New.
	(strcmp_iw): Rename to ...
	(strncmp_iw_with_mode): ... this.  Add string2_len and mode
	parameters.  Handle them.
	(strncmp_iw): New.
	(strcmp_iw): Reimplement as wrapper around strncmp_iw_with_mode.
	* utils.h (strncmp_iw): Declare.
	(strcmp_iw): Move describing comments here.
---
 gdb/utils.c | 77 +++++++++++++++++++++++++++++++++++++++++++------------------
 gdb/utils.h | 16 +++++++++++++
 2 files changed, 70 insertions(+), 23 deletions(-)

diff --git a/gdb/utils.c b/gdb/utils.c
index 00b1bbb..9f1c83a 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2418,41 +2418,72 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
     }
 }
 
-/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
-   differences in whitespace.  Returns 0 if they match, non-zero if they
-   don't (slightly different than strcmp()'s range of return values).
+/* Modes of operation for strncmp_iw_with_mode.  */
 
-   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
-   This "feature" is useful when searching for matching C++ function names
-   (such as if the user types 'break FOO', where FOO is a mangled C++
-   function).  */
+enum class strncmp_iw_mode
+{
+  /* Work like strncmp, while ignoring whitespace.  */
+  NORMAL,
 
-int
-strcmp_iw (const char *string1, const char *string2)
+  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
+     string1=="FOO(PARAMS)" matches string2=="FOO".  */
+  MATCH_PARAMS,
+};
+
+/* Helper for strncmp_iw and strcmp_iw.  */
+
+static int
+strncmp_iw_with_mode (const char *string1, const char *string2,
+		      size_t string2_len, strncmp_iw_mode mode)
 {
-  while ((*string1 != '\0') && (*string2 != '\0'))
+  const char *end_str2 = string2 + string2_len;
+
+  while (1)
     {
       while (isspace (*string1))
-	{
-	  string1++;
-	}
-      while (isspace (*string2))
-	{
-	  string2++;
-	}
+	string1++;
+      while (string2 < end_str2 && isspace (*string2))
+	string2++;
+      if (*string1 == '\0' || string2 == end_str2)
+	break;
       if (case_sensitivity == case_sensitive_on && *string1 != *string2)
 	break;
       if (case_sensitivity == case_sensitive_off
 	  && (tolower ((unsigned char) *string1)
 	      != tolower ((unsigned char) *string2)))
 	break;
-      if (*string1 != '\0')
-	{
-	  string1++;
-	  string2++;
-	}
+
+      string1++;
+      string2++;
     }
-  return (*string1 != '\0' && *string1 != '(') || (*string2 != '\0');
+
+  if (string2 == end_str2)
+    {
+      if (mode == strncmp_iw_mode::NORMAL)
+	return 0;
+      else
+	return (*string1 != '\0' && *string1 != '(');
+    }
+  else
+    return 1;
+}
+
+/* See utils.h.  */
+
+int
+strncmp_iw (const char *string1, const char *string2, size_t string2_len)
+{
+  return strncmp_iw_with_mode (string1, string2, string2_len,
+			       strncmp_iw_mode::NORMAL);
+}
+
+/* See utils.h.  */
+
+int
+strcmp_iw (const char *string1, const char *string2)
+{
+  return strncmp_iw_with_mode (string1, string2, strlen (string2),
+			       strncmp_iw_mode::MATCH_PARAMS);
 }
 
 /* This is like strcmp except that it ignores whitespace and treats
diff --git a/gdb/utils.h b/gdb/utils.h
index 3347c23..2396dcd 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -31,6 +31,22 @@ extern void initialize_utils (void);
 
 extern int sevenbit_strings;
 
+/* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
+   differences in whitespace.  STRING2_LEN is STRING2's length.
+   Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
+   non-zero otherwise (slightly different than strncmp()'s range of
+   return values).  */
+extern int strncmp_iw (const char *, const char *, size_t);
+
+/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
+   differences in whitespace.  Returns 0 if they match, non-zero if
+   they don't (slightly different than strcmp()'s range of return
+   values).
+
+   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
+   This "feature" is useful when searching for matching C++ function
+   names (such as if the user types 'break FOO', where FOO is a
+   mangled C++ function).  */
 extern int strcmp_iw (const char *, const char *);
 
 extern int strcmp_iw_ordered (const char *, const char *);
-- 
2.5.5

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

* [PATCH 28/40] lookup_name_info::make_ignore_params
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (18 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-08 20:55   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 15/40] Rewrite/enhance explicit locations completer, parse left->right Pedro Alves
                   ` (20 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

A few places in the completion code look for a "(" to find a
function's parameter list, in order to strip it, because psymtabs (and
gdb index) don't include parameter info in the symbol names.

See compare_symbol_name and
default_collect_symbol_completion_matches_break_on.

This is too naive.  Consider:

 ns_overload2_test::([TAB]

We'd want to complete that to:
 ns_overload2_test::(anonymous namespace)::struct_overload2_test

Or:

 b (anonymous namespace)::[TAB]

That currently completes to:

 b (anonymous namespace)

Which is obviously broken.  This patch makes that work.

Also, the current compare_symbol_name hack means that while this
works:

 "b function([TAB]"  ->  "b function()"

This does not:

 "b function ([TAB]"

This patch fixes that.  Whitespace "ignoring" now Just Works, i.e.,
assuming a symbol named "function(int, long)", this:
 b function (   int , lon[TAB]
completes to:
 b function (   int , long)

To address all of this, this patch builds on top of the rest of the
series, and pushes the responsibility of stripping parameters from a
lookup name to the new lookup_name_info object, where we can apply
per-language rules.  Also note that we now only make a version of the
lookup name with parameters stripped out where it's actually required
to do that, in the psymtab and GDB index code.

For C++, the right way to strip parameters is with "cp_remove_params",
which uses a real parser (cp-name-parser.y) to split the name into a
component tree and then discards parameters.

The trouble for completion is that in that case we have an incomplete
name, like "foo::func(int" and thus cp_remove_params throws an error.

This patch sorts that by adding a cp_remove_params_if_any variant of
cp_remove_params that tries removing characters from the end of the
string until cp_remove_params works.  So cp_remove_params_if_any
behaves like this:

With a complete name:

   "foo::func(int)"  => foo::func(int)  # cp_remove_params_1 succeeds the first time.

With an incomplete name:

   "foo::func(int"  => NULL             # cp_remove_params fails the first time.
   "foo::func(in"   => NULL             # and again...
   "foo::func(i"    => NULL             # and again...
   "foo::func("     => NULL             # and again...
   "foo::func"      => "foo::func"      # success!

Note that even if this approach removes significant rightmost
characters, it's still OK, because this parameter stripping is only
necessary for psymtabs and gdb index, where we're determining whether
to expand a symbol table.  Say cp_remove_params_if_any returned
"foo::" above for "foo::func(int".  That'd cause us to expand more
symtabs than ideal (because we'd expand all symtabs with symbols that
start with "foo::", not just "foo::func"), but then when we actually
look for completion matches, we'd still use the original lookup name,
with parameter information ["foo::func(int"], and thus we'll return no
false positive to the user.  Whether the stripping works as intended
and doesn't strip too much is thus covered by a unit test instead of a
testsuite test.

The "if_any" part of the name refers to the fact that while
cp_remove_params returns NULL if the input name has no parameters in
the first place, like:

   "foo::func"      => NULL         # cp_remove_params

cp_remove_params_if_any still returns the function name:

   "foo::func"      => "foo::func"  # cp_remove_params_if_any

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
	unittests/lookup_name_info-selftests.c.
	(SUBDIR_UNITTESTS_OBS): Add lookup_name_info-selftests.o.
	* cp-support.c: Include "selftest.h".
	(cp_remove_params_1): Rename from cp_remove_params.  Add
	'require_param's parameter, and handle it.
	(cp_remove_params): Reimplement.
	(cp_remove_params_if_any): New.
	(selftests::quote): New.
	(selftests::check_remove_params): New.
	(selftests::test_cp_remove_params): New.
	(_initialize_cp_support): Install
	selftests::test_cp_remove_params.
	* cp-support.h (cp_remove_params_if_any): Declare.
	* dwarf2read.c :Include "selftest.h".
	(dw2_expand_symtabs_matching_symbol): Use
	lookup_name_info::make_ignore_params.
	(selftests::dw2_expand_symtabs_matching::mock_mapped_index)
	(selftests::dw2_expand_symtabs_matching::string_or_null)
	(selftests::dw2_expand_symtabs_matching::check_match)
	(selftests::dw2_expand_symtabs_matching::test_symbols)
	(selftests::dw2_expand_symtabs_matching::run_test): New.
	(_initialize_dwarf2_read): Register
	selftests::dw2_expand_symtabs_matching::run_test.
	* psymtab.c (psym_expand_symtabs_matching): Use
	lookup_name_info::make_ignore_params.
	* symtab.c (demangle_for_lookup_info::demangle_for_lookup_info):
	If the lookup name wants to ignore parameters, strip them.
	(compare_symbol_name): Remove sym_text/sym_text_len parameters and
	code handling '('.
	(completion_list_add_name): Don't pass down sym_text/sym_text_len.
	(default_collect_symbol_completion_matches_break_on): Don't try to
	strip parameters.
	* symtab.h (lookup_name_info::lookup_name_info): Add
	'ignore_parameters' parameter.
	(lookup_name_info::ignore_parameters)
	(lookup_name_info::make_ignore_params): New methods.
	(lookup_name_info::m_ignore_parameters): New field.
	* unittests/lookup_name_info-selftests.c: New file.
---
 gdb/Makefile.in                            |   2 +
 gdb/cp-support.c                           | 190 +++++++++++++++++-
 gdb/cp-support.h                           |   3 +
 gdb/dwarf2read.c                           | 299 ++++++++++++++++++++++++++++-
 gdb/psymtab.c                              |   4 +-
 gdb/symtab.c                               |  65 ++-----
 gdb/symtab.h                               |  15 +-
 gdb/unittests/lookup_name_info-selftests.c | 110 +++++++++++
 8 files changed, 629 insertions(+), 59 deletions(-)
 create mode 100644 gdb/unittests/lookup_name_info-selftests.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 380df11..d92c823 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -527,6 +527,7 @@ SUBDIR_PYTHON_CFLAGS =
 
 SUBDIR_UNITTESTS_SRCS = \
 	unittests/function-view-selftests.c \
+	unittests/lookup_name_info-selftests.c \
 	unittests/offset-type-selftests.c \
 	unittests/optional-selftests.c \
 	unittests/ptid-selftests.c \
@@ -534,6 +535,7 @@ SUBDIR_UNITTESTS_SRCS = \
 
 SUBDIR_UNITTESTS_OBS = \
 	function-view-selftests.o \
+	lookup_name_info-selftests.o \
 	offset-type-selftests.o \
 	optional-selftests.o \
 	ptid-selftests.o \
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 0095c6d..064a45b 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -36,6 +36,7 @@
 #include <signal.h>
 #include "gdb_setjmp.h"
 #include "safe-ctype.h"
+#include "selftest.h"
 
 #define d_left(dc) (dc)->u.s_binary.left
 #define d_right(dc) (dc)->u.s_binary.right
@@ -833,12 +834,14 @@ cp_func_name (const char *full_name)
   return ret;
 }
 
-/* DEMANGLED_NAME is the name of a function, including parameters and
-   (optionally) a return type.  Return the name of the function without
-   parameters or return type, or NULL if we can not parse the name.  */
+/* Helper for cp_remove_params.  DEMANGLED_NAME is the name of a
+   function, including parameters and (optionally) a return type.
+   Return the name of the function without parameters or return type,
+   or NULL if we can not parse the name.  If COMPLETION_MODE is true,
+   then tolerate a non-existing or unbalanced parameter list.  */
 
-gdb::unique_xmalloc_ptr<char>
-cp_remove_params (const char *demangled_name)
+static gdb::unique_xmalloc_ptr<char>
+cp_remove_params_1 (const char *demangled_name, bool require_params)
 {
   bool done = false;
   struct demangle_component *ret_comp;
@@ -874,10 +877,60 @@ cp_remove_params (const char *demangled_name)
   /* What we have now should be a function.  Return its name.  */
   if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME)
     ret = cp_comp_to_string (d_left (ret_comp), 10);
+  else if (!require_params
+	   && (ret_comp->type == DEMANGLE_COMPONENT_NAME
+	       || ret_comp->type == DEMANGLE_COMPONENT_QUAL_NAME
+	       || ret_comp->type == DEMANGLE_COMPONENT_TEMPLATE))
+    ret = cp_comp_to_string (ret_comp, 10);
 
   return gdb::unique_xmalloc_ptr<char> (ret);
 }
 
+/* DEMANGLED_NAME is the name of a function, including parameters and
+   (optionally) a return type.  Return the name of the function
+   without parameters or return type, or NULL if we can not parse the
+   name.  */
+
+gdb::unique_xmalloc_ptr<char>
+cp_remove_params (const char *demangled_name)
+{
+  return cp_remove_params_1 (demangled_name, true);
+}
+
+/* DEMANGLED_NAME is the name of a function, (optionally) including
+   parameters and (optionally) a return type.  Return the name of the
+   function without parameters or return type, or NULL if we can not
+   parse the name.  If COMPLETION_MODE is true, then tolerate a
+   non-existing or unbalanced parameter list.  */
+
+gdb::unique_xmalloc_ptr<char>
+cp_remove_params_if_any (const char *qualified, bool completion_mode)
+{
+  /* Trying to remove parameters from the empty string fails.  If
+     we're completing / matching everything, avoid returning NULL
+     which would make callers interpret the result as an error.  */
+  if (qualified[0] == '\0' && completion_mode)
+    return gdb::unique_xmalloc_ptr<char> (xstrdup (""));
+
+  gdb::unique_xmalloc_ptr<char> without_params
+    = cp_remove_params_1 (qualified, false);
+
+  if (without_params == NULL && completion_mode)
+    {
+      std::string copy = qualified;
+
+      while (!copy.empty ())
+	{
+	  copy.pop_back ();
+	  without_params = cp_remove_params_1 (copy.c_str (), false);
+	  if (without_params != NULL)
+	    break;
+	}
+    }
+
+  return without_params;
+}
+
 /* Here are some random pieces of trivia to keep in mind while trying
    to take apart demangled names:
 
@@ -1624,6 +1677,129 @@ cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
   return cp_fq_symbol_name_matches;
 }
 
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+/* If non-NULL, return STR wrapped in quotes.  Otherwise, return a
+   "<null>" string (with no quotes).  */
+
+static std::string
+quote (const char *str)
+{
+  if (str != NULL)
+    return std::string (1, '\"') + str + '\"';
+  else
+    return "<null>";
+}
+
+/* Check that removing parameter info out of NAME produces EXPECTED.
+   COMPLETION_MODE indicates whether we're testing normal and
+   completion mode.  FILE and LINE are used to provide better test
+   location information in case ithe check fails.  */
+
+static void
+check_remove_params (const char *file, int line,
+		      const char *name, const char *expected,
+		      bool completion_mode)
+{
+  gdb::unique_xmalloc_ptr<char> result
+    = cp_remove_params_if_any (name, completion_mode);
+
+  if ((expected == NULL) != (result == NULL)
+      || (expected != NULL
+	  && strcmp (result.get (), expected) != 0))
+    {
+      error (_("%s:%d: make-paramless self-test failed: (completion=%d) "
+	       "\"%s\" -> %s, expected %s"),
+	     file, line, completion_mode, name,
+	     quote (result.get ()).c_str (), quote (expected).c_str ());
+    }
+}
+
+/* Entry point for cp_remove_params unit tests.  */
+
+static void
+test_cp_remove_params ()
+{
+  /* Check that removing parameter info out of NAME produces EXPECTED.
+     Checks both normal and completion modes.  */
+#define CHECK(NAME, EXPECTED)						\
+  do									\
+    {									\
+      check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, false);	\
+      check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, true);	\
+    }									\
+  while (0)
+
+  /* Similar, but used when NAME is incomplete -- i.e., is has
+     unbalanced parentheses.  In this case, looking for the exact name
+     should fail / return empty.  */
+#define CHECK_INCOMPL(NAME, EXPECTED)					\
+  do									\
+    {									\
+      check_remove_params (__FILE__, __LINE__, NAME, NULL, false);	\
+      check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, true);	\
+    }									\
+  while (0)
+
+  CHECK ("function()", "function");
+  CHECK_INCOMPL ("function(", "function");
+  CHECK ("function() const", "function");
+
+  CHECK ("(anonymous namespace)::A::B::C",
+	 "(anonymous namespace)::A::B::C");
+
+  CHECK ("A::(anonymous namespace)",
+	 "A::(anonymous namespace)");
+
+  CHECK_INCOMPL ("A::(anonymou", "A");
+
+  CHECK ("A::foo<int>()",
+	 "A::foo<int>");
+
+  CHECK_INCOMPL ("A::foo<int>(",
+		 "A::foo<int>");
+
+  CHECK ("A::foo<(anonymous namespace)::B>::func(int)",
+	 "A::foo<(anonymous namespace)::B>::func");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>::func(in",
+		 "A::foo<(anonymous namespace)::B>::func");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>::",
+		 "A::foo<(anonymous namespace)::B>");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>:",
+		 "A::foo<(anonymous namespace)::B>");
+
+  CHECK ("A::foo<(anonymous namespace)::B>",
+	 "A::foo<(anonymous namespace)::B>");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B",
+		 "A::foo");
+
+  /* Shouldn't this parse?  Looks like a bug in
+     cp_demangled_name_to_comp.  */
+#if 0
+  CHECK ("A::foo<void(int)>::func(int)",
+	 "A::foo<void(int)>::func");
+#else
+  CHECK_INCOMPL ("A::foo<void(int)>::func(int)",
+		 "A::foo");
+#endif
+
+  CHECK_INCOMPL ("A::foo<void(int",
+		 "A::foo");
+
+#undef CHECK
+#undef CHECK_INCOMPL
+}
+
+} // namespace selftests
+
+#endif /* GDB_SELF_CHECK */
+
 /* Don't allow just "maintenance cplus".  */
 
 static  void
@@ -1709,4 +1885,8 @@ display the offending symbol."),
 			   &maintenance_set_cmdlist,
 			   &maintenance_show_cmdlist);
 #endif
+
+#if GDB_SELF_TEST
+  register_self_test (selftests::test_cp_remove_params);
+#endif
 }
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index dd42415..1cef3f7 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -97,6 +97,9 @@ extern char *cp_func_name (const char *full_name);
 
 extern gdb::unique_xmalloc_ptr<char> cp_remove_params (const char *qualified);
 
+extern gdb::unique_xmalloc_ptr<char> cp_remove_params_if_any
+  (const char *qualified, bool completion_mode);
+
 extern struct symbol **make_symbol_overload_list (const char *,
 						  const char *);
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index dfa79aa..a267377 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -77,6 +77,7 @@
 #include <sys/types.h>
 #include <algorithm>
 #include "filename-seen-cache.h"
+#include "selftest.h"
 
 typedef struct symbol *symbolp;
 DEF_VEC_P (symbolp);
@@ -4253,13 +4254,15 @@ dw2_expand_symtabs_matching
 static void
 dw2_expand_symtabs_matching_symbol
   (mapped_index &index,
-   const lookup_name_info &lookup_name,
+   const lookup_name_info &lookup_name_in,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    enum search_domain kind,
    gdb::function_view<void (offset_type)> match_callback)
 {
+  lookup_name_info lookup_name_without_params
+    = lookup_name_in.make_ignore_params ();
   gdb_index_symbol_name_matcher lookup_name_matcher
-    (lookup_name);
+    (lookup_name_without_params);
 
   auto *name_cmp = case_sensitivity == case_sensitive_on ? strcmp : strcasecmp;
 
@@ -4317,7 +4320,7 @@ dw2_expand_symtabs_matching_symbol
     }
 
   const char *cplus
-    = lookup_name.cplus ().lookup_name ().c_str ();
+    = lookup_name_without_params.cplus ().lookup_name ().c_str ();
 
   /* Comparison function object for lower_bound that matches against a
      given symbol name.  */
@@ -4345,7 +4348,7 @@ dw2_expand_symtabs_matching_symbol
   /* Find the lower bound.  */
   auto lower = [&] ()
     {
-      if (lookup_name.completion_mode () && cplus[0] == '\0')
+      if (lookup_name_in.completion_mode () && cplus[0] == '\0')
 	return begin;
       else
 	return std::lower_bound (begin, end, cplus, lookup_compare_lower);
@@ -4354,7 +4357,7 @@ dw2_expand_symtabs_matching_symbol
   /* Find the upper bound.  */
   auto upper = [&] ()
     {
-      if (lookup_name.completion_mode ())
+      if (lookup_name_in.completion_mode ())
 	{
 	  /* The string frobbing below won't work if the string is
 	     empty.  We don't need it then, anyway -- if we're
@@ -4420,6 +4423,288 @@ dw2_expand_symtabs_matching_symbol
   static_assert (sizeof (prev) > sizeof (offset_type), "");
 }
 
+#if GDB_SELF_TEST
+
+namespace selftests { namespace dw2_expand_symtabs_matching {
+
+/* A wrapper around mapped_index that builds a mock mapped_index, from
+   the symbol list passed as parameter to the constructor.  */
+class mock_mapped_index
+{
+public:
+  template<size_t N>
+  mock_mapped_index (const char *(&symbols)[N])
+    : mock_mapped_index (symbols, N)
+  {}
+
+  /* Access the built index.  */
+  mapped_index &index ()
+  { return m_index; }
+
+  /* Disable copy.  */
+  mock_mapped_index(const mock_mapped_index &) = delete;
+  void operator= (const mock_mapped_index &) = delete;
+
+private:
+  mock_mapped_index (const char **symbols, size_t symbols_size)
+  {
+    /* No string can live at offset zero.  Add a dummy entry.  */
+    obstack_grow_str0 (&m_constant_pool, "");
+
+    for (size_t i = 0; i < symbols_size; i++)
+      {
+	const char *sym = symbols[i];
+	size_t offset = obstack_object_size (&m_constant_pool);
+	obstack_grow_str0 (&m_constant_pool, sym);
+	m_symbol_table.push_back (offset);
+	m_symbol_table.push_back (0);
+      };
+
+    m_index.constant_pool = (const char *) obstack_base (&m_constant_pool);
+    m_index.symbol_table = m_symbol_table.data ();
+    m_index.symbol_table_slots = m_symbol_table.size () / 2;
+  }
+
+public:
+  /* The built mapped_index.  */
+  mapped_index m_index{};
+
+  /* The storage that the built mapped_index uses for symbol and
+     constant pool tables.  */
+  std::vector<offset_type> m_symbol_table;
+  auto_obstack m_constant_pool;
+};
+
+/* Convenience function that converts a NULL pointer to a "<null>"
+   string, to pass to print routines.  */
+
+static const char *
+string_or_null (const char *str)
+{
+  return str != NULL ? str : "<null>";
+}
+
+/* Check if a lookup_name_info built from
+   NAME/MATCH_TYPE/COMPLETION_MODE matches the symbols in the mock
+   index.  EXPECTED_LIST is the list of expected matches, in expected
+   matching order.  If no match expected, then an empty list is
+   specified.  Returns true on success.  On failure prints a warning
+   indicating the file:line that failed, and returns false.  */
+
+static bool
+check_match (const char *file, int line,
+	     mock_mapped_index &mock_index,
+	     const char *name, symbol_name_match_type match_type,
+	     bool completion_mode,
+	     std::initializer_list<const char *> expected_list)
+{
+  lookup_name_info lookup_name (name, match_type, completion_mode);
+
+  bool matched = true;
+
+  auto mismatch = [&] (const char *expected_str,
+		       const char *got)
+  {
+    warning (_("%s:%d: match_type=%s, looking-for=\"%s\", "
+	       "expected=\"%s\", got=\"%s\"\n"),
+	     file, line,
+	     (match_type == symbol_name_match_type::FULL
+	      ? "FULL" : "WILD"),
+	     name, string_or_null (expected_str), string_or_null (got));
+    matched = false;
+  };
+
+  auto expected_it = expected_list.begin ();
+  auto expected_end = expected_list.end ();
+
+  dw2_expand_symtabs_matching_symbol (mock_index.index (), lookup_name,
+				      NULL, ALL_DOMAIN,
+				      [&] (offset_type idx)
+  {
+    const char *matched_name = mock_index.index ().symbol_name_at (idx);
+    const char *expected_str
+      = expected_it == expected_end ? NULL : *expected_it++;
+
+    if (expected_str == NULL || strcmp (expected_str, matched_name) != 0)
+      mismatch (expected_str, matched_name);
+  });
+
+  const char *expected_str
+  = expected_it == expected_end ? NULL : *expected_it++;
+  if (expected_str != NULL)
+    mismatch (expected_str, NULL);
+
+  return matched;
+}
+
+/* The symbols added to the mock mapped_index for testing (in
+   canonical form).  */
+static const char *test_symbols[] = {
+  "function",
+  "std::bar",
+  "std::zfunction",
+  "std::zfunction2",
+  "w1::w2",
+  "ns::foo<char*>",
+  "ns::foo<int>",
+  "ns::foo<long>",
+
+  /* A name with all sorts of complications.  Starts with "z" to make
+     it easier for the completion tests below.  */
+#define Z_SYM_NAME \
+  "z::std::tuple<(anonymous namespace)::ui*, std::bar<(anonymous namespace)::ui> >" \
+    "::tuple<(anonymous namespace)::ui*, " \
+    "std::default_delete<(anonymous namespace)::ui>, void>"
+
+  Z_SYM_NAME
+};
+
+static void
+run_test ()
+{
+  mock_mapped_index mock_index (test_symbols);
+
+  /* We let all tests run until the end even if some fails, for debug
+     convenience.  */
+  bool any_mismatch = false;
+
+  /* Create the expected symbols list (an initializer_list).  Needed
+     because lists have commas, and we need to pass them to CHECK,
+     which is a macro.  */
+#define EXPECT(...) { __VA_ARGS__ }
+
+  /* Wrapper for check_match that passes down the current
+     __FILE__/__LINE__.  */
+#define CHECK_MATCH(NAME, MATCH_TYPE, COMPLETION_MODE, EXPECTED_LIST)	\
+  any_mismatch |= !check_match (__FILE__, __LINE__,			\
+				mock_index,				\
+				NAME, MATCH_TYPE, COMPLETION_MODE,	\
+				EXPECTED_LIST)
+
+  /* Identity checks.  */
+  for (const char *sym : test_symbols)
+    {
+      /* Should be able to match all existing symbols.  */
+      CHECK_MATCH (sym, symbol_name_match_type::FULL, false,
+		   EXPECT (sym));
+
+      /* Should be able to match all existing symbols with
+	 parameters.  */
+      std::string with_params = std::string (sym) + "(int)";
+      CHECK_MATCH (with_params.c_str (), symbol_name_match_type::FULL, false,
+		   EXPECT (sym));
+
+      /* Should be able to match all existing symbols with
+	 parameters and qualifiers.  */
+      with_params = std::string (sym) + " ( int ) const";
+      CHECK_MATCH (with_params.c_str (), symbol_name_match_type::FULL, false,
+		   EXPECT (sym));
+
+      /* This should really find sym, but cp-name-parser.y doesn't
+	 know about lvalue/rvalue qualifiers yet.  */
+      with_params = std::string (sym) + " ( int ) &&";
+      CHECK_MATCH (with_params.c_str (), symbol_name_match_type::FULL, false,
+		   {});
+    }
+
+  /* Check that completion mode works at each prefix of the expected
+     symbol name.  */
+  {
+    static const char str[] = "function(int)";
+    size_t len = strlen (str);
+    std::string lookup;
+
+    for (size_t i = 1; i < len; i++)
+      {
+	lookup.assign (str, i);
+	CHECK_MATCH (lookup.c_str (), symbol_name_match_type::FULL, true,
+		     EXPECT ("function"));
+      }
+  }
+
+  /* While "w" is a prefix of both components, the match function
+     should still only be called once.  */
+  {
+    CHECK_MATCH ("w", symbol_name_match_type::FULL, true,
+		 EXPECT ("w1::w2"));
+  }
+
+  /* Same, with a "complicated" symbol.  */
+  {
+    static const char str[] = Z_SYM_NAME;
+    size_t len = strlen (str);
+    std::string lookup;
+
+    for (size_t i = 1; i < len; i++)
+      {
+	lookup.assign (str, i);
+	CHECK_MATCH (lookup.c_str (), symbol_name_match_type::FULL, true,
+		     EXPECT (Z_SYM_NAME));
+      }
+  }
+
+  /* In FULL mode, an incomplete symbol doesn't match.  */
+  {
+    CHECK_MATCH ("std::zfunction(int", symbol_name_match_type::FULL, false,
+		 {});
+  }
+
+  /* A complete symbol with parameters matches any overload, since the
+     index has no overload info.  */
+  {
+    CHECK_MATCH ("std::zfunction(int)", symbol_name_match_type::FULL, true,
+		 EXPECT ("std::zfunction", "std::zfunction2"));
+  }
+
+  /* Check that whitespace is ignored appropriately.  A symbol with a
+     template argument list. */
+  {
+    static const char expected[] = "ns::foo<int>";
+    CHECK_MATCH ("ns :: foo < int > ", symbol_name_match_type::FULL, false,
+		 EXPECT (expected));
+  }
+
+  /* Check that whitespace is ignored appropriately.  A symbol with a
+     template argument list that includes a pointer.  */
+  {
+    static const char expected[] = "ns::foo<char*>";
+    /* Try both completion and non-completion modes.  */
+    static const bool completion_mode[2] = {false, true};
+    for (size_t i = 0; i < 2; i++)
+      {
+	CHECK_MATCH ("ns :: foo < char * >", symbol_name_match_type::FULL,
+		     completion_mode[i], EXPECT (expected));
+
+	CHECK_MATCH ("ns :: foo < char * > (int)", symbol_name_match_type::FULL,
+		     completion_mode[i], EXPECT (expected));
+      }
+  }
+
+  {
+    /* Check method qualifiers are ignored.  */
+    static const char expected[] = "ns::foo<char*>";
+    CHECK_MATCH ("ns :: foo < char * >  ( int ) const",
+		 symbol_name_match_type::FULL, true, EXPECT (expected));
+    CHECK_MATCH ("ns :: foo < char * >  ( int ) &&",
+		 symbol_name_match_type::FULL, true, EXPECT (expected));
+  }
+
+  /* Test lookup names that don't match anything.  */
+  {
+    CHECK_MATCH ("doesntexist", symbol_name_match_type::FULL, false,
+		 {});
+  }
+
+  SELF_CHECK (!any_mismatch);
+
+#undef EXPECT
+#undef CHECK_MATCH
+}
+
+}} // namespace selftests::dw2_expand_symtabs_matching
+
+#endif /* GDB_SELF_TEST */
+
 /* Helper for dw2_expand_matching symtabs.  Called on each symbol
    matched, to expand corresponding CUs that were marked.  IDX is the
    index of the symbol name that matched.  */
@@ -24536,4 +24821,8 @@ Usage: save gdb-index DIRECTORY"),
 					&dwarf2_block_frame_base_locexpr_funcs);
   dwarf2_loclist_block_index = register_symbol_block_impl (LOC_BLOCK,
 					&dwarf2_block_frame_base_loclist_funcs);
+
+#if GDB_SELF_TEST
+  register_self_test (selftests::dw2_expand_symtabs_matching::run_test);
+#endif
 }
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 1a1929d..a51637b 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -1408,13 +1408,15 @@ static void
 psym_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
-   const lookup_name_info &lookup_name,
+   const lookup_name_info &lookup_name_in,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain domain)
 {
   struct partial_symtab *ps;
 
+  lookup_name_info lookup_name = lookup_name_in.make_ignore_params ();
+
   /* Clear the search flags.  */
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
     {
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 41348bd..68b8af5 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -1769,6 +1769,20 @@ demangle_for_lookup_info::demangle_for_lookup_info (const lookup_name_info &look
 {
   demangle_result_storage storage;
 
+  if (lookup_name.ignore_parameters () && lang == language_cplus)
+    {
+      gdb::unique_xmalloc_ptr<char> without_params
+	= cp_remove_params_if_any (lookup_name.name ().c_str (),
+				   lookup_name.completion_mode ());
+
+      if (without_params != NULL)
+	{
+	  m_demangled_name = demangle_for_lookup (without_params.get (),
+						  lang, storage);
+	  return;
+	}
+    }
+
   m_demangled_name = demangle_for_lookup (lookup_name.name ().c_str (),
 					  lang, storage);
 }
@@ -4767,20 +4781,11 @@ rbreak_command (char *regexp, int from_tty)
 }
 \f
 
-/* Evaluate if NAME matches SYM_TEXT and SYM_TEXT_LEN.
-
-   Either sym_text[sym_text_len] != '(' and then we search for any
-   symbol starting with SYM_TEXT text.
-
-   Otherwise sym_text[sym_text_len] == '(' and then we require symbol name to
-   be terminated at that point.  Partial symbol tables do not have parameters
-   information.  */
+/* Evaluate if SYMNAME matches LOOKUP_NAME.  */
 
 static int
-compare_symbol_name (const char *name,
-		     language symbol_language,
+compare_symbol_name (const char *symbol_name, language symbol_language,
 		     const lookup_name_info &lookup_name,
-		     const char *sym_text, int sym_text_len,
 		     completion_match_result &match_res)
 {
   const language_defn *lang;
@@ -4802,23 +4807,7 @@ compare_symbol_name (const char *name,
   symbol_name_matcher_ftype *name_match
     = language_get_symbol_name_matcher (lang, lookup_name);
 
-  /* Clip symbols that cannot match.  */
-  if (!name_match (name, lookup_name, &match_res.match))
-    return 0;
-
-  if (sym_text[sym_text_len] == '(')
-    {
-      /* User searches for `name(someth...'.  Require NAME to be terminated.
-	 Normally psymtabs and gdbindex have no parameter types so '\0' will be
-	 present but accept even parameters presence.  In this case this
-	 function is in fact strcmp_iw but whitespace skipping is not supported
-	 for tab completion.  */
-
-      if (name[sym_text_len] != '\0' && name[sym_text_len] != '(')
-	return 0;
-    }
-
-  return 1;
+  return name_match (symbol_name, lookup_name, &match_res.match);
 }
 
 /*  Test to see if the symbol specified by SYMNAME (which is already
@@ -4837,10 +4826,7 @@ completion_list_add_name (completion_tracker &tracker,
     = tracker.reset_completion_match_result ();
 
   /* Clip symbols that cannot match.  */
-  if (!compare_symbol_name (symname, symbol_language,
-			    lookup_name,
-			    sym_text, sym_text_len,
-			    match_res))
+  if (!compare_symbol_name (symname, symbol_language, lookup_name, match_res))
     return;
 
   /* Refresh SYMNAME from the match string.  It's potentially
@@ -5165,21 +5151,6 @@ default_collect_symbol_completion_matches_break_on
 
   sym_text_len = strlen (sym_text);
 
-  /* Prepare SYM_TEXT_LEN for compare_symbol_name.  */
-
-  if (current_language->la_language == language_cplus
-      || current_language->la_language == language_fortran)
-    {
-      /* These languages may have parameters entered by user but they are never
-	 present in the partial symbol tables.  */
-
-      const char *cs = (const char *) memchr (sym_text, '(', sym_text_len);
-
-      if (cs)
-	sym_text_len = cs - sym_text;
-    }
-  gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
-
   lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
 				name_match_type, true);
 
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 4c8b1f6..a452804 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -166,9 +166,11 @@ class lookup_name_info final
   /* Create a new object.  */
   lookup_name_info (std::string name,
 		    symbol_name_match_type match_type,
-		    bool completion_mode = false)
+		    bool completion_mode = false,
+		    bool ignore_parameters = false)
     : m_match_type (match_type),
       m_completion_mode (completion_mode),
+      m_ignore_parameters (ignore_parameters),
       m_name (std::move (name))
   {}
 
@@ -176,6 +178,16 @@ class lookup_name_info final
   symbol_name_match_type match_type () const { return m_match_type; }
   bool completion_mode () const { return m_completion_mode; }
   const std::string &name () const { return m_name; }
+  const bool ignore_parameters () const { return m_ignore_parameters; }
+
+  /* Return a version of this lookup name that is usable with
+     comparisons against symbols have have no parameter info, such as
+     psymbols and GDB index symbols.  */
+  lookup_name_info make_ignore_params () const
+  {
+    return lookup_name_info (m_name, m_match_type, m_completion_mode,
+			     true /* ignore params */);
+  }
 
   /* Get the search name hash for searches in language LANG.  */
   unsigned int search_name_hash (language lang) const
@@ -252,6 +264,7 @@ private:
   /* The lookup info as passed to the ctor.  */
   symbol_name_match_type m_match_type;
   bool m_completion_mode;
+  bool m_ignore_parameters;
   std::string m_name;
 
   /* Language-specific info.  These fields are filled lazily the first
diff --git a/gdb/unittests/lookup_name_info-selftests.c b/gdb/unittests/lookup_name_info-selftests.c
new file mode 100644
index 0000000..99238ae
--- /dev/null
+++ b/gdb/unittests/lookup_name_info-selftests.c
@@ -0,0 +1,110 @@
+/* Self tests for lookup_name_info for GDB, the GNU debugger.
+
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "selftest.h"
+#include "symtab.h"
+
+namespace selftests {
+namespace lookup_name {
+
+/* Check that removing parameter info out of NAME produces EXPECTED.
+   COMPLETION_MODE indicates whether we're testing normal and
+   completion mode.  FILE and LINE are used to provide better test
+   location information in case the check fails.  */
+
+static void
+check_make_paramless (const char *file, int line,
+		      enum language lang,
+		      const char *name, const char *expected,
+		      bool completion_mode)
+{
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL,
+				completion_mode, true /* ignore_parameters */);
+  const std::string &result = lookup_name.language_lookup_name (lang);
+
+  if (result != expected)
+    {
+      error (_("%s:%d: make-paramless self-test failed: (completion=%d, lang=%d) "
+	       "\"%s\" -> \"%s\", expected \"%s\""),
+	     file, line, completion_mode, lang, name,
+	     result.c_str (), expected);
+    }
+}
+
+static void
+run_tests ()
+{
+  /* Helper for CHECK and CHECK_INCOMPL.  */
+#define CHECK_1(INCOMPLETE, LANG, NAME, EXPECTED)			\
+  do									\
+    {									\
+      check_make_paramless (__FILE__, __LINE__,				\
+			    LANG, NAME,					\
+			    (INCOMPLETE) ? "" : (EXPECTED), false);	\
+      check_make_paramless (__FILE__, __LINE__,				\
+			    LANG, NAME, EXPECTED, true);		\
+    }									\
+  while (0)
+
+  /* Check that removing parameter info out of NAME produces EXPECTED.
+     Checks both normal and completion modes.  */
+#define CHECK(LANG, NAME, EXPECTED)		\
+  CHECK_1(false, LANG, NAME, EXPECTED)
+
+  /* Similar, but used when NAME is incomplete -- i.e., NAME has
+     unbalanced parentheses.  In this case, looking for the exact name
+     should fail / return empty.  */
+#define CHECK_INCOMPL(LANG, NAME, EXPECTED)				\
+  CHECK_1 (true, LANG, NAME, EXPECTED)
+
+  /* None of these languages support function overloading like C++
+     does, so building a nameless lookup name ends up being just the
+     same as any other lookup name.  Mainly this serves as smoke test
+     that C++-specific code doesn't mess up with other languages that
+     use some other scoping character ('.' vs '::').  */
+  CHECK (language_ada, "pck.ada_hello", "pck__ada_hello");
+  CHECK (language_go, "pck.go_hello", "pck.go_hello");
+  CHECK (language_fortran, "mod::func", "mod::func");
+
+  /* D does support function overloading similar to C++, but we're
+     missing support for stripping parameters.  At least make sure the
+     input name is preserved unmodified.  */
+  CHECK (language_d, "pck.d_hello", "pck.d_hello");
+
+  /* Just a few basic tests to make sure
+     lookup_name_info::make_paramless is well integrated with
+     cp_remove_params_if_any.  gdb/cp-support.c has comprehensive
+     testing of C++ specifics.  */
+  CHECK (language_cplus, "function()", "function");
+  CHECK (language_cplus, "function() const", "function");
+  CHECK (language_cplus, "A::B::C()", "A::B::C");
+  CHECK (language_cplus, "A::B::C", "A::B::C");
+
+#undef CHECK
+#undef CHECK_INCOMPL
+}
+
+}} // namespace selftests::lookup_name
+
+void
+_initialize_lookup_name_info_selftests ()
+{
+  register_self_test (selftests::lookup_name::run_tests);
+}
-- 
2.5.5

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

* [PATCH 38/40] Use TOLOWER in SYMBOL_HASH_NEXT
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (20 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 15/40] Rewrite/enhance explicit locations completer, parse left->right Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-09 19:25   ` Keith Seitz
  2017-06-02 12:28 ` [PATCH 24/40] Per-language symbol name hashing algorithm Pedro Alves
                   ` (18 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

The ABI tags patch will add a use of SYMBOL_HASH_NEXT in cp-support.c,
which fails to compile with:

  src/gdb/cp-support.c:38:0:
  src/gdb/cp-support.c: In function ‘unsigned int cp_search_name_hash(const char*)’:
  src/gdb/../include/safe-ctype.h:148:20: error: ‘do_not_use_tolower_with_safe_ctype’ was not declared in this scope
   #define tolower(c) do_not_use_tolower_with_safe_ctype
		      ^
  src/gdb/minsyms.h:174:18: note: in expansion of macro ‘tolower’
     ((hash) * 67 + tolower ((unsigned char) (c)) - 113)
		    ^
  src/gdb/cp-support.c:1677:14: note: in expansion of macro ‘SYMBOL_HASH_NEXT’
	 hash = SYMBOL_HASH_NEXT (hash, *string);
		^

This fixes the problem before it happens.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

     * dictionary.c: Include "safe-ctype.h".
     * minsyms.c: Include "safe-ctype.h".
     * minsyms.c (SYMBOL_HASH_NEXT): Use TOLOWER instead of tolower.
---
 gdb/dictionary.c | 1 +
 gdb/minsyms.c    | 1 +
 gdb/minsyms.h    | 2 +-
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index 87e05d6..079bb04 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -26,6 +26,7 @@
 #include "symtab.h"
 #include "buildsym.h"
 #include "dictionary.h"
+#include "safe-ctype.h"
 
 /* This file implements dictionaries, which are tables that associate
    symbols to names.  They are represented by an opaque type 'struct
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index 842ae07..9751321 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -52,6 +52,7 @@
 #include "cli/cli-utils.h"
 #include "symbol.h"
 #include <algorithm>
+#include "safe-ctype.h"
 
 /* Accumulate the minimal symbols for each objfile in bunches of BUNCH_SIZE.
    At the end, copy them all into one newly allocated location on an objfile's
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index c4a1d21..faff881 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -171,7 +171,7 @@ unsigned int msymbol_hash_iw (const char *);
    requirements.  */
 
 #define SYMBOL_HASH_NEXT(hash, c)			\
-  ((hash) * 67 + tolower ((unsigned char) (c)) - 113)
+  ((hash) * 67 + TOLOWER ((unsigned char) (c)) - 113)
 
 \f
 
-- 
2.5.5

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

* [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (6 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS) Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-09 17:59   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 37/40] Fix completing an empty string Pedro Alves
                   ` (32 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

This exercises the special handling C++ operators require in several
places in the linespec parser, both the linespec and explicit location
completers, symbol lookup, etc.  Particularly, makes sure all that
works without quoting.

Note that despite the apparent smallish size, this adds thousands of
tests to the testsuite, due to combination explosion (linespecs,
explicit locations, tab completion, complete command, completion at
different points in each function, etc.)

Grows the gdb.linespec/ tests like this:

 -# of expected passes           4458
 +# of expected passes           8817

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.linespec/cpls-ops.cc: New file.
	* gdb.linespec/cpls-ops.exp: New file.
---
 gdb/testsuite/gdb.linespec/cpls-ops.cc  | 253 ++++++++++++++
 gdb/testsuite/gdb.linespec/cpls-ops.exp | 567 ++++++++++++++++++++++++++++++++
 2 files changed, 820 insertions(+)
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-ops.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-ops.exp

diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.cc b/gdb/testsuite/gdb.linespec/cpls-ops.cc
new file mode 100644
index 0000000..c1156f1
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls-ops.cc
@@ -0,0 +1,253 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <cstddef>
+
+/* Code for operator() tests.  */
+
+struct test_unique_op_call
+{
+  void operator() (int);
+};
+
+void
+test_unique_op_call::operator() (int)
+{}
+
+struct test_op_call
+{
+  void operator() ();
+  void operator() (int);
+  void operator() (long);
+
+  template<typename T>
+  void operator() (T *);
+};
+
+void
+test_op_call::operator() (int)
+{}
+
+void
+test_op_call::operator() ()
+{}
+
+void
+test_op_call::operator() (long)
+{}
+
+template<typename T>
+void
+test_op_call::operator() (T *t)
+{
+}
+
+/* Code for operator[] tests.  */
+
+struct test_unique_op_array
+{
+  void operator[] (int);
+};
+
+void
+test_unique_op_array::operator[] (int)
+{}
+
+struct test_op_array
+{
+  void operator[] (int);
+  void operator[] (long);
+
+  template<typename T>
+  void operator[] (T *);
+};
+
+void
+test_op_array::operator[] (int)
+{}
+
+void
+test_op_array::operator[] (long)
+{}
+
+template<typename T>
+void
+test_op_array::operator[] (T *t)
+{}
+
+/* Code for operator new tests.  */
+
+struct test_op_new
+{
+  void *operator new (size_t);
+};
+
+void *
+test_op_new::operator new (size_t)
+{
+  return NULL;
+}
+
+/* Code for operator delete tests.  */
+
+struct test_op_delete
+{
+  void operator delete (void *);
+};
+
+void
+test_op_delete::operator delete (void *)
+{
+}
+
+/* Code for operator new[] tests.  */
+
+struct test_op_new_array
+{
+  void *operator new[] (size_t);
+};
+
+void *
+test_op_new_array::operator new[] (size_t)
+{
+  return NULL;
+}
+
+/* Code for operator delete[] tests.  */
+
+struct test_op_delete_array
+{
+  void operator delete[] (void *);
+};
+
+void
+test_op_delete_array::operator delete[] (void *)
+{
+}
+
+/* Code for user-defined conversion tests.  */
+
+struct test_op_conversion_res;
+
+struct test_op_conversion
+{
+  operator const volatile test_op_conversion_res **() const volatile;
+};
+
+test_op_conversion::operator const volatile test_op_conversion_res **() const volatile
+{
+  return NULL;
+}
+
+/* Code for the assignment operator tests.  */
+
+struct test_op_assign
+{
+  test_op_assign operator= (const test_op_assign &);
+};
+
+test_op_assign
+test_op_assign::operator= (const test_op_assign &)
+{
+  return test_op_assign ();
+}
+
+/* Code for the arrow operator tests.  */
+
+struct test_op_arrow
+{
+  test_op_arrow operator-> ();
+};
+
+test_op_arrow
+test_op_arrow::operator-> ()
+{
+  return test_op_arrow ();
+}
+
+/* Code for the logical/arithmetic operators tests.  */
+
+struct E
+{
+};
+
+#define GEN_OP(NS, ...)				\
+  namespace test_ops {				\
+    void operator __VA_ARGS__ {}		\
+  }						\
+  namespace test_op_ ## NS {			\
+    void operator __VA_ARGS__ {}		\
+  }
+
+GEN_OP (PLUS_A,    +=  (E, E)  )
+GEN_OP (PLUS,      +   (E, E)  )
+GEN_OP (MINUS_A,   -=  (E, E)  )
+GEN_OP (MINUS,     -   (E, E)  )
+GEN_OP (MOD_A,     %=  (E, E)  )
+GEN_OP (MOD,       %   (E, E)  )
+GEN_OP (EQ,        ==  (E, E)  )
+GEN_OP (NEQ,       !=  (E, E)  )
+GEN_OP (LAND,      &&  (E, E)  )
+GEN_OP (LOR,       ||  (E, E)  )
+GEN_OP (SL_A,      <<= (E, E)  )
+GEN_OP (SR_A,      >>= (E, E)  )
+GEN_OP (SL,        <<  (E, E)  )
+GEN_OP (SR,        >>  (E, E)  )
+GEN_OP (OE,        |=  (E, E)  )
+GEN_OP (BIT_O,     |   (E, E)  )
+GEN_OP (XOR_A,     ^=  (E, E)  )
+GEN_OP (XOR,       ^   (E, E)  )
+GEN_OP (BIT_AND_A, &=  (E, E)  )
+GEN_OP (BIT_AND,   &   (E, E)  )
+GEN_OP (LT,        <   (E, E)  )
+GEN_OP (LTE,       <=  (E, E)  )
+GEN_OP (GTE,       >=  (E, E)  )
+GEN_OP (GT,        >   (E, E)  )
+GEN_OP (MUL_A,     *=  (E, E)  )
+GEN_OP (MUL,       *   (E, E)  )
+GEN_OP (DIV_A,     /=  (E, E)  )
+GEN_OP (DIV,       /   (E, E)  )
+GEN_OP (NEG,       ~   (E)      )
+GEN_OP (NOT,       !   (E)      )
+GEN_OP (PRE_INC,   ++  (E)      )
+GEN_OP (POST_INC,  ++  (E, int) )
+GEN_OP (PRE_DEC,   --  (E)      )
+GEN_OP (POST_DEC,  --  (E, int) )
+GEN_OP (COMMA,     ,   (E, E)  )
+
+int
+main ()
+{
+  test_op_call opcall;
+  opcall ();
+  opcall (1);
+  opcall (1l);
+  opcall ((int *) 0);
+
+  test_unique_op_call opcall2;
+  opcall2 (1);
+
+  test_op_array op_array;
+  op_array[1];
+  op_array[1l];
+  op_array[(int *) 0];
+
+  test_unique_op_array unique_op_array;
+  unique_op_array[1];
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.exp b/gdb/testsuite/gdb.linespec/cpls-ops.exp
new file mode 100644
index 0000000..5e173d4
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls-ops.exp
@@ -0,0 +1,567 @@
+# Copyright 2017 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/>.
+
+# This file is part of the gdb testsuite.
+
+load_lib completion-support.exp
+
+standard_testfile cpls-ops.cc
+
+if {[prepare_for_testing "failed to prepare" $testfile \
+	 [list $srcfile] {debug}]} {
+    return -1
+}
+
+gdb_test_no_output "set max-completions unlimited"
+
+set timeout 5
+
+# Check that the explicit location completer manages to find the next
+# option name after a "-function function" option.  A useful test when
+# the -function options's argument is a C++ operator, which can
+# include characters like '-'.
+
+proc check_explicit_skips_function_argument {function} {
+    test_gdb_complete_unique \
+	"b -function $function -sour" \
+	"b -function $function -source"
+}
+
+# Helper function for the operator new/new[] tests.  CLASS_NAME is the
+# name of the class that contains the operator we're testing.
+# BRACKETS is set to [] if testing operator new[], and to empty if
+# testing operator new.
+
+proc test_operator_new {class_name brackets} {
+    # The type size_t is typedef-ed to.
+    set size_t "unsigned long"
+
+    # Complete all prefixes between "operato" and the full prototype.
+    foreach cmd_prefix {"b" "b -function"} {
+	set location "${class_name}::operator new${brackets}($size_t)"
+	set line "$cmd_prefix $location"
+	set start [index_after "operato" $line]
+	test_complete_prefix_range $line $start
+	check_bp_locations_match_list "$cmd_prefix $location" [list $location]
+
+	# Same, but with extra spaces.  Note that the original spaces in
+	# the input line are preserved after completion.
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator new " \
+	    "$cmd_prefix ${class_name}::operator new ${brackets}($size_t)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} (" \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} ($size_t)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} ( $size_t " \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} ( $size_t )"
+
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator new${brackets}" \
+		 "${class_name}::operator new${brackets} ($size_t)" \
+		 "${class_name}::operator new ${brackets} ( $size_t )"]
+	foreach linespec $location_list {
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" [list $location]
+	}
+    }
+
+    # Check that the explicit location completer manages to find the
+    # option name after -function, when the -function's argument is a
+    # C++ operator new / new[].
+    check_explicit_skips_function_argument \
+	"${class_name}::operator new ${brackets} ( $size_t )"
+}
+
+proc_with_prefix operator-new {} {
+    test_operator_new test_op_new ""
+}
+
+proc_with_prefix operator-new\[\] {} {
+    test_operator_new test_op_new_array "\[\]"
+}
+
+# Helper function for the operator delete/delete[] tests.  CLASS_NAME
+# is the name of the class that contains the operator we're testing.
+# BRACKETS is set to "[]" if testing operator delete[], and to empty
+# if testing operator delete.
+
+proc test_operator_delete {class_name brackets} {
+    # Complete all prefixes between "operato" and the full prototype.
+    foreach cmd_prefix {"b" "b -function"} {
+	set location "${class_name}::operator delete${brackets}(void*)"
+	set line "$cmd_prefix $location"
+	set start [index_after "operato" $line]
+	test_complete_prefix_range $line $start
+	check_bp_locations_match_list "$cmd_prefix $location" [list $location]
+
+	# Same, but with extra spaces.  Note that the original spaces in
+	# the input line are preserved after completion.
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete " \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets}(void*)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} (" \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} (void*)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void* " \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void* )"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void * " \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void * )"
+
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator delete${brackets}" \
+		 "${class_name}::operator delete${brackets}(void *)" \
+		 "${class_name}::operator delete ${brackets} ( void * )"]
+	foreach linespec $location_list {
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" [list $location]
+	}
+    }
+
+    # Check that the explicit location completer manages to find the
+    # option name after -function, when the -function's argument is a
+    # C++ operator delete / delete[].
+    check_explicit_skips_function_argument \
+	"${class_name}::operator delete ${brackets} ( void * )"
+}
+
+proc_with_prefix operator-delete {} {
+    test_operator_delete test_op_delete ""
+}
+
+proc_with_prefix operator-delete\[\] {} {
+    test_operator_delete test_op_delete_array "\[\]"
+}
+
+# Helper for testing both operator() and operator[].  Tests completion
+# when the operator match is unique.  CLASS_NAME is the class that
+# holds the operator to test.  OPN and CLS are the open and close
+# characters ("()" or "[]").
+
+proc test_operator_unique {class_name opn cls} {
+    # Complete all prefixes between "oper" and the full prototype.
+    foreach cmd_prefix {"b" "b -function"} {
+	set location "${class_name}::operator${opn}${cls}(int)"
+	set line "$cmd_prefix $location"
+	set start [index_after "${class_name}" $line]
+	test_complete_prefix_range $line $start
+	check_bp_locations_match_list "$cmd_prefix $location" [list $location]
+
+	# Same, but with extra spaces.  Note that the original spaces in
+	# the input line are preserved after completion.
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int " \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int )"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls}" \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls}(int)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn}${cls}" \
+	    "$cmd_prefix ${class_name}::operator ${opn}${cls}(int)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn}" \
+	    "$cmd_prefix ${class_name}::operator ${opn}${cls}(int)"
+
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator${opn}${cls}" \
+		 "${class_name}::operator ${opn}${cls}" \
+		 "${class_name}::operator ${opn}${cls}(int)" \
+		 "${class_name}::operator ${opn} ${cls} ( int )"]
+	foreach linespec $location_list {
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" [list $location]
+	}
+    }
+
+    # Check that the explicit location completer manages to find the
+    # option name after -function, when the -function's argument is a
+    # C++ operator().
+    check_explicit_skips_function_argument \
+	"${class_name}::operator ${opn} ${cls} ( int )"
+}
+
+# Helper for testing both operator() and operator[].  Tests completion
+# when the operator match is ambiguous.  CLASS_NAME is the class that
+# holds the operator to test.  OPN and CLS are the open and close
+# characters ("()" or "[]").
+
+proc test_operator_ambiguous {class_name opn cls} {
+    foreach cmd_prefix {"b" "b -function"} {
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set linespec_noparams "${class_name}::operator${opn}${cls}"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator${opn}${cls}(int)" \
+		 "${class_name}::operator${opn}${cls}(long)" \
+		 "${class_name}::operator${opn}${cls}<int>(int*)"]
+	# The operator[] test can't have a "()" overload, since that
+	# wouldn't compile.
+	if {$opn == "("} {
+	    set location_list \
+		[concat \
+		     [list "${class_name}::operator${opn}${cls}()"] \
+		     $location_list]
+	}
+	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>" \
+	    [list "${class_name}::operator${opn}${cls}<int>(int*)"]
+
+	# 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*)"]
+
+	# Add extra spaces.
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( in" \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int)"
+	check_bp_locations_match_list \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int )" \
+	    [list "${class_name}::operator${opn}${cls}(int)"]
+    }
+}
+
+proc_with_prefix operator()-unique {} {
+    test_operator_unique test_unique_op_call "(" ")"
+}
+
+proc_with_prefix operator\[\]-unique {} {
+    test_operator_unique test_unique_op_array "\[" "\]"
+}
+
+proc_with_prefix operator()-ambiguous {} {
+    test_operator_ambiguous test_op_call "(" ")"
+}
+
+proc_with_prefix operator\[\]-ambiguous {} {
+    test_operator_ambiguous test_op_array "\[" "\]"
+}
+
+# Test arithmetic/logical operators.  Test completing all C++
+# arithmetic/logical operators, when all the operators are in the same
+# class.
+
+proc_with_prefix ops-valid-ambiguous {} {
+    set locations {
+	"test_ops::operator!(E)"
+	"test_ops::operator!=(E, E)"
+	"test_ops::operator%(E, E)"
+	"test_ops::operator%=(E, E)"
+	"test_ops::operator&&(E, E)"
+	"test_ops::operator&(E, E)"
+	"test_ops::operator&=(E, E)"
+	"test_ops::operator*(E, E)"
+	"test_ops::operator*=(E, E)"
+	"test_ops::operator+(E, E)"
+	"test_ops::operator++(E)"
+	"test_ops::operator++(E, int)"
+	"test_ops::operator+=(E, E)"
+	"test_ops::operator,(E, E)"
+	"test_ops::operator-(E, E)"
+	"test_ops::operator--(E)"
+	"test_ops::operator--(E, int)"
+	"test_ops::operator-=(E, E)"
+	"test_ops::operator/(E, E)"
+	"test_ops::operator/=(E, E)"
+	"test_ops::operator<(E, E)"
+	"test_ops::operator<<(E, E)"
+	"test_ops::operator<<=(E, E)"
+	"test_ops::operator<=(E, E)"
+	"test_ops::operator==(E, E)"
+	"test_ops::operator>(E, E)"
+	"test_ops::operator>=(E, E)"
+	"test_ops::operator>>(E, E)"
+	"test_ops::operator>>=(E, E)"
+	"test_ops::operator^(E, E)"
+	"test_ops::operator^=(E, E)"
+	"test_ops::operator|(E, E)"
+	"test_ops::operator|=(E, E)"
+	"test_ops::operator||(E, E)"
+	"test_ops::operator~(E)"
+    }
+    foreach linespec $locations {
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_unique \
+		"$cmd_prefix $linespec" \
+		"$cmd_prefix $linespec"
+
+	}
+
+	check_explicit_skips_function_argument "$linespec"
+    }
+
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_multiple \
+	    "$cmd_prefix " "test_ops::operator" "" $locations
+    }
+}
+
+# Test completing all C++ operators, with and without spaces.  The
+# test without spaces makes sure the completion matches exactly the
+# expected prototype.  The version with whitespace is a bit more lax
+# for simplicity.  In that case, we only make sure we get back the
+# terminating ')'.  Each operator is defined in a separate class so
+# that we can exercise unique completion matches.
+
+proc_with_prefix ops-valid-unique {} {
+    set locations {
+	"test_op_BIT_AND::operator&(E, E)"
+	"test_op_BIT_AND_A::operator&=(E, E)"
+	"test_op_BIT_O::operator|(E, E)"
+	"test_op_COMMA::operator,(E, E)"
+	"test_op_DIV::operator/(E, E)"
+	"test_op_DIV_A::operator/=(E, E)"
+	"test_op_EQ::operator==(E, E)"
+	"test_op_GT::operator>(E, E)"
+	"test_op_GTE::operator>=(E, E)"
+	"test_op_LAND::operator&&(E, E)"
+	"test_op_LOR::operator||(E, E)"
+	"test_op_LT::operator<(E, E)"
+	"test_op_LTE::operator<=(E, E)"
+	"test_op_MINUS::operator-(E, E)"
+	"test_op_MINUS_A::operator-=(E, E)"
+	"test_op_MOD::operator%(E, E)"
+	"test_op_MOD_A::operator%=(E, E)"
+	"test_op_MUL::operator*(E, E)"
+	"test_op_MUL_A::operator*=(E, E)"
+	"test_op_NEG::operator~(E)"
+	"test_op_NEQ::operator!=(E, E)"
+	"test_op_NOT::operator!(E)"
+	"test_op_OE::operator|=(E, E)"
+	"test_op_PLUS::operator+(E, E)"
+	"test_op_PLUS_A::operator+=(E, E)"
+	"test_op_POST_DEC::operator--(E, int)"
+	"test_op_POST_INC::operator++(E, int)"
+	"test_op_PRE_DEC::operator--(E)"
+	"test_op_PRE_INC::operator++(E)"
+	"test_op_SL::operator<<(E, E)"
+	"test_op_SL_A::operator<<=(E, E)"
+	"test_op_SR::operator>>(E, E)"
+	"test_op_SR_A::operator>>=(E, E)"
+	"test_op_XOR::operator^(E, E)"
+	"test_op_XOR_A::operator^=(E, E)"
+    }
+    set linespecs_ws {
+	"test_op_BIT_AND::operator & ( E , E )"
+	"test_op_BIT_AND_A::operator &= ( E , E )"
+	"test_op_BIT_O::operator | (E , E )"
+	"test_op_COMMA::operator , ( E , E )"
+	"test_op_DIV::operator / (E , E )"
+	"test_op_DIV_A::operator /= ( E , E )"
+	"test_op_EQ::operator == ( E , E )"
+	"test_op_GT::operator > ( E , E )"
+	"test_op_GTE::operator >= ( E , E )"
+	"test_op_LAND::operator && ( E , E )"
+	"test_op_LOR::operator || ( E , E )"
+	"test_op_LT::operator < ( E , E )"
+	"test_op_LTE::operator <= ( E , E )"
+	"test_op_MINUS::operator - ( E , E )"
+	"test_op_MINUS_A::operator -= ( E , E )"
+	"test_op_MOD::operator % ( E , E )"
+	"test_op_MOD_A::operator %= ( E , E )"
+	"test_op_MUL::operator * ( E , E )"
+	"test_op_MUL_A::operator *= ( E , E )"
+	"test_op_NEG::operator ~ ( E )"
+	"test_op_NEQ::operator != ( E , E )"
+	"test_op_NOT::operator ! ( E )"
+	"test_op_OE::operator |= ( E , E )"
+	"test_op_PLUS::operator + ( E , E )"
+	"test_op_PLUS_A::operator += ( E , E )"
+	"test_op_POST_DEC::operator -- ( E , int )"
+	"test_op_POST_INC::operator ++ ( E , int )"
+	"test_op_PRE_DEC::operator -- ( E )"
+	"test_op_PRE_INC::operator ++ ( E )"
+	"test_op_SL::operator << ( E , E )"
+	"test_op_SL_A::operator <<= ( E , E )"
+	"test_op_SR::operator >> ( E , E )"
+	"test_op_SR_A::operator >>= ( E , E )"
+	"test_op_XOR::operator ^ ( E , E )"
+	"test_op_XOR_A::operator ^= ( E , E )"
+    }
+    foreach linespec $locations linespec_ws $linespecs_ws {
+	foreach cmd_prefix {"b" "b -function"} {
+	    with_test_prefix "no-whitespace" {
+		set line "$cmd_prefix $linespec"
+		set start [index_after "::operato" $line]
+		test_complete_prefix_range $line $start
+	    }
+
+	    with_test_prefix "whitespace" {
+		set line_ws "$cmd_prefix $linespec_ws"
+		set start_ws [index_after "::operator " $line_ws]
+		test_complete_prefix_range_input \
+		    $line_ws "$cmd_prefix test_op_.*::operator .*\\\)" $start_ws
+	    }
+	}
+
+	check_explicit_skips_function_argument "$linespec"
+	check_explicit_skips_function_argument "$linespec_ws"
+    }
+}
+
+# Test completing an invalid (whitespace at the wrong place) operator
+# name.
+
+proc_with_prefix ops-invalid {} {
+    foreach linespec {
+	"test_op_BIT_AND_A::operator& =(E, E)"
+	"test_op_DIV_A::operator/ =(E, E)"
+	"test_op_EQ::operator= =(E, E)"
+	"test_op_GTE::operator> =(E, E)"
+	"test_op_LAND::operator& &(E, E)"
+	"test_op_LOR::operator| |(E, E)"
+	"test_op_LTE::operator< =(E, E)"
+	"test_op_MINUS_A::operator- =(E, E)"
+	"test_op_MOD_A::operator% =(E, E)"
+	"test_op_MUL_A::operator* =(E, E)"
+	"test_op_NEQ::operator! =(E, E)"
+	"test_op_OE::operator| =(E, E)"
+	"test_op_PLUS_A::operator+ =(E, E)"
+	"test_op_POST_DEC::operator- -(E, int)"
+	"test_op_POST_INC::operator+ +(E, int)"
+	"test_op_PRE_DEC::operator- -(E)"
+	"test_op_PRE_INC::operator+ +(E)"
+	"test_op_SL::operator< <(E, E)"
+	"test_op_SL_A::operator< < =(E, E)"
+	"test_op_SR::operator> >(E, E)"
+	"test_op_SR_A::operator> > =(E, E)"
+	"test_op_XOR_A::operator^ =(E, E)"
+    } {
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_tab_none "$cmd_prefix $linespec"
+	    check_setting_bp_fails "$cmd_prefix $linespec"
+	}
+    }
+}
+
+# Test completing function/method FUNCTION.  Completion is tested at
+# every point starting after START_AFTER.  FUNCTION_WS is a version of
+# FUNCTION with extra (but valid) whitespace.  FUNCTION_INVALID is a
+# version of FUNCTION with invalid whitespace.  Tests that completion
+# of FUNCTION_WS completes to self, and that a completion of
+# FUNCTION_INVALID fails.
+
+proc test_function {function start_after function_ws {function_invalid ""}} {
+    foreach cmd_prefix {"b" "b -function"} {
+	set line "$cmd_prefix $function"
+	set start [index_after $start_after $line]
+	test_complete_prefix_range $line $start
+    }
+
+    check_explicit_skips_function_argument $function
+    check_explicit_skips_function_argument $function_ws
+
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_unique \
+	    "$cmd_prefix $function_ws" \
+	    "$cmd_prefix $function_ws"
+	if {$function_invalid != ""} {
+	    test_gdb_complete_tab_none "$cmd_prefix $function_invalid"
+	    check_setting_bp_fails "$cmd_prefix $function_invalid"
+	}
+    }
+}
+
+# Test completing a user-defined conversion operator.
+
+proc_with_prefix conversion-operator {} {
+    test_function \
+	"test_op_conversion::operator test_op_conversion_res const volatile**() const volatile" \
+	"test_op_conversio" \
+	"test_op_conversion::operator test_op_conversion_res const volatile * * ( ) const volatile"}
+
+# Test completing an assignment operator.
+
+proc_with_prefix assignment-operator {} {
+    test_function \
+	"test_op_assign::operator=(test_op_assign const&)" \
+	"test_op_assig" \
+	"test_op_assign::operator = ( test_op_assign const & )" \
+}
+
+# Test completing an arrow operator.
+
+proc_with_prefix arrow-operator {} {
+    test_function \
+	"test_op_arrow::operator->()" \
+	"test_op_arro" \
+	"test_op_arrow::operator -> ( )" \
+	"test_op_arrow::operator - > ( )"
+}
+
+# The testcase driver.  Calls all test procedures.
+
+proc test_driver {} {
+    operator-delete
+    operator-delete\[\]
+    operator-new
+    operator-new\[\]
+    operator()-unique
+    operator()-ambiguous
+    operator\[\]-unique
+    operator\[\]-ambiguous
+    ops-valid-ambiguous
+    ops-valid-unique
+    ops-invalid
+    conversion-operator
+    assignment-operator
+    arrow-operator
+}
+
+test_driver
-- 
2.5.5

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

* [PATCH 37/40] Fix completing an empty string
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (7 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-09 18:01   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer Pedro Alves
                   ` (31 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

While working on this series, I managed to break

 (gdb) [TAB]

locally, and make GDB crash in the big completer rework, but only
notice a few weeks down the road, because we have no test for that...

I also noticed that:

 (gdb)     [TAB]

didn't work (didn't show all commands as matches), even though
entering a command with leading whitespace works:

 (gdb)     help

This commit fixes the latter and adds a testcase that covers both issues.

The gdb.base/completion.exp change is necessary because the new test has a
file name that also starts with "gdb.base/complet", making that particular
test ambiguous.  Adding another letter disambiguates.

gdb/testsuite/ChangeLog:
yyyy-mm-dd   Pedro Alves  <palves@redhat.com>

	* completer.c (complete_line_internal_1): Skip spaces until the
	start of the command.
	* gdb.base/complete-empty.exp: New file.
	* gdb.base/completion.exp: Adjust.
---
 gdb/completer.c                           |  9 ++++--
 gdb/testsuite/gdb.base/complete-empty.exp | 46 +++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.base/completion.exp     |  6 ++--
 3 files changed, 55 insertions(+), 6 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/complete-empty.exp

diff --git a/gdb/completer.c b/gdb/completer.c
index 99e40a3..74269ad 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1286,10 +1286,13 @@ complete_line_internal_1 (completion_tracker &tracker,
       word = tmp_command + point - strlen (text);
     }
 
-  if (point == 0)
+  /* Move P up to the start of the command.  */
+  p = skip_spaces_const (p);
+
+  if (*p == '\0')
     {
-      /* An empty line we want to consider ambiguous; that is, it
-	 could be any command.  */
+      /* An empty line is ambiguous; that is, it could be any
+	 command.  */
       c = CMD_LIST_AMBIGUOUS;
       result_list = 0;
     }
diff --git a/gdb/testsuite/gdb.base/complete-empty.exp b/gdb/testsuite/gdb.base/complete-empty.exp
new file mode 100644
index 0000000..79f9629
--- /dev/null
+++ b/gdb/testsuite/gdb.base/complete-empty.exp
@@ -0,0 +1,46 @@
+# Copyright 2017 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/>.
+
+# This file is part of the gdb testsuite.
+
+load_lib completion-support.exp
+
+gdb_exit
+gdb_start
+
+set timeout 5
+
+# Start of tests.
+
+# Test TAB with no input.
+proc_with_prefix empty-input-line {} {
+    # Set max-completions to 1 to avoid having to hardcode a set of
+    # command names.
+    gdb_test_no_output "set max-completions 1"
+
+    # Given the completion limit, this completes to the command with
+    # the lowest alphanumeric sort, which is, and is likely to remain,
+    # "!".
+    test_gdb_complete_unique "" "!" " " 1
+
+    # Same, but with some leading whitespace.
+    test_gdb_complete_unique "   " "   !" " " 1
+}
+
+proc test_driver {} {
+    empty-input-line
+}
+
+test_driver
diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
index f03bfc3..a9a8e4f 100644
--- a/gdb/testsuite/gdb.base/completion.exp
+++ b/gdb/testsuite/gdb.base/completion.exp
@@ -722,13 +722,13 @@ gdb_test "complete file ./gdb.base/compl" \
     "file ./gdb.base/completion\\.exp.*" \
     "complete-command 'file ./gdb.base/compl'"
 
-set test "complete 'file ./gdb.base/complet'"
-send_gdb "file ./gdb.base/complet\t"
+set test "complete 'file ./gdb.base/completi'"
+send_gdb "file ./gdb.base/completi\t"
 gdb_test_multiple "" "$test" {
     -re "^file ./gdb.base/completion\\.exp $" {
 	send_gdb "\n"
 	# Ignore the exact error message.
-	gdb_test_multiple "" "complete 'file ./gdb.base/complet'" {
+	gdb_test_multiple "" "complete 'file ./gdb.base/completi'" {
 	    -re "\r\nA program is being debugged already\\.\[\r\n\]+Are you sure you want to change the file\\? \\(y or n\\) $" {
 		send_gdb "n\n"
 		exp_continue
-- 
2.5.5

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

* [PATCH 34/40] Make strcmp_iw NOT ignore whitespace in the middle of tokens
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (9 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-09 15:48   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 10/40] Clean up "completer_handle_brkchars" callback handling Pedro Alves
                   ` (29 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

currently "b func tion" manages to set a breakpoint at "function" !

All this years I had never noticed this, but now that the linespec
completer actually works, this easily happens by accident, with:

  "b func t<tab>"

expecting to get "thread", but getting instead:

  "b func tion"

...

Also, this:

  "b rettypefunc<int>"

manages to set a breakpoint on "rettype func<int>()".

These things happen due to strcmp_iw "magic".

Fix it by teaching strcmp_iw about when can it skip whitespace.  This
required handling user-defined operators, and scope operators,
complicating the code a bit, unfortunately.  I added unit tests for
all the corner cases I stumbled on, as I was developing this, and then
in the end wrote a testsuite testcase covering many of the same things
and more (later in the series).

The operator_stoken changes are necessary due to a latent bug --
currently "operator char" becomes "operatorchar", and later look ups
only find it because strcmp_iw ignores the whitespace...

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* c-exp.y (oper): Add space to operator names.
	* cp-support.c (cp_symbol_name_matches_1)
	(cp_fq_symbol_name_matches): Pass language to
	strncmp_iw_with_mode.
	(test_cp_symbol_name_cmp): Add unit tests.
	* language.c (default_symbol_name_matcher): Pass language to
	strncmp_iw_with_mode.
	* utils.c: Include "cp-support.h" and <algorithm>.
	(valid_identifier_name_char, cp_skip_operator_token, skip_ws)
	(cp_is_operator): New functions.
	(strncmp_iw_with_mode): Use them.  Add language parameter.  Don't
	skip whitespace in the symbol name when the lookup name doesn't
	have spaces, and vice versa.
	(strncmp_iw, strcmp_iw): Pass language to strncmp_iw_with_mode.
	* utils.h (strncmp_iw_with_mode): Add language parameter.
---
 gdb/c-exp.y      |   5 +-
 gdb/cp-support.c |  65 +++++++++++++++-
 gdb/language.c   |   2 +-
 gdb/utils.c      | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 gdb/utils.h      |   3 +-
 5 files changed, 289 insertions(+), 13 deletions(-)

diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index 24a2fbd..0a182cc 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -1487,7 +1487,7 @@ oper:	OPERATOR NEW
 	|	OPERATOR '>'
 			{ $$ = operator_stoken (">"); }
 	|	OPERATOR ASSIGN_MODIFY
-			{ const char *op = "unknown";
+			{ const char *op = " unknown";
 			  switch ($2)
 			    {
 			    case BINOP_RSH:
@@ -1563,7 +1563,8 @@ oper:	OPERATOR NEW
 
 			  c_print_type ($2, NULL, &buf, -1, 0,
 					&type_print_raw_options);
-			  $$ = operator_stoken (buf.c_str ());
+			  std::string name = " " + buf.string ();
+			  $$ = operator_stoken (name.c_str ());
 			}
 	;
 
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 84d8a6b..4c353c5 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1709,7 +1709,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) == 0)
+				mode, language_cplus) == 0)
 	{
 	  if (comp_match_res != NULL)
 	    {
@@ -1747,7 +1747,7 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
 
   if (strncmp_iw_with_mode (symbol_search_name,
 			    name.c_str (), name.size (),
-			    mode) == 0)
+			    mode, language_cplus) == 0)
     {
       if (comp_match_res != NULL)
 	{
@@ -1857,6 +1857,67 @@ test_cp_symbol_name_cmp ()
   CHECK_MATCH_C ("function(int)", "function(int)");
   CHECK_NOT_MATCH_C ("function(int)", "function()");
 
+  /* Check that whitespace within symbol names is not ignored.  */
+  CHECK_NOT_MATCH_C ("function", "func tion");
+  CHECK_NOT_MATCH_C ("func__tion", "func_ _tion");
+  CHECK_NOT_MATCH_C ("func11tion", "func1 1tion");
+
+  /* Check the converse, which can happen with template function,
+     where the return type is part of the demangled name.  */
+  CHECK_NOT_MATCH_C ("func tion", "function");
+  CHECK_NOT_MATCH_C ("func1 1tion", "func11tion");
+  CHECK_NOT_MATCH_C ("func_ _tion", "func__tion");
+
+  /* Within parameters too.  */
+  CHECK_NOT_MATCH_C ("func(param)", "func(par am)");
+
+  /* Check handling of whitespace around C++ operators.  */
+  CHECK_NOT_MATCH_C ("operator<<", "opera tor<<");
+  CHECK_NOT_MATCH_C ("operator<<", "operator< <");
+  CHECK_NOT_MATCH_C ("operator<<", "operator < <");
+  CHECK_NOT_MATCH_C ("operator==", "operator= =");
+  CHECK_NOT_MATCH_C ("operator==", "operator = =");
+  CHECK_MATCH_C ("operator<<", "operator <<");
+  CHECK_MATCH_C ("operator<<()", "operator <<");
+  CHECK_NOT_MATCH_C ("operator<<()", "operator<<(int)");
+  CHECK_NOT_MATCH_C ("operator<<(int)", "operator<<()");
+  CHECK_MATCH_C ("operator==", "operator ==");
+  CHECK_MATCH_C ("operator==()", "operator ==");
+  CHECK_MATCH_C ("operator <<", "operator<<");
+  CHECK_MATCH_C ("operator ==", "operator==");
+  CHECK_MATCH_C ("operator bool", "operator  bool");
+  CHECK_MATCH_C ("operator bool ()", "operator  bool");
+  CHECK_MATCH_C ("operatorX<<", "operatorX < <");
+  CHECK_MATCH_C ("Xoperator<<", "Xoperator < <");
+
+  CHECK_MATCH_C ("operator()(int)", "operator()(int)");
+  CHECK_MATCH_C ("operator()(int)", "operator ( ) ( int )");
+  CHECK_MATCH_C ("operator()<long>(int)", "operator ( ) < long > ( int )");
+  /* The first "()" is not the parameter list.  */
+  CHECK_NOT_MATCH ("operator()(int)", "operator");
+
+  /* Misc user-defined operator tests.  */
+
+  CHECK_NOT_MATCH_C ("operator/=()", "operator ^=");
+  /* Same length at end of input.  */
+  CHECK_NOT_MATCH_C ("operator>>", "operator[]");
+  /* Same length but not at end of input.  */
+  CHECK_NOT_MATCH_C ("operator>>()", "operator[]()");
+
+  CHECK_MATCH_C ("base::operator char*()", "base::operator char*()");
+  CHECK_MATCH_C ("base::operator char*()", "base::operator char * ()");
+  CHECK_MATCH_C ("base::operator char**()", "base::operator char * * ()");
+  CHECK_MATCH ("base::operator char**()", "base::operator char * *");
+  CHECK_MATCH_C ("base::operator*()", "base::operator*()");
+  CHECK_NOT_MATCH_C ("base::operator char*()", "base::operatorc");
+  CHECK_NOT_MATCH ("base::operator char*()", "base::operator char");
+  CHECK_NOT_MATCH ("base::operator char*()", "base::operat");
+
+  /* Check handling of whitespace around C++ scope operators.  */
+  CHECK_NOT_MATCH_C ("foo::bar", "foo: :bar");
+  CHECK_MATCH_C ("foo::bar", "foo :: bar");
+  CHECK_MATCH_C ("foo :: bar", "foo::bar");
+
   /* Tests matching symbols in some scope.  */
   CHECK_MATCH_C ("foo::function()", "function");
   CHECK_MATCH_C ("foo::function(int)", "function");
diff --git a/gdb/language.c b/gdb/language.c
index 511a86f..377d748 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -723,7 +723,7 @@ default_symbol_name_matcher (const char *symbol_search_name,
 			  : strncmp_iw_mode::MATCH_PARAMS);
 
   if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
-			    mode) == 0)
+			    mode, language_minimal) == 0)
     {
       if (comp_match_res != NULL)
 	{
diff --git a/gdb/utils.c b/gdb/utils.c
index 9798edc..484c1ef 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -65,6 +65,8 @@
 #include "gdb_usleep.h"
 #include "interps.h"
 #include "gdb_regex.h"
+#include "cp-support.h"
+#include <algorithm>
 
 #if !HAVE_DECL_MALLOC
 extern PTR malloc ();		/* ARI: PTR */
@@ -2418,22 +2420,227 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
     }
 }
 
+/* True if CH is a character that can be part of a symbol name.  I.e.,
+   either a number, a letter, or a '_'.  */
+
+static bool
+valid_identifier_name_char (int ch)
+{
+  return (isalnum (ch) || ch == '_');
+}
+
+/* Skip to end of token, or to END, whatever comes first.  */
+
+static const char *
+cp_skip_operator_token (const char *token, const char *end)
+{
+  const char *p = token;
+  while (p != end && !isspace (*p) && *p != '(')
+    {
+      if (valid_identifier_name_char (*p))
+	{
+	  while (p != end && valid_identifier_name_char (*p))
+	    p++;
+	  return p;
+	}
+      else
+	{
+	  /* Note, ordered such that among ops that share a prefix,
+	     longer comes first.  This is so that the loop below can
+	     bail on first match.  */
+	  static const char *ops[] =
+	    {
+	      "[",
+	      "]",
+	      "~",
+	      ",",
+	      "-=", "--", "->", "-",
+	      "+=", "++", "+",
+	      "*=", "*",
+	      "/=", "/",
+	      "%=", "%",
+	      "|=", "||", "|",
+	      "&=", "&&", "&",
+	      "^=", "^",
+	      "!=", "!",
+	      "<<=", "<=", "<<", "<",
+	      ">>=", ">=", ">>", ">",
+	      "==", "=",
+	    };
+
+	  for (const char *op : ops)
+	    {
+	      size_t oplen = strlen (op);
+	      size_t lencmp = std::min<size_t> (oplen, end - p);
+
+	      if (strncmp (p, op, lencmp) == 0)
+		return p + lencmp;
+	    }
+	  /* Some unidentified character.  Return it.  */
+	  return p + 1;
+	}
+    }
+
+  return p;
+}
+
+/* Advance string1/string2 past whitespace.  */
+
+static void
+skip_ws (const char *&string1, const char *&string2, const char *end_str2)
+{
+  while (isspace (*string1))
+    string1++;
+  while (string2 < end_str2 && isspace (*string2))
+    string2++;
+}
+
+static bool
+cp_is_operator (const char *string, const char *start)
+{
+  return ((string == start
+	   || !valid_identifier_name_char (string[-1]))
+	  && strncmp (string, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+	  && !valid_identifier_name_char (string[CP_OPERATOR_LEN]));
+}
+
 /* See utils.h.  */
 
 int
 strncmp_iw_with_mode (const char *string1, const char *string2,
-		      size_t string2_len, strncmp_iw_mode mode)
+		      size_t string2_len, strncmp_iw_mode mode,
+		      enum language language)
 {
+  const char *string1_start = string1;
   const char *end_str2 = string2 + string2_len;
+  bool skip_spaces = true;
+  bool have_colon_op = (language == language_cplus
+			|| language == language_rust
+			|| language == language_fortran);
 
   while (1)
     {
-      while (isspace (*string1))
-	string1++;
-      while (string2 < end_str2 && isspace (*string2))
-	string2++;
+      if (skip_spaces
+	  || ((isspace (*string1) && !valid_identifier_name_char (*string2))
+	      || (isspace (*string2) && !valid_identifier_name_char (*string1))))
+	{
+	  skip_ws (string1, string2, end_str2);
+	  skip_spaces = false;
+	}
+
       if (*string1 == '\0' || string2 == end_str2)
 	break;
+
+      /* Handle the :: operator.  */
+      if (have_colon_op && string1[0] == ':' && string1[1] == ':')
+	{
+	  if (*string2 != ':')
+	    return 1;
+
+	  string1++;
+	  string2++;
+
+	  if (string2 == end_str2)
+	    break;
+
+	  if (*string2 != ':')
+	    return 1;
+
+	  string1++;
+	  string2++;
+
+	  while (isspace (*string1))
+	    string1++;
+	  while (string2 < end_str2 && isspace (*string2))
+	    string2++;
+	  continue;
+	}
+
+      /* Handle C++ user-defined operators.  */
+      else if (language == language_cplus
+	       && *string1 == 'o')
+	{
+	  if (cp_is_operator (string1, string1_start))
+	    {
+	      /* An operator name in STRING1.  Check STRING2.  */
+	      size_t cmplen = std::min<size_t> (CP_OPERATOR_LEN, end_str2 - string2);
+	      if (strncmp (string1, string2, cmplen) != 0)
+		return 1;
+
+	      string1 += cmplen;
+	      string2 += cmplen;
+
+	      if (string2 != end_str2)
+		{
+		  /* Check for "operatorX" in STRING2.  */
+		  if (valid_identifier_name_char (*string2))
+		    return 1;
+
+		  skip_ws (string1, string2, end_str2);
+		}
+
+	      /* Handle operator().  */
+	      if (*string1 == '(')
+		{
+		  if (string2 == end_str2)
+		    {
+		      if (mode == strncmp_iw_mode::NORMAL)
+			return 0;
+		      else
+			{
+			  /* Don't break for the regular return at the
+			     bottom, because "operator" should not
+			     match "operator()", since this open
+			     parentheses is not the parameter list
+			     start.  */
+			  return *string1 != '\0';
+			}
+		    }
+
+		  if (*string1 != *string2)
+		    return 1;
+
+		  string1++;
+		  string2++;
+		}
+
+	      while (1)
+		{
+		  skip_ws (string1, string2, end_str2);
+
+		  /* Skip to end of token, or to END, whatever comes
+		     first.  */
+		  const char *end_str1 = string1 + strlen (string1);
+		  const char *p1 = cp_skip_operator_token (string1, end_str1);
+		  const char *p2 = cp_skip_operator_token (string2, end_str2);
+
+		  cmplen = std::min (p1 - string1, p2 - string2);
+		  if (p2 == end_str2)
+		    {
+		      if (strncmp (string1, string2, cmplen) != 0)
+			return 1;
+		    }
+		  else
+		    {
+		      if (p1 - string1 != p2 - string2)
+			return 1;
+		      if (strncmp (string1, string2, cmplen) != 0)
+			return 1;
+		    }
+
+		  string1 += cmplen;
+		  string2 += cmplen;
+
+		  if (*string1 == '\0' || string2 == end_str2)
+		    break;
+		  if (*string1 == '(' || *string2 == '(')
+		    break;
+		}
+
+	      continue;
+	    }
+	}
+
       if (case_sensitivity == case_sensitive_on && *string1 != *string2)
 	break;
       if (case_sensitivity == case_sensitive_off
@@ -2441,6 +2648,12 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
 	      != tolower ((unsigned char) *string2)))
 	break;
 
+      /* If we see any non-whitespace, non-identifier-name character
+	 (any of "()<>*&" etc.), then skip spaces the next time
+	 around.  */
+      if (!isspace (*string1) && !valid_identifier_name_char (*string1))
+	skip_spaces = true;
+
       string1++;
       string2++;
     }
@@ -2462,7 +2675,7 @@ int
 strncmp_iw (const char *string1, const char *string2, size_t string2_len)
 {
   return strncmp_iw_with_mode (string1, string2, string2_len,
-			       strncmp_iw_mode::NORMAL);
+			       strncmp_iw_mode::NORMAL, language_minimal);
 }
 
 /* See utils.h.  */
@@ -2471,7 +2684,7 @@ int
 strcmp_iw (const char *string1, const char *string2)
 {
   return strncmp_iw_with_mode (string1, string2, strlen (string2),
-			       strncmp_iw_mode::MATCH_PARAMS);
+			       strncmp_iw_mode::MATCH_PARAMS, language_minimal);
 }
 
 /* This is like strcmp except that it ignores whitespace and treats
diff --git a/gdb/utils.h b/gdb/utils.h
index 9e531e0..4ce263e 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -56,7 +56,8 @@ enum class strncmp_iw_mode
 extern int strncmp_iw_with_mode (const char *string1,
 				 const char *string2,
 				 size_t string2_len,
-				 strncmp_iw_mode mode);
+				 strncmp_iw_mode mode,
+				 enum language language);
 
 /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
    differences in whitespace.  STRING2_LEN is STRING2's length.
-- 
2.5.5

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

* [PATCH 10/40] Clean up "completer_handle_brkchars" callback handling
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (10 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 34/40] Make strcmp_iw NOT ignore whitespace in the middle of tokens Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-07-13 21:08   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 13/40] Introduce strncmp_iw Pedro Alves
                   ` (28 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

This patch cleans up "completer_handle_brkchars" callback handling:

- Renames the function typedef to better match its intent:
  completer_ftype_void ->  completer_handle_brkchars_ftype

- Factors out common code in complete_line_internal handling the
  "handle_brkchars" callback to a separate function.

- Centralizes all the "completer method" to "handle_brkchars method"
  mapping in a single function.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-decode.c (set_cmd_completer_handle_brkchars): Adjust to
	renames.
	* cli/cli-decode.h (struct cmd_list_element) <completer>: Move
	comments to completer_ftype's declaration.
	<completer_handle_brkchars>: Change type to
	completer_handle_brkchars_ftype.
	* command.h (completer_ftype): Add describing comment and give
	names to parameters.
	(completer_ftype_void): Rename to ...
	(completer_handle_brkchars_ftype) ... this.  Add describing comment.
	(set_cmd_completer_handle_brkchars): Adjust.
	* completer.c (filename_completer_handle_brkchars): New function.
	(complete_line_internal_normal_command): New function, factored
	out from ...
	(complete_line_internal): ... here.
	(command_completer_handle_brkchars)
	(default_completer_handle_brkchars)
	(completer_handle_brkchars_func_for_completer): New functions.
	* completer.h (set_gdb_completion_word_break_characters): Delete
	declaration.
	(completer_handle_brkchars_func_for_completer): New declaration.
	* python/py-cmd.c (cmdpy_completer_handle_brkchars): Adjust to use
	completer_handle_brkchars_func_for_completer.
---
 gdb/cli/cli-decode.c |   4 +-
 gdb/cli/cli-decode.h |  17 +------
 gdb/command.h        |  18 +++++--
 gdb/completer.c      | 139 ++++++++++++++++++++++++++++++++++++---------------
 gdb/completer.h      |  10 ++--
 gdb/python/py-cmd.c  |   7 ++-
 6 files changed, 127 insertions(+), 68 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index f982028..f163581 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -166,9 +166,9 @@ set_cmd_completer (struct cmd_list_element *cmd, completer_ftype *completer)
 
 void
 set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
-			       completer_ftype_void *completer_handle_brkchars)
+				   completer_handle_brkchars_ftype *func)
 {
-  cmd->completer_handle_brkchars = completer_handle_brkchars;
+  cmd->completer_handle_brkchars = func;
 }
 
 /* Add element named NAME.
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 66159fd..97dc5d4 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -161,19 +161,7 @@ struct cmd_list_element
     /* The prefix command of this command.  */
     struct cmd_list_element *prefix;
 
-    /* Completion routine for this command.  TEXT is the text beyond
-       what was matched for the command itself (leading whitespace is
-       skipped).  It stops where we are supposed to stop completing
-       (rl_point) and is '\0' terminated.
-
-       Return value is a malloc'd vector of pointers to possible
-       completions terminated with NULL.  If there are no completions,
-       returning a pointer to a NULL would work but returning NULL
-       itself is also valid.  WORD points in the same buffer as TEXT,
-       and completions should be returned relative to this position.
-       For example, suppose TEXT is "foo" and we want to complete to
-       "foobar".  If WORD is "oo", return "oobar"; if WORD is
-       "baz/foo", return "baz/foobar".  */
+    /* Completion routine for this command.  */
     completer_ftype *completer;
 
     /* Handle the word break characters for this completer.  Usually
@@ -182,8 +170,7 @@ struct cmd_list_element
        a class) the word break chars may need to be redefined
        depending on the completer type (e.g., for filename
        completers).  */
-
-    completer_ftype_void *completer_handle_brkchars;
+    completer_handle_brkchars_ftype *completer_handle_brkchars;
 
     /* Destruction routine for this command.  If non-NULL, this is
        called when this command instance is destroyed.  This may be
diff --git a/gdb/command.h b/gdb/command.h
index 4a56a51..2a190d4 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -174,18 +174,28 @@ typedef void cmd_sfunc_ftype (char *args, int from_tty,
 extern void set_cmd_sfunc (struct cmd_list_element *cmd,
 			   cmd_sfunc_ftype *sfunc);
 
+/* A completion routine.  Return a list of possible completions.
+
+   TEXT is the text beyond what was matched for the command itself
+   (leading whitespace is skipped).  It stops where we are supposed to
+   stop completing (rl_point) and is '\0' terminated.  WORD points in
+   the same buffer as TEXT, and completions should be returned
+   relative to this position.  For example, suppose TEXT is "foo" and
+   we want to complete to "foobar".  If WORD is "oo", return "oobar";
+   if WORD is "baz/foo", return "baz/foobar".  */
 typedef VEC (char_ptr) *completer_ftype (struct cmd_list_element *,
-					 const char *, const char *);
+					 const char *text, const char *word);
 
-typedef void completer_ftype_void (struct cmd_list_element *,
-				   const char *, const char *);
+/* Same, but for set_cmd_completer_handle_brkchars.  */
+typedef void completer_handle_brkchars_ftype (struct cmd_list_element *,
+					      const char *text, const char *word);
 
 extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
 
 /* Set the completer_handle_brkchars callback.  */
 
 extern void set_cmd_completer_handle_brkchars (struct cmd_list_element *,
-					       completer_ftype_void *);
+					       completer_handle_brkchars_ftype *);
 
 /* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
    around in cmd objects to test the value of the commands sfunc().  */
diff --git a/gdb/completer.c b/gdb/completer.c
index 4ab2388..fe69faa 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -132,6 +132,7 @@ noop_completer (struct cmd_list_element *ignore,
 }
 
 /* Complete on filenames.  */
+
 VEC (char_ptr) *
 filename_completer (struct cmd_list_element *ignore, 
 		    const char *text, const char *word)
@@ -192,6 +193,17 @@ filename_completer (struct cmd_list_element *ignore,
   return return_val;
 }
 
+/* The corresponding completer_handle_brkchars
+   implementation.  */
+
+static void
+filename_completer_handle_brkchars (struct cmd_list_element *ignore,
+				    const char *text, const char *word)
+{
+  set_rl_completer_word_break_characters
+    (gdb_completer_file_name_break_characters);
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -711,6 +723,51 @@ typedef enum
 }
 complete_line_internal_reason;
 
+/* Helper for complete_line_internal to simplify it.  */
+
+static VEC (char_ptr) *
+complete_line_internal_normal_command (const char *command, const char *word,
+				       const char *cmd_args,
+				       complete_line_internal_reason reason,
+				       struct cmd_list_element *c)
+{
+  const char *p = cmd_args;
+
+  if (c->completer == filename_completer)
+    {
+      /* Many commands which want to complete on file names accept
+	 several file names, as in "run foo bar >>baz".  So we don't
+	 want to complete the entire text after the command, just the
+	 last word.  To this end, we need to find the beginning of the
+	 file name by starting at `word' and going backwards.  */
+      for (p = word;
+	   p > command
+	     && strchr (gdb_completer_file_name_break_characters,
+			p[-1]) == NULL;
+	   p--)
+	;
+    }
+
+  if (reason == handle_brkchars)
+    {
+      completer_handle_brkchars_ftype *brkchars_fn;
+
+      if (c->completer_handle_brkchars != NULL)
+	brkchars_fn = c->completer_handle_brkchars;
+      else
+	{
+	  brkchars_fn
+	    = (completer_handle_brkchars_func_for_completer
+	       (c->completer));
+	}
+
+      brkchars_fn (c, p, word);
+    }
+
+  if (reason != handle_brkchars && c->completer != NULL)
+    return (*c->completer) (c, p, word);
+  return NULL;
+}
 
 /* Internal function used to handle completions.
 
@@ -880,29 +937,9 @@ complete_line_internal (const char *text,
 		{
 		  /* It is a normal command; what comes after it is
 		     completed by the command's completer function.  */
-		  if (c->completer == filename_completer)
-		    {
-		      /* Many commands which want to complete on
-			 file names accept several file names, as
-			 in "run foo bar >>baz".  So we don't want
-			 to complete the entire text after the
-			 command, just the last word.  To this
-			 end, we need to find the beginning of the
-			 file name by starting at `word' and going
-			 backwards.  */
-		      for (p = word;
-			   p > tmp_command
-			     && strchr (gdb_completer_file_name_break_characters, p[-1]) == NULL;
-			   p--)
-			;
-		      set_rl_completer_word_break_characters
-			(gdb_completer_file_name_break_characters);
-		    }
-		  if (reason == handle_brkchars
-		      && c->completer_handle_brkchars != NULL)
-		    (*c->completer_handle_brkchars) (c, p, word);
-		  if (reason != handle_brkchars && c->completer != NULL)
-		    list = (*c->completer) (c, p, word);
+		  list = complete_line_internal_normal_command (tmp_command,
+								word, p,
+								reason, c);
 		}
 	    }
 	  else
@@ -953,24 +990,9 @@ complete_line_internal (const char *text,
 	  else
 	    {
 	      /* It is a normal command.  */
-	      if (c->completer == filename_completer)
-		{
-		  /* See the commentary above about the specifics
-		     of file-name completion.  */
-		  for (p = word;
-		       p > tmp_command
-			 && strchr (gdb_completer_file_name_break_characters, 
-				    p[-1]) == NULL;
-		       p--)
-		    ;
-		  set_rl_completer_word_break_characters
-		    (gdb_completer_file_name_break_characters);
-		}
-	      if (reason == handle_brkchars
-		  && c->completer_handle_brkchars != NULL)
-		(*c->completer_handle_brkchars) (c, p, word);
-	      if (reason != handle_brkchars && c->completer != NULL)
-		list = (*c->completer) (c, p, word);
+	      list = complete_line_internal_normal_command (tmp_command,
+							    word, p,
+							    reason, c);
 	    }
 	}
     }
@@ -1124,6 +1146,7 @@ complete_line (const char *text, const char *line_buffer, int point)
 }
 
 /* Complete on command names.  Used by "help".  */
+
 VEC (char_ptr) *
 command_completer (struct cmd_list_element *ignore, 
 		   const char *text, const char *word)
@@ -1132,6 +1155,16 @@ command_completer (struct cmd_list_element *ignore,
 				 strlen (text), handle_help);
 }
 
+/* The corresponding completer_handle_brkchars implementation.  */
+
+static void
+command_completer_handle_brkchars (struct cmd_list_element *ignore,
+				   const char *text, const char *word)
+{
+  set_rl_completer_word_break_characters
+    (gdb_completer_command_word_break_characters);
+}
+
 /* Complete on signals.  */
 
 VEC (char_ptr) *
@@ -1240,6 +1273,30 @@ reggroup_completer (struct cmd_list_element *ignore,
 				   complete_reggroup_names);
 }
 
+/* The default completer_handle_brkchars implementation.  */
+
+static void
+default_completer_handle_brkchars (struct cmd_list_element *ignore,
+				   const char *text, const char *word)
+{
+  set_rl_completer_word_break_characters
+    (current_language->la_word_break_characters ());
+}
+
+/* See definition in completer.h.  */
+
+completer_handle_brkchars_ftype *
+completer_handle_brkchars_func_for_completer (completer_ftype *fn)
+{
+  if (fn == filename_completer)
+    return filename_completer_handle_brkchars;
+
+  if (fn == command_completer)
+    return command_completer_handle_brkchars;
+
+  return default_completer_handle_brkchars;
+}
+
 /* Get the list of chars that are considered as word breaks
    for the current command.  */
 
diff --git a/gdb/completer.h b/gdb/completer.h
index 0a40eaa..463a53d 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -112,12 +112,14 @@ extern char *gdb_completion_word_break_characters (void);
    not "const char *".  */
 extern void set_rl_completer_word_break_characters (const char *break_chars);
 
-/* Set the word break characters array to the corresponding set of
-   chars, based on FN.  This function is useful for cases when the
-   completer doesn't know the type of the completion until some
+/* Get the matching completer_handle_brkchars_ftype function for FN.
+   FN is one of the core completer functions above (filename,
+   location, symbol, etc.).  This function is useful for cases when
+   the completer doesn't know the type of the completion until some
    calculation is done (e.g., for Python functions).  */
 
-extern void set_gdb_completion_word_break_characters (completer_ftype *fn);
+extern completer_handle_brkchars_ftype *
+  completer_handle_brkchars_func_for_completer (completer_ftype *fn);
 
 /* Exported to linespec.c */
 
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 72ea577..b9a866e 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -293,11 +293,14 @@ cmdpy_completer_handle_brkchars (struct cmd_list_element *command,
 	}
       else if (value >= 0 && value < (long) N_COMPLETERS)
 	{
+	  completer_handle_brkchars_ftype *brkchars_fn;
+
 	  /* This is the core of this function.  Depending on which
 	     completer type the Python function returns, we have to
 	     adjust the break characters accordingly.  */
-	  set_gdb_completion_word_break_characters
-	    (completers[value].completer);
+	  brkchars_fn = (completer_handle_brkchars_func_for_completer
+			 (completers[value].completer));
+	  brkchars_fn (command, text, word);
 	}
     }
 }
-- 
2.5.5

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

* [PATCH 35/40] Comprehensive C++ linespec/completer tests
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (12 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 13/40] Introduce strncmp_iw Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-08-09 17:30   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 19/40] Fix cp_find_first_component_aux bug Pedro Alves
                   ` (26 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

Exercises all sorts of aspects fixed by the previous patches.

 - Exercises label completion, linespecs and explicit locations.

 - Exercises both quoting vs non-quoting, source filenames, function
   names, labels, with both linespecs and explicit locations.

 - Tests corner cases around not-quoting function names, and
   whitespace and/and completing inside a parameter or template
   argument list, anonymous namespace awareness, etc.

   E.g.,

     "break foo<[TAB]"          -> "break foo<int>()"
     "break bar ( int[TAB]"     -> "break bar ( int)
     "break ( anon"             -> "break ( anonymous namespace)::func()"
     "b cfunc() [tab]"          -> "b cfunc() const"
     "b rettype templfunc[tab]" -> "b rettype templfunc<bar>()"

   ... and others.

 - Tests the "b source.c[TAB] -> b source.cc:" feature.  I.e., colon
   auto-appending.

 - Exercises corner cases around C++ "operator<" / "operator<<".
   (Much more extensive C++ operator completion/linespec handling in a
   separate patch.)

 - Exercises both tab completion and "complete" command completion,
   using routines that handle it automatically, to ensure no test
   forgets either mode.

 - Many of the completion tests test completion at at prefix of a
   given tricky name, to make sure all corner cases are covered.
   E.g., completing before, at and after ":", "(", "<".

 - Exercises "keyword" completion.  I.e., "b function() [TAB]"
   displaying "if task thread" as completion match list.  Likewise for
   display explicit location options matches at the appropriate
   points.

 - Exercises matching symbols in all scopes (wild matching).  Ensures
   that the completer finds the same breakpoint locations that setting
   a breakpoint finds.

 - Tests that linespec/location completion doesn't find data symbols.

 - Tests that expression completion still kicks in after a
   linespec/location keyword.  I.e., this:

     "b function () if global1 + global[TAB]"

   knows that after "if", you're completing on an expression, and thus
   breaks words after "if" as an expression and matches on "global" as
   a data symbol.

 - Adds common routines to help with all the above, to be used by
   multiple completion and linespec/location test cases.

 - More...

Grows the gdb.linespec/ tests like this:

  -# of expected passes           573
  +# of expected passes           4458

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.linespec/cpcompletion.exp: New file.
	* gdb.linespec/cpls-hyphen.cc: New file.
	* gdb.linespec/cpls.cc: New file.
	* gdb.linespec/cpls2.cc: New file.
	* gdb.linespec/explicit.exp: Load completion-support.exp.  Adjust
	test to use test_gdb_complete_unique.  Add label completion,
	keyword completion and explicit location completion tests.
	* lib/completion-support.exp: New file.
---
 gdb/testsuite/gdb.linespec/cpcompletion.exp | 959 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.linespec/cpls-hyphen.cc   |  14 +
 gdb/testsuite/gdb.linespec/cpls.cc          | 386 +++++++++++
 gdb/testsuite/gdb.linespec/cpls2.cc         |  46 ++
 gdb/testsuite/gdb.linespec/explicit.exp     | 209 +++++-
 gdb/testsuite/lib/completion-support.exp    | 513 +++++++++++++++
 6 files changed, 2120 insertions(+), 7 deletions(-)
 create mode 100644 gdb/testsuite/gdb.linespec/cpcompletion.exp
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-hyphen.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls2.cc
 create mode 100644 gdb/testsuite/lib/completion-support.exp

diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
new file mode 100644
index 0000000..dd9bf70
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -0,0 +1,959 @@
+# Copyright 2017 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/>.
+
+# This file is part of the gdb testsuite.
+
+load_lib completion-support.exp
+
+standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc
+
+if {[prepare_for_testing "failed to prepare" $testfile \
+	 [list $srcfile $srcfile2 $srcfile3] {debug}]} {
+    return -1
+}
+
+# Disable the completion limit for the whole testcase.
+gdb_test_no_output "set max-completions unlimited"
+
+set timeout 5
+
+# Start of tests.
+
+# Test completion of all parameter prefixes, crossing "(" and ")",
+# with and without whitespace.
+
+proc_with_prefix all-param-prefixes {} {
+
+    # Test both linespecs and explicit locations.
+    foreach cmd_prefix {"b" "b -function"} {
+	set line "$cmd_prefix param_prefixes_test_long(long)"
+	set start [index_after "test_long" $line]
+	test_complete_prefix_range $line $start
+
+	# Same, but with extra spaces.  Note that the original spaces in
+	# the input line are preserved after completion.
+	test_gdb_complete_unique \
+	    "$cmd_prefix param_prefixes_test_long(long "   \
+	    "$cmd_prefix param_prefixes_test_long(long )"
+	test_gdb_complete_unique \
+	    "$cmd_prefix param_prefixes_test_long( long "  \
+	    "$cmd_prefix param_prefixes_test_long( long )"
+	test_gdb_complete_unique \
+	    "$cmd_prefix param_prefixes_test_long ( long " \
+	    "$cmd_prefix param_prefixes_test_long ( long )"
+
+	# Complete all parameter prefixes between "(i" and "(int*, int&)".
+	# Note that this exercises completing when the point is at the
+	# space in "param_prefixes_test_intp_intr(int*, ".
+	set line "$cmd_prefix param_prefixes_test_intp_intr(int*, int&)"
+	set start [index_after "intp_intr" $line]
+	test_complete_prefix_range $line $start
+
+	# Similar, but with extra spaces.
+	test_gdb_complete_unique \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int* " \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int* , int&)"
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int *" \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int *, int&)"
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int *, int " \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int *, int &)"
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int *,  int & " \
+	    "$cmd_prefix param_prefixes_test_intp_intr (  int *,  int & )"
+    }
+}
+
+# Test completion of an overloaded function.
+
+proc_with_prefix overload {} {
+    set completion_list {
+	"overload_ambiguous_test(int, int)"
+	"overload_ambiguous_test(int, long)"
+	"overload_ambiguous_test(long)"
+    }
+
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_multiple \
+	    "$cmd_prefix " "overload_ambiguous_" "test(" \
+	    $completion_list
+	check_bp_locations_match_list \
+	    "$cmd_prefix overload_ambiguous_test" \
+	    $completion_list
+
+	# Test disambiguating by typing enough to pick the "int" as
+	# first parameter type.  This then tests handling ambiguity in
+	# the second parameter, which checks that tab completion when
+	# the point is at the whitespace behaves naturally, by showing
+	# the remaining matching overloads to the user.
+	test_gdb_complete_multiple \
+	    "$cmd_prefix " "overload_ambiguous_test(i" "nt, " {
+	    "overload_ambiguous_test(int, int)"
+	    "overload_ambiguous_test(int, long)"
+	}
+
+	# Add a few more characters to make the completion
+	# unambiguous.
+	test_gdb_complete_unique \
+	    "$cmd_prefix overload_ambiguous_test(int, i" \
+	    "$cmd_prefix overload_ambiguous_test(int, int)"
+	check_bp_locations_match_list \
+	    "$cmd_prefix overload_ambiguous_test(int, int)" {
+		"overload_ambiguous_test(int, int)"
+	    }
+    }
+}
+
+# Test completion of a function that is defined in different scopes
+# with different parameters.
+
+proc_with_prefix overload-2 {} {
+    with_test_prefix "all" {
+	set completion_list {
+	    "(anonymous namespace)::overload2_function(overload2_arg3)"
+	    "(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg4)"
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+	    "ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	    "ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
+	    "ns_overload2_test::overload2_function(overload2_arg5)"
+	    "ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"
+	    "overload2_function(overload2_arg1)"
+	    "struct_overload2_test::overload2_function(overload2_arg2)"
+	}
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "overload2_func" "tion(overload2_arg" $completion_list
+	    check_bp_locations_match_list \
+		"$cmd_prefix overload2_function" $completion_list
+	}
+    }
+
+    # Same, but restrict to functions/methods in some scope.
+    with_test_prefix "restrict scope" {
+	set completion_list {
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	    "ns_overload2_test::overload2_function(overload2_arg5)"
+	}
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "ns_overload2_test::overload2_func" "tion(overload2_arg" $completion_list
+	    check_bp_locations_match_list \
+		"$cmd_prefix ns_overload2_test::overload2_function" $completion_list
+	}
+    }
+
+    # Restrict to anonymous namespace scopes.
+    with_test_prefix "restrict scope 2" {
+	set completion_list {
+	    "(anonymous namespace)::overload2_function(overload2_arg3)"
+	    "ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	}
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "(anonymous namespace)::overload2_func" "tion(overload2_arg" $completion_list
+	    check_bp_locations_match_list \
+		"$cmd_prefix (anonymous namespace)::overload2_function" $completion_list
+	}
+    }
+
+    # Add enough scopes, and we get a unique completion.
+    with_test_prefix "unique completion" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_unique \
+		"$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_func" \
+		"$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	    check_setting_bp_fails "$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_func"
+	    check_bp_locations_match_list \
+		"$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_function" \
+		{"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"}
+	}
+    }
+}
+
+# Test linespecs / locations using fully-qualified names.
+
+proc_with_prefix fqn {} {
+    set cmd_prefix "b -qualified"
+
+    test_gdb_complete_unique \
+	"$cmd_prefix overload2_func" \
+	"$cmd_prefix overload2_function(overload2_arg1)"
+
+    # Drill down until we find a unique completion.
+    test_gdb_complete_multiple "$cmd_prefix " "ns_overload2_test::" "" {
+	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+	"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
+	"ns_overload2_test::overload2_function(overload2_arg5)"
+	"ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"
+    }
+
+    test_gdb_complete_multiple "$cmd_prefix " "ns_overload2_test::(anonymous namespace)::" "" {
+	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+	"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
+    }
+
+    test_gdb_complete_multiple "$cmd_prefix " "ns_overload2_test::(anonymous namespace)::ns_overload2_test::" "" {
+	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+    }
+
+    test_gdb_complete_unique \
+	"$cmd_prefix ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_func" \
+	"$cmd_prefix ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+}
+
+# Check that a fully-qualified looked name don't match symbols in
+# nested scopes.
+
+proc_with_prefix fqn-2 {} {
+    set linespec "struct_overload2_test::overload2_function(overload2_arg6)"
+    set cmd_prefix "b -qualified"
+    check_setting_bp_fails "$cmd_prefix $linespec"
+    test_gdb_complete_none "$cmd_prefix $linespec"
+
+    # Check that using the same name, but not fully-qualifying it,
+    # would find something, just to make sure the test above is
+    # testing what we intend to test.
+    set cmd_prefix "b -function"
+    test_gdb_complete_unique "$cmd_prefix $linespec" "$cmd_prefix $linespec"
+    check_bp_locations_match_list \
+	"$cmd_prefix $linespec" \
+	{"ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"}
+}
+
+# Test completion of functions in different scopes that have the same
+# name and parameters.  Restricting the scopes should find fewer and
+# fewer matches.
+
+proc_with_prefix overload-3 {} {
+    with_test_prefix "all overloads" {
+	set completion_list {
+	    "(anonymous namespace)::overload3_function(int)"
+	    "(anonymous namespace)::overload3_function(long)"
+	    "(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+	    "(anonymous namespace)::struct_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::struct_overload3_test::overload3_function(long)"
+	    "overload3_function(int)"
+	    "overload3_function(long)"
+	    "struct_overload3_test::overload3_function(int)"
+	    "struct_overload3_test::overload3_function(long)"
+	}
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_multiple "$cmd_prefix " "overload3_func" "tion(" $completion_list
+	    check_bp_locations_match_list "$cmd_prefix overload3_function" $completion_list
+	}
+    }
+
+    with_test_prefix "restrict overload" {
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_unique \
+		"$cmd_prefix overload3_function(int)" \
+		"$cmd_prefix overload3_function(int)"
+	    check_bp_locations_match_list "$cmd_prefix overload3_function(int)" {
+		"(anonymous namespace)::overload3_function(int)"
+		"(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+		"ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(int)"
+		"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
+		"ns_overload3_test::(anonymous namespace)::overload3_function(int)"
+		"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+		"ns_overload3_test::overload3_function(int)"
+		"ns_overload3_test::struct_overload3_test::overload3_function(int)"
+		"overload3_function(int)"
+		"struct_overload3_test::overload3_function(int)"
+	    }
+	}
+    }
+
+    with_test_prefix "restrict scope" {
+	set completion_list {
+	    "(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+	    "(anonymous namespace)::struct_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::struct_overload3_test::overload3_function(long)"
+	    "struct_overload3_test::overload3_function(int)"
+	    "struct_overload3_test::overload3_function(long)"
+	}
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_multiple \
+		"$cmd_prefix " "struct_overload3_test::overload3_func" "tion(" \
+		$completion_list
+	    check_bp_locations_match_list \
+		"$cmd_prefix struct_overload3_test::overload3_function" \
+		$completion_list
+	}
+    }
+}
+
+# Test completing an overloaded template method.
+
+proc_with_prefix template-overload {} {
+    set completion_list {
+	"template_struct<int>::template_overload_fn(int)"
+	"template_struct<long>::template_overload_fn(long)"
+    }
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_multiple "$cmd_prefix " "template_overload_fn" "(" $completion_list
+	check_bp_locations_match_list "$cmd_prefix template_overload_fn" $completion_list
+	check_bp_locations_match_list \
+	    "$cmd_prefix template_struct<int>::template_overload_fn" \
+	    "template_struct<int>::template_overload_fn(int)"
+    }
+}
+
+# Test completing template methods with non-void return type.
+
+proc_with_prefix template-ret-type {} {
+    set method_name "template2_fn<int, int>"
+    set param_list "(template2_ret_type<int>, int, int)"
+    set struct_type "template2_struct<template2_ret_type<int> >"
+    set ret_type "template2_ret_type<int>"
+
+    # Templates are listed both with and without return type, making
+    # "template2_<tab>" ambiguous.
+    foreach cmd_prefix {"b" "b -function"} {
+	set completion_list \
+	    [list \
+		 "${ret_type} ${struct_type}::${method_name}${param_list}" \
+		 "${struct_type}::${method_name}${param_list}"]
+	test_gdb_complete_multiple "$cmd_prefix " "template2_" "" $completion_list
+
+	# Add one character more after "2_", and the linespec becomes
+	# unambiguous.  Test completing the whole prefix range after that,
+	# thus testing completing either with or without return type.
+	foreach {s t} [list \
+			   "template2_r" \
+			   "${ret_type} ${struct_type}::${method_name}${param_list}" \
+			   "template2_s" \
+			   "${struct_type}::${method_name}${param_list}"] {
+	    set linespec $t
+	    set complete_line "$cmd_prefix $linespec"
+	    set start [index_after $s $complete_line]
+	    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.
+	foreach linespec [list \
+			      "${method_name}" \
+			      "${method_name}${param_list}" \
+			      "${struct_type}::${method_name}" \
+			      "${struct_type}::${method_name}${param_list}" \
+			      "${ret_type} ${struct_type}::${method_name}" \
+			      "${ret_type} ${struct_type}::${method_name}${param_list}"] {
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" \
+		[list "${struct_type}::${method_name}${param_list}"]
+	}
+    }
+}
+
+# Test completion of a const-overloaded funtion (const-overload).
+# Note that "const" appears after the function/method parameters.
+
+proc_with_prefix const-overload {} {
+    set completion_list {
+	"struct_with_const_overload::const_overload_fn()"
+	"struct_with_const_overload::const_overload_fn() const"
+    }
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_multiple \
+	    "$cmd_prefix " "const_overload_fn" "()" \
+	    $completion_list
+	test_gdb_complete_multiple \
+	    "$cmd_prefix " "const_overload_fn ( " ")" \
+	    $completion_list
+	test_gdb_complete_multiple \
+	    "$cmd_prefix " "const_overload_fn()" "" \
+	    $completion_list
+
+	check_bp_locations_match_list \
+	    "$cmd_prefix const_overload_fn" \
+	    {"struct_with_const_overload::const_overload_fn()"
+		"struct_with_const_overload::const_overload_fn() const"}
+
+	check_setting_bp_fails "$cmd_prefix const_overload_fn("
+	check_bp_locations_match_list \
+	    "$cmd_prefix const_overload_fn()" \
+	    {"struct_with_const_overload::const_overload_fn()"}
+	check_bp_locations_match_list \
+	    "$cmd_prefix const_overload_fn() const" \
+	    {"struct_with_const_overload::const_overload_fn() const"}
+    }
+}
+
+# Same but quote-enclose the function name.  This makes the overload
+# no longer be ambiguous.
+
+proc_with_prefix const-overload-quoted {} {
+    foreach cmd_prefix {"b" "b -function"} {
+	set linespec "'const_overload_fn()'"
+	test_gdb_complete_unique "$cmd_prefix $linespec" "$cmd_prefix $linespec"
+	check_bp_locations_match_list \
+	    "$cmd_prefix $linespec" {
+		"struct_with_const_overload::const_overload_fn()"
+	    }
+
+	set linespec "'const_overload_fn() const'"
+	test_gdb_complete_unique "$cmd_prefix $linespec" "$cmd_prefix $linespec"
+	check_bp_locations_match_list \
+	    "$cmd_prefix $linespec" {
+		"struct_with_const_overload::const_overload_fn() const"
+	    }
+    }
+}
+
+# Test that when the function is unambiguous, linespec completion
+# appends the end quote char automatically, both ' and ".
+
+proc_with_prefix append-end-quote-char-when-unambiguous {} {
+    global all_quotes_list
+
+    foreach cmd_prefix {"b" "b -function"} {
+	foreach qc $all_quotes_list {
+	    set linespec "${qc}not_overloaded_fn()${qc}"
+	    foreach cmd [list "$cmd_prefix ${qc}not_overloaded_fn()" \
+			      "$cmd_prefix ${qc}not_overloaded_fn" \
+			      "$cmd_prefix ${qc}not_overloaded_"] {
+		test_gdb_complete_unique $cmd "$cmd_prefix $linespec"
+	    }
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" {"not_overloaded_fn()"}
+	}
+    }
+}
+
+# Test completing symbols of source files.
+
+proc_with_prefix in-source-file-unconstrained {} {
+    # First test that unconstrained matching picks up functions from
+    # multiple files.
+    test_gdb_complete_multiple "b " "file_constrained_test" "_cpls" {
+	"file_constrained_test_cpls2_function(int)"
+	"file_constrained_test_cpls_function(int)"
+    }
+    check_setting_bp_fails "b file_constrained_test_cpls"
+}
+
+# Test an unambiguous completion that would be ambiguous if it weren't
+# for the source file component, due to
+# "file_constrained_test_cpls_function" in cpls.cc.  Test with
+# different components quoted, and with whitespace before the function
+# name.
+
+proc_with_prefix in-source-file-unambiguous {} {
+    global maybe_quoted_list
+
+    foreach sqc $maybe_quoted_list {
+	foreach fqc $maybe_quoted_list {
+	    # Linespec.
+	    foreach sep {":" ": "} {
+		set linespec "${sqc}cpls2.cc${sqc}${sep}${fqc}file_constrained_test_cpls2_function(int)${fqc}"
+		set complete_line "b $linespec"
+		set start [index_after "constrained_test" $complete_line]
+		set input_line [string range $complete_line 0 $start]
+		test_gdb_complete_unique $input_line ${complete_line}
+		check_bp_locations_match_list "b $linespec" {
+		    "file_constrained_test_cpls2_function(int)"
+		}
+	    }
+
+	    # Explicit location.
+	    set source_opt "-source ${sqc}cpls2.cc${sqc}"
+	    set function_opt "-function ${fqc}file_constrained_test_cpls2_function(int)${fqc}"
+	    set complete_line "b $source_opt $function_opt"
+	    set start [index_after "cpls2_functio" $complete_line]
+	    set input_line [string range $complete_line 0 $start]
+	    test_gdb_complete_unique $input_line ${complete_line}
+	    check_bp_locations_match_list "$complete_line" {
+		    "file_constrained_test_cpls2_function(int)"
+	    }
+	}
+    }
+}
+
+# Test an ambiguous completion constrained by a source file.  Test
+# with different components quoted, and with whitespace before the
+# function name.
+
+proc_with_prefix in-source-file-ambiguous {} {
+    global maybe_quoted_list
+
+    foreach sqc $maybe_quoted_list {
+	foreach fqc $maybe_quoted_list {
+	    # Linespec.
+	    foreach sep {":" ": "} {
+		set cmd_prefix "b ${sqc}cpls2.cc${sqc}${sep}"
+		test_gdb_complete_multiple "${cmd_prefix}" ${fqc} "" {
+		    "another_file_constrained_test_cpls2_function(int)"
+		    "file_constrained_test_cpls2_function(int)"
+		} ${fqc} ${fqc}
+	    }
+
+	    # Explicit location.
+	    test_gdb_complete_multiple \
+		"b -source ${sqc}cpls2.cc${sqc} -function " ${fqc} "" {
+		"another_file_constrained_test_cpls2_function(int)"
+		"file_constrained_test_cpls2_function(int)"
+	    } ${fqc} ${fqc}
+	}
+    }
+}
+
+# Check that completing a file name in a linespec auto-appends a colon
+# instead of a whitespace character.
+
+proc_with_prefix source-complete-appends-colon {} {
+    global maybe_quoted_list
+    global all_quotes_list
+    global keyword_list
+
+    # Test with quotes to make sure the end quote char is put at the
+    # right place.
+    foreach qc $maybe_quoted_list {
+	test_gdb_complete_unique \
+	    "b ${qc}cpls2." \
+	    "b ${qc}cpls2.cc${qc}" ":"
+	test_gdb_complete_unique \
+	    "b ${qc}cpls2.c" \
+	    "b ${qc}cpls2.cc${qc}" ":"
+	test_gdb_complete_unique \
+	    "b ${qc}cpls2.cc" \
+	    "b ${qc}cpls2.cc${qc}" ":"
+
+	# Same, but with a filename with an hyphen (which is normally
+	# a language word break char).
+	test_gdb_complete_unique \
+	    "b ${qc}cpls-" \
+	    "b ${qc}cpls-hyphen.cc${qc}" ":"
+	test_gdb_complete_unique \
+	    "b ${qc}cpls-hyphen" \
+	    "b ${qc}cpls-hyphen.cc${qc}" ":"
+    }
+
+    # Test the same, but with the name of a nonexisting file.
+
+    # Cursor at the end of the string.
+    test_gdb_complete_none "b nonexistingfilename.cc"
+    # Cursor past the end of the string.
+    test_gdb_complete_multiple "b nonexistingfilename.cc " "" "" $keyword_list
+    foreach qc $all_quotes_list {
+	# Unterminated quote.
+	test_gdb_complete_none "b ${qc}nonexistingfilename.cc"
+	test_gdb_complete_none "b ${qc}nonexistingfilename.cc "
+	# Terminated quote, cursor at the quote.
+	test_gdb_complete_unique \
+	    "b ${qc}nonexistingfilename.cc${qc}" \
+	    "b ${qc}nonexistingfilename.cc${qc}"
+	# Terminated quote, cursor past the quote.
+	test_gdb_complete_multiple \
+	    "b ${qc}nonexistingfilename.cc${qc} " "" "" $keyword_list
+    }
+}
+
+####################################################################
+
+# Test that a colon at the end of the linespec is understood as an
+# incomplete scope operator (incomplete-scope-colon), instead of a
+# source/function separator.
+
+proc_with_prefix incomplete-scope-colon {} {
+
+    # Helper for the loop below to simplify it.  Tests completion of
+    # the range defined by the RANGE_SS found in the constructed line.
+    #
+    # E.g., with:
+    #
+    #   source="source.cc"
+    #   fqc="'"
+    #   prototype="ns::function()"
+    #   range_ss="s::f"
+    #
+    # we'd try completing with the cursor set in each of the
+    # underlined range's positions of:
+    #
+    #   b source.cc:'ns::function()'"
+    #                 ^^^^
+    #
+    # Also test that setting a breakpoint at the constructed line
+    # finds the same breakpoint location as completion does.
+    #
+    proc incomplete_scope_colon_helper {prototype range_ss {skip_check_bp 0}} {
+	global maybe_quoted_list
+
+	foreach source {"" "cpls.cc"} {
+	    # Test with and without source quoting.
+	    foreach sqc $maybe_quoted_list {
+		if {$source == "" && $sqc != ""} {
+		    # Invalid combination.
+		    continue
+		}
+
+		# Test with and without function quoting.
+		foreach fqc $maybe_quoted_list {
+		    if {$source == ""} {
+			set linespec_source ""
+			set explicit_source ""
+		    } else {
+			set linespec_source "${sqc}${source}${sqc}:"
+			set explicit_source "-source ${sqc}${source}${sqc}"
+		    }
+
+		    # Even though this use case is trickier with
+		    # linespecs due to the ":" as separator, test both
+		    # linespecs and explicit locations for
+		    # completeness.
+		    foreach location [list \
+					  "${linespec_source}${fqc}$prototype${fqc}" \
+					  "${explicit_source} -function ${fqc}$prototype${fqc}"] {
+			set complete_line "b $location"
+			set start [string first $range_ss $complete_line]
+			set end [expr ($start + [string length $range_ss])]
+			test_complete_prefix_range $complete_line $start $end
+			if {!$skip_check_bp} {
+			    check_bp_locations_match_list "b $location" [list "$prototype"]
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    incomplete_scope_colon_helper \
+	"struct_incomplete_scope_colon_test::incomplete_scope_colon_test()" \
+	"t::i"
+
+    incomplete_scope_colon_helper \
+	"ns_incomplete_scope_colon_test::incomplete_scope_colon_test()" \
+	"t::i"
+
+    # Test completing around both "::"s.
+    foreach range_ss {"t::s" "t::i"} skip_check_bp {0 1} {
+	incomplete_scope_colon_helper \
+	    "ns2_incomplete_scope_colon_test::struct_in_ns2_incomplete_scope_colon_test::incomplete_scope_colon_test()" \
+	    $range_ss $skip_check_bp
+    }
+}
+
+# Test completing functions/methods in anonymous namespaces.
+
+proc_with_prefix anon-ns {} {
+    global maybe_quoted_list
+
+    foreach cmd_prefix {"b" "b -function"} {
+	foreach qc $maybe_quoted_list {
+	    test_gdb_complete_unique \
+		"$cmd_prefix ${qc}anon_ns_function" \
+		"$cmd_prefix ${qc}anon_ns_function()${qc}"
+	    check_bp_locations_match_list "$cmd_prefix ${qc}anon_ns_function()${qc}" {
+		"(anonymous namespace)::anon_ns_function()"
+		"(anonymous namespace)::anon_ns_struct::anon_ns_function()"
+		"the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_function()"
+		"the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_struct::anon_ns_function()"
+	    }
+	}
+
+	# A "(" finds all anonymous namespace functions/methods in all
+	# scopes.
+	test_gdb_complete_multiple "$cmd_prefix " "(" "anonymous namespace)::" {
+	    "(anonymous namespace)::anon_ns_function()"
+	    "(anonymous namespace)::anon_ns_struct::anon_ns_function()"
+	    "(anonymous namespace)::overload2_function(overload2_arg3)"
+	    "(anonymous namespace)::overload3_function(int)"
+	    "(anonymous namespace)::overload3_function(long)"
+	    "(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg4)"
+	    "(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+	    "(anonymous namespace)::struct_overload3_test::overload3_function(long)"
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+	    "ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	    "ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::overload3_function(long)"
+	    "ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
+	    "ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(long)"
+	    "the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_function()"
+	    "the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_struct::anon_ns_function()"
+	}
+
+	set function "the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_function()"
+	test_gdb_complete_unique "$cmd_prefix $function" "$cmd_prefix $function"
+	check_bp_locations_match_list "$cmd_prefix $function" [list $function]
+
+	# Test completing after the "(anonymous namespace)" part.
+	test_gdb_complete_unique \
+	    "$cmd_prefix the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_fu" \
+	    "$cmd_prefix $function"
+
+	# Test whitespace in the "(anonymous namespace)" component.
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix the_anon_ns_wrapper_ns::( anonymous   namespace )::anon_ns_fu" \
+	    "$cmd_prefix the_anon_ns_wrapper_ns::( anonymous   namespace )::anon_ns_function()"
+	check_setting_bp_fails \
+	    "$cmd_prefix the_anon_ns_wrapper_ns::( anonymous   namespace )::anon_ns_fu"
+
+	set function_ws \
+	    "the_anon_ns_wrapper_ns::( anonymous   namespace )::anon_ns_function ( )"
+	test_gdb_complete_unique "$cmd_prefix $function_ws" "$cmd_prefix $function_ws"
+	check_bp_locations_match_list "$cmd_prefix $function_ws" [list $function]
+    }
+}
+
+# Basic test for completing "operator<".  More extensive C++ operator
+# tests in cpls-op.exp.
+
+proc_with_prefix operator< {} {
+    # Complete all prefixes between "oper" and the whole prototype.
+    set function "operator<(foo_enum, foo_enum)"
+    foreach cmd_prefix {"b" "b -function"} {
+	set line "$cmd_prefix $function"
+	set start [index_after "oper" $line]
+	test_complete_prefix_range $line $start
+    }
+
+    # There's a label in the function; try completing it.  (Exhaustive
+    # label completion tests further below.)
+    foreach location [list \
+		     "$function:label1" \
+		     "-function $function -label label1"] {
+
+	set cmd "b $location"
+	set input_line [string range $cmd 0 [expr [string length $cmd] - 3]]
+
+	test_gdb_complete_unique $input_line $cmd
+	test_gdb_complete_unique $cmd $cmd
+	check_bp_locations_match_list $cmd [list "$location"]
+    }
+}
+
+# Test completion of scopes with an ambiguous prefix.
+
+proc_with_prefix ambiguous-prefix {} {
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_multiple "$cmd_prefix " "ambiguous_pre" "fix_" {
+	    "ambiguous_prefix_global_func()"
+	    "the_ambiguous_prefix_ns::ambiguous_prefix_ns_func()"
+	    "the_ambiguous_prefix_struct::ambiguous_prefix_method()"
+	}
+	check_setting_bp_fails "$cmd_prefix ambiguous_prefix_"
+    }
+}
+
+# Test completion of function labels.
+
+proc_with_prefix function-labels {} {
+    # Test with and without a source file component.
+    foreach_location_functions \
+	{ "" "cpls.cc" } \
+	{ "function_with_labels(int)" } \
+	{
+	    # Linespec version.  Test various spacing around the label
+	    # colon separator.
+	    foreach label_sep {":" " :" ": " " : "} {
+		set linespec "${location}${label_sep}"
+		test_gdb_complete_multiple "b $linespec" "l" "abel" {
+		    "label1"
+		    "label2"
+		}
+		check_setting_bp_fails "b ${linespec}label"
+
+		set tsep [string trim ${source_sep}]
+		check_bp_locations_match_list \
+		    "b ${linespec}label1" [list "${source}${tsep}${function}:label1"]
+		check_bp_locations_match_list \
+		    "b ${linespec}label2" [list "${source}${tsep}${function}:label2"]
+	    }
+	} \
+	{
+	    # Explicit locations version.
+	    append location " -label"
+	    test_gdb_complete_multiple "b $location " "l" "abel" {
+		"label1"
+		"label2"
+	    }
+	    check_setting_bp_fails "b $location label"
+
+	    if {$source != ""} {
+		set bp_loc_src "-source ${source} "
+	    } else {
+		set bp_loc_src ""
+	    }
+	    check_bp_locations_match_list \
+		"b ${location} label1" [list "${bp_loc_src}-function $function -label label1"]
+	    check_bp_locations_match_list \
+		"b ${location} label2" [list "${bp_loc_src}-function $function -label label2"]
+	}
+}
+
+# Test that completion after a function name offers keyword
+# (if/task/thread) matches in linespec mode, and also the explicit
+# location options in explicit locations mode.
+
+proc_with_prefix keywords-after-function {} {
+    global explicit_opts_list keyword_list
+
+    set explicit_list [concat $explicit_opts_list $keyword_list]
+
+    # Test without a source file, with a known source file, and with
+    # and unknown source file.
+    # Test a known and an unknown function.
+    foreach_location_functions \
+	{ "" "cpls.cc" "unknown_file.cc" } \
+	{ "function_with_labels(int)" "unknown_function(int)" } \
+	{
+	    # Linespec version.
+	    test_gdb_complete_multiple "b ${location} " "" "" $keyword_list
+	} \
+	{
+	    # Explicit locations version.
+	    test_gdb_complete_multiple "b ${location} " "" "" $explicit_list
+	}
+}
+
+# Same, but after a label.
+
+proc_with_prefix keywords-after-label {} {
+    global explicit_opts_list keyword_list
+
+    set explicit_list [concat $explicit_opts_list $keyword_list]
+
+    foreach_location_labels \
+	{ "" "cpls.cc" } \
+	{ "function_with_labels(int)" "unknown_function(int)" } \
+	{ "label1" "non_existing_label" } \
+	{
+	    # Linespec version.
+	    test_gdb_complete_multiple "b ${location} " "" "" $keyword_list
+	} \
+	{
+	    # Explicit locations version.
+	    test_gdb_complete_multiple "b ${location} " "" "" $explicit_list
+	}
+}
+
+# Similar, but after an unknown file, and in linespec mode only.
+
+proc_with_prefix keywords-after-unknown-file {} {
+    global maybe_quoted_list
+    global keyword_list
+
+    # Test with and without quoting.
+    foreach qc $maybe_quoted_list {
+	set line "b ${qc}unknown_file.cc${qc}: "
+	test_gdb_complete_multiple $line "" "" $keyword_list
+    }
+}
+
+# Test that linespec / function completion does not match data
+# symbols, only functions/methods.
+
+proc_with_prefix no-data-symbols {} {
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_unique "$cmd_prefix code_" "$cmd_prefix code_function()"
+    }
+}
+
+
+# After "if", we expect an expression, which has a different completer
+# that matches data symbols as well.  Check that that works.
+
+proc_with_prefix if-expression {} {
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_multiple "$cmd_prefix function() if " "code_" "" {
+	    "code_data"
+	    "code_function()"
+	}
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix function() if code_data + another_da" \
+	    "$cmd_prefix function() if code_data + another_data"
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix non_existing_function() if code_data + another_da" \
+	    "$cmd_prefix non_existing_function() if code_data + another_data"
+
+	# FIXME: For now, thread and task also use the expression
+	# completer.
+	test_gdb_complete_unique \
+	    "$cmd_prefix function() thread code_data + another_da" \
+	    "$cmd_prefix function() thread code_data + another_data"
+	test_gdb_complete_unique \
+	    "$cmd_prefix function() task code_data + another_da" \
+	    "$cmd_prefix function() task code_data + another_data"
+    }
+}
+
+# The testcase driver.  Calls all test procedures.
+
+proc test_driver {} {
+    all-param-prefixes
+    overload
+    overload-2
+    fqn
+    fqn-2
+    overload-3
+    template-overload
+    template-ret-type
+    const-overload
+    const-overload-quoted
+    append-end-quote-char-when-unambiguous
+    in-source-file-unconstrained
+    in-source-file-unambiguous
+    in-source-file-ambiguous
+    source-complete-appends-colon
+    incomplete-scope-colon
+    anon-ns
+    operator<
+    ambiguous-prefix
+    function-labels
+    keywords-after-function
+    keywords-after-label
+    keywords-after-unknown-file
+    no-data-symbols
+    if-expression
+}
+
+test_driver
diff --git a/gdb/testsuite/gdb.linespec/cpls-hyphen.cc b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
new file mode 100644
index 0000000..fdc063f
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
@@ -0,0 +1,14 @@
+int
+ns_hyphen_function (int i)
+{
+  if (i > 0)
+    {
+    label1:
+      return i + 20;
+    }
+  else
+    {
+    label2:
+      return i + 10;
+    }
+}
diff --git a/gdb/testsuite/gdb.linespec/cpls.cc b/gdb/testsuite/gdb.linespec/cpls.cc
new file mode 100644
index 0000000..776dd4c
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls.cc
@@ -0,0 +1,386 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 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/>.  */
+
+/* Code for the all-param-prefixes test.  */
+
+void
+param_prefixes_test_long (long)
+{}
+
+void
+param_prefixes_test_intp_intr (int *, int&)
+{}
+
+/* Code for the overload test.  */
+
+void
+overload_ambiguous_test (long)
+{}
+
+void
+overload_ambiguous_test (int, int)
+{}
+
+void
+overload_ambiguous_test (int, long)
+{}
+
+/* Code for the overload-2 test.  */
+
+/* Generate functions/methods all with the same name, in different
+   scopes, but all with different parameters.  */
+
+struct overload2_arg1 {};
+struct overload2_arg2 {};
+struct overload2_arg3 {};
+struct overload2_arg4 {};
+struct overload2_arg5 {};
+struct overload2_arg6 {};
+struct overload2_arg7 {};
+struct overload2_arg8 {};
+struct overload2_arg9 {};
+struct overload2_arga {};
+
+#define GEN_OVERLOAD2_FUNCTIONS(ARG1, ARG2)		\
+  void							\
+  overload2_function (ARG1)				\
+  {}							\
+							\
+  struct struct_overload2_test				\
+  {							\
+    void overload2_function (ARG2);			\
+  };							\
+							\
+  void							\
+  struct_overload2_test::overload2_function (ARG2)	\
+  {}
+
+/* In the global namespace.  */
+GEN_OVERLOAD2_FUNCTIONS( overload2_arg1, overload2_arg2)
+
+namespace
+{
+  /* In an anonymous namespace.  */
+  GEN_OVERLOAD2_FUNCTIONS (overload2_arg3, overload2_arg4)
+}
+
+namespace ns_overload2_test
+{
+  /* In a namespace.  */
+  GEN_OVERLOAD2_FUNCTIONS (overload2_arg5, overload2_arg6)
+
+  namespace
+  {
+    /* In a nested anonymous namespace.  */
+    GEN_OVERLOAD2_FUNCTIONS (overload2_arg7, overload2_arg8)
+
+    namespace ns_overload2_test
+    {
+      /* In a nested namespace.  */
+      GEN_OVERLOAD2_FUNCTIONS (overload2_arg9, overload2_arga)
+    }
+  }
+}
+
+/* Code for the overload-3 test.  */
+
+#define GEN_OVERLOAD3_FUNCTIONS(ARG1, ARG2)		\
+  void							\
+  overload3_function (ARG1)				\
+  {}							\
+  void							\
+  overload3_function (ARG2)				\
+  {}							\
+							\
+  struct struct_overload3_test				\
+  {							\
+    void overload3_function (ARG1);			\
+    void overload3_function (ARG2);			\
+  };							\
+							\
+  void							\
+  struct_overload3_test::overload3_function (ARG1)	\
+  {}							\
+  void							\
+  struct_overload3_test::overload3_function (ARG2)	\
+  {}
+
+/* In the global namespace.  */
+GEN_OVERLOAD3_FUNCTIONS (int, long)
+
+namespace
+{
+  /* In an anonymous namespace.  */
+  GEN_OVERLOAD3_FUNCTIONS (int, long)
+}
+
+namespace ns_overload3_test
+{
+  /* In a namespace.  */
+  GEN_OVERLOAD3_FUNCTIONS (int, long)
+
+  namespace
+  {
+    /* In a nested anonymous namespace.  */
+    GEN_OVERLOAD3_FUNCTIONS (int, long)
+
+    namespace ns_overload3_test
+    {
+      /* In a nested namespace.  */
+      GEN_OVERLOAD3_FUNCTIONS (int, long)
+    }
+  }
+}
+
+/* Code for the template-overload tests.  */
+
+template <typename T>
+struct template_struct
+{
+  T template_overload_fn (T);
+};
+
+template <typename T>
+T template_struct<T>::template_overload_fn (T t)
+{
+  return t;
+}
+
+template_struct<int> template_struct_int;
+template_struct<long> template_struct_long;
+
+/* Code for the template2-ret-type tests.  */
+
+template <typename T>
+struct template2_ret_type {};
+
+template <typename T>
+struct template2_struct
+{
+  template <typename T2, typename T3>
+  T template2_fn (T = T (), T2 t2 = T2 (), T3 t3 = T3 ());
+};
+
+template <typename T>
+template <typename T2, typename T3>
+T template2_struct<T>::template2_fn (T t, T2 t2, T3 t3)
+{
+  return T ();
+}
+
+template2_struct<template2_ret_type<int> > template2_struct_inst;
+
+/* Code for the const-overload tests.  */
+
+struct struct_with_const_overload
+{
+  void const_overload_fn ();
+  void const_overload_fn () const;
+};
+
+void
+struct_with_const_overload::const_overload_fn ()
+{}
+
+void
+struct_with_const_overload::const_overload_fn () const
+{}
+
+void
+not_overloaded_fn ()
+{}
+
+/* Code for the incomplete-scope-colon tests.  */
+
+struct struct_incomplete_scope_colon_test
+{
+  void incomplete_scope_colon_test ();
+};
+
+void
+struct_incomplete_scope_colon_test::incomplete_scope_colon_test ()
+{}
+
+namespace ns_incomplete_scope_colon_test
+{
+  void incomplete_scope_colon_test () {}
+}
+
+namespace ns2_incomplete_scope_colon_test
+{
+  struct struct_in_ns2_incomplete_scope_colon_test
+  {
+    void incomplete_scope_colon_test ();
+  };
+
+  void
+  struct_in_ns2_incomplete_scope_colon_test::incomplete_scope_colon_test ()
+  {}
+}
+
+/* Code for the anon-ns tests.  */
+
+namespace
+{
+  void anon_ns_function ()
+  {}
+
+  struct anon_ns_struct
+  {
+    void anon_ns_function ();
+  };
+
+  void
+  anon_ns_struct::anon_ns_function ()
+  {}
+}
+
+namespace the_anon_ns_wrapper_ns
+{
+
+namespace
+{
+  void anon_ns_function ()
+  {}
+
+  struct anon_ns_struct
+  {
+    void anon_ns_function ();
+  };
+
+  void
+  anon_ns_struct::anon_ns_function ()
+  {}
+}
+
+} /* the_anon_ns_wrapper_ns */
+
+/* Code for the global-ns-scope-op tests.  */
+
+void global_ns_scope_op_function ()
+{
+}
+
+/* Add a function with the same name to a namespace.  We want to test
+   that "b ::global_ns_function" does NOT select it.  */
+namespace the_global_ns_scope_op_ns
+{
+  void global_ns_scope_op_function ()
+  {
+  }
+}
+
+/* Code for the ambiguous-prefix tests.  */
+
+/* Create a few functions/methods with the same "ambiguous_prefix_"
+   prefix.  They in different scopes, but "b ambiguous_prefix_<tab>"
+   should list them all, and figure out the LCD is
+   ambiguous_prefix_.  */
+
+void ambiguous_prefix_global_func ()
+{
+}
+
+namespace the_ambiguous_prefix_ns
+{
+  void ambiguous_prefix_ns_func ()
+  {
+  }
+}
+
+struct the_ambiguous_prefix_struct
+{
+  void ambiguous_prefix_method ();
+};
+
+void
+the_ambiguous_prefix_struct::ambiguous_prefix_method ()
+{
+}
+
+/* Code for the function-labels test.  */
+
+int
+function_with_labels (int i)
+{
+  if (i > 0)
+    {
+    label1:
+      return i + 20;
+    }
+  else
+    {
+    label2:
+      return i + 10;
+    }
+}
+
+/* Code for the no-data-symbols and if-expression tests.  */
+
+int code_data = 0;
+
+int another_data = 0;
+
+/* A function that has a same "code" prefix as the global above.  We
+   want to ensure that completing on "b code" doesn't offer the data
+   symbol.  */
+void
+code_function ()
+{
+}
+
+/* Code for the operator< tests.  */
+
+enum foo_enum
+  {
+    foo_value
+  };
+
+bool operator<(foo_enum lhs, foo_enum rhs)
+{
+ label1:
+	return false;
+}
+
+/* Code for the in-source-file-unconstrained /
+   in-source-file-ambiguous tests.  */
+
+int
+file_constrained_test_cpls_function (int i)
+{
+  if (i > 0)
+    {
+    label1:
+      return i + 20;
+    }
+  else
+    {
+    label2:
+      return i + 10;
+    }
+}
+
+
+int
+main ()
+{
+  template2_struct_inst.template2_fn<int, int> ();
+  template_struct_int.template_overload_fn(0);
+  template_struct_long.template_overload_fn(0);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/cpls2.cc b/gdb/testsuite/gdb.linespec/cpls2.cc
new file mode 100644
index 0000000..a8f1319
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls2.cc
@@ -0,0 +1,46 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+file_constrained_test_cpls2_function (int i)
+{
+  if (i > 0)
+    {
+    label1:
+      return i + 20;
+    }
+  else
+    {
+    label2:
+      return i + 10;
+    }
+}
+
+int
+another_file_constrained_test_cpls2_function (int i)
+{
+  if (i > 0)
+    {
+    label1:
+      return i + 20;
+    }
+  else
+    {
+    label2:
+      return i + 10;
+    }
+}
diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp
index 65d78ca..998b70a 100644
--- a/gdb/testsuite/gdb.linespec/explicit.exp
+++ b/gdb/testsuite/gdb.linespec/explicit.exp
@@ -15,6 +15,8 @@
 
 # Tests for explicit locations
 
+load_lib completion-support.exp
+
 standard_testfile explicit.c explicit2.c 3explicit.c
 set exefile $testfile
 
@@ -222,13 +224,14 @@ namespace eval $testfile {
 	    }
 	}
 
-	set tst "complete unique file name"
-	send_gdb "break -source 3ex\t"
-	gdb_test_multiple "" $tst {
-	    -re "break -source 3explicit.c " {
-		send_gdb "\n"
-		gdb_test "" \
-		    {Source filename requires function, label, or line offset.} $tst
+	with_test_prefix "complete unique file name" {
+	    foreach qc $maybe_quoted_list {
+		set cmd "break -source ${qc}3explicit.c${qc}"
+		test_gdb_complete_unique \
+		    "break -source ${qc}3ex" \
+		    $cmd
+		gdb_test $cmd \
+		    {Source filename requires function, label, or line offset.}
 	    }
 	}
 
@@ -326,10 +329,202 @@ namespace eval $testfile {
 	    }
 	}
 
+	global maybe_quoted_list
+
+	with_test_prefix "complete unique label name" {
+	    foreach qc $maybe_quoted_list {
+		test_gdb_complete_unique \
+		    "break -function myfunction -label ${qc}to" \
+		    "break -function myfunction -label ${qc}top${qc}"
+	    }
+	}
+
+	with_test_prefix "complete unique label name with source file" {
+	    test_gdb_complete_unique \
+		"break -source explicit.c -function myfunction -label to" \
+		"break -source explicit.c -function myfunction -label top"
+	}
+
+	with_test_prefix "complete unique label name reversed" {
+	    test_gdb_complete_multiple "b -label top -function " "myfunction" "" {
+		"myfunction"
+		"myfunction2"
+		"myfunction3"
+		"myfunction4"
+	    }
+	}
+
+	with_test_prefix "complete non-unique label name" {
+	    test_gdb_complete_multiple "b -function myfunction -label " "" "" {
+		"done"
+		"top"
+	    }
+	}
+
+	# The program is stopped at myfunction, so gdb is able to
+	# infer the label's function.
+	with_test_prefix "complete label name with no function" {
+	    test_gdb_complete_unique \
+		"break -label to" \
+		"break -label top"
+	    check_bp_locations_match_list \
+		"break -label top" {
+		    "-function myfunction -label top"
+		}
+	}
+
+	# See above.
+	with_test_prefix "complete label name with source file but no function" {
+	    test_gdb_complete_unique \
+		"break -source explicit.c -label to" \
+		"break -source explicit.c -label top"
+	    check_bp_locations_match_list \
+		"break -source explicit.c -label top" {
+		    "-source explicit.c -function myfunction -label top"
+		}
+	}
+
+	with_test_prefix "complete label name with wrong source file" {
+	    test_gdb_complete_none \
+		"break -source explicit2.c -function myfunction -label to"
+	    check_setting_bp_fails \
+		"break -source explicit2.c -function myfunction -label top"
+	}
+
+	# Get rid of symbols from shared libraries, otherwise
+	# "b -source thr<tab>" could find some system library's
+	# source.
+	gdb_test_no_output "nosharedlibrary"
+
+	# Test that after a seemingly finished option argument,
+	# completion matches both the explicit location options and
+	# the linespec keywords.
+	set completions_list {
+	    "-function"
+	    "-label"
+	    "-line"
+	    "-qualified"
+	    "-source"
+	    "if"
+	    "task"
+	    "thread"
+	}
+	foreach what { "-function" "-label" "-line" "-qualified" "-source" } {
+	    with_test_prefix "complete after $what" {
+		if {$what != "-line"} {
+		    test_gdb_complete_multiple \
+			"b $what argument " "" "" $completions_list
+		    test_gdb_complete_unique \
+			"b $what argument thr" \
+			"b $what argument thread"
+		    test_gdb_complete_unique \
+			"b $what argument -fun" \
+			"b $what argument -function"
+		} else {
+		    # After -line, we expect a number / offset.
+		    foreach line {"10" "+10" "-10"} {
+			test_gdb_complete_multiple \
+			    "b -line $line " "" "" $completions_list
+			test_gdb_complete_unique \
+			    "b -line $line thr" \
+			    "b -line $line thread"
+			test_gdb_complete_unique \
+			    "b -line $line -fun" \
+			    "b -line $line -function"
+		    }
+
+		    # With an invalid -line argument, we don't get any
+		    # completions.
+		    test_gdb_complete_none "b -line argument "
+		}
+
+		# Don't complete a linespec keyword ("thread") or
+		# another option name when expecting an option
+		# argument.
+		test_gdb_complete_none "b $what thr"
+		test_gdb_complete_none "b $what -fun"
+	    }
+	}
+
+	# Tests that ensure that after "if" we complete on expressions
+	# are in cpcompletion.exp.
+
+	# Disable the completion limit for the rest of the testcase.
+	gdb_test_no_output "set max-completions unlimited"
+
+	# Get rid of symbols from shared libraries, otherwise the
+	# completions match list for "break <tab>" is huge and makes
+	# the test below quite long while the gdb_test_multiple loop
+	# below consumes the matches.  Not doing this added ~20
+	# seconds at the time of writing.  (Actually, already done above.)
+	# gdb_test_no_output "nosharedlibrary"
+
+	# Test completion with no input argument.  We should see all
+	# the options, plus all the functions.  To keep it simple, as
+	# proxy, we check for presence of one explicit location
+	# option, one probe location, and one function.
+	set saw_opt_function 0
+	set saw_opt_probe_stap 0
+	set saw_function 0
+
+	set tst "complete with no arguments"
+	send_gdb "break \t"
+	gdb_test_multiple "" $tst {
+	    "break \\\x07" {
+		send_gdb "\t\t"
+		gdb_test_multiple "" $tst {
+		    "Display all" {
+			send_gdb "y"
+			exp_continue
+		    }
+		    -re "-function" {
+			set saw_opt_function 1
+			exp_continue
+		    }
+		    -re "-probe-stap" {
+			set saw_opt_probe_stap 1
+			exp_continue
+		    }
+		    -re "myfunction4" {
+			set saw_function 1
+			exp_continue
+		    }
+		    -re "\r\n$gdb_prompt " {
+			gdb_assert {$saw_opt_function && $saw_opt_probe_stap && $saw_function} $tst
+		    }
+		    -re "  " {
+			exp_continue
+		    }
+		}
+	    }
+	}
+	clear_input_line $tst
+
 	# NOTE: We don't bother testing more elaborate combinations of options,
 	# such as "-func main -sour 3ex\t" (main is defined in explicit.c).
 	# The completer cannot handle these yet.
 
+	# Follows completion tests that require having no symbols
+	# loaded.
+	gdb_exit
+	gdb_start
+
+	# The match list you get when you complete with no options
+	# specified at all.
+	set completion_list {
+	    "-function"
+	    "-label"
+	    "-line"
+	    "-probe"
+	    "-probe-dtrace"
+	    "-probe-stap"
+	    "-qualified"
+	    "-source"
+	}
+	with_test_prefix "complete with no arguments and no symbols" {
+	    test_gdb_complete_multiple "b " "" "-" $completion_list
+	    test_gdb_complete_multiple "b " "-" "" $completion_list
+	}
     }
     # End of completion tests.
 
diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
new file mode 100644
index 0000000..ef78269
--- /dev/null
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -0,0 +1,513 @@
+# Copyright 2017 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/>.
+
+# This file is part of the gdb testsuite.
+
+set timeout 5
+
+set bell_re "\\\x07"
+
+# List of all quote chars.
+set all_quotes_list {"'" "\""}
+
+# List of all quote chars, including no-quote at all.
+set maybe_quoted_list {"" "'" "\""}
+
+set keyword_list {"if" "task" "thread"}
+set explicit_opts_list {"-function" "-label" "-line" "-qualified" "-source"}
+
+# Make a regular expression that matches a TAB completion list.
+
+proc make_tab_completion_list_re { completion_list } {
+    # readline separates the completion columns that fit on the same
+    # line with whitespace.  Since we're testing under "set width
+    # unlimited", all completions will be printed on the same line.
+    # The amount of whitespace depends on the length of the widest
+    # completion.  We could compute that here and expect the exact
+    # number of ws characters between each completion match, but to
+    # keep it simple, we accept any number of characters.
+    set ws " +"
+
+    set completion_list_re ""
+    foreach c $completion_list {
+	append completion_list_re [string_to_regexp $c]
+	append completion_list_re $ws
+    }
+    append completion_list_re $ws
+
+    return $completion_list_re
+}
+
+# Make a regular expression that matches a "complete" command
+# completion list.  CMD_PREFIX is the command prefix added to each
+# completion match.
+
+proc make_cmd_completion_list_re { cmd_prefix completion_list start_quote_char end_quote_char } {
+
+    set completion_list_re ""
+    foreach c $completion_list {
+	# The command prefix is included in all completion matches.
+	append completion_list_re [string_to_regexp $cmd_prefix$start_quote_char$c$end_quote_char]
+	append completion_list_re "\r\n"
+    }
+
+    return $completion_list_re
+}
+
+# Clear the input line.
+
+proc clear_input_line { test } {
+    global gdb_prompt
+
+    send_gdb "\003"
+    gdb_test_multiple "" "$test (clearing input line)" {
+	-re "Quit\r\n$gdb_prompt $" {
+	}
+    }
+}
+
+# Test that completing LINE with TAB completes to nothing.
+
+proc test_gdb_complete_tab_none { line } {
+    global bell_re
+
+    set line_re [string_to_regexp $line]
+
+    set test "tab complete \"$line\""
+    send_gdb "$line\t"
+    gdb_test_multiple "" "$test" {
+	-re "^$line_re$bell_re$" {
+	    pass "$test"
+	}
+    }
+
+    clear_input_line $test
+}
+
+# Test that completing INPUT_LINE with TAB completes to
+# COMPLETE_LINE_RE.  APPEND_CHAR_RE is the character expected to be
+# appended after EXPECTED_OUTPUT.  Normally that's a whitespace, but
+# in some cases it's some other character, like a colon.
+
+proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re } {
+
+    set test "tab complete \"$input_line\""
+    send_gdb "$input_line\t"
+    gdb_test_multiple "" "$test" {
+	-re "^$complete_line_re$append_char_re$" {
+	    pass "$test"
+	}
+    }
+
+    clear_input_line $test
+}
+
+# Test that completing INPUT_LINE with TAB completes to "INPUT_LINE +
+# ADD_COMPLETED_LINE" and that is displays the completion matches in
+# COMPLETION_LIST.
+
+proc test_gdb_complete_tab_multiple { input_line add_completed_line \
+					  completion_list } {
+    global gdb_prompt
+    global bell_re
+
+    set input_line_re [string_to_regexp $input_line]
+    set add_completed_line_re [string_to_regexp $add_completed_line]
+
+    set expected_re [make_tab_completion_list_re $completion_list]
+
+    set test "tab complete \"$input_line\""
+    send_gdb "$input_line\t"
+    gdb_test_multiple "" "$test (first tab)" {
+	-re "^${input_line_re}$bell_re$add_completed_line_re$" {
+	    send_gdb "\t"
+	    # If we auto-completed to an ambiguous prefix, we need an
+	    # extra tab to show the matches list.
+	    if {$add_completed_line != ""} {
+		send_gdb "\t"
+	    }
+	    gdb_test_multiple "" "$test (second tab)" {
+		-re "$expected_re\r\n$gdb_prompt $input_line_re$add_completed_line_re$" {
+		    pass "$test"
+		}
+	    }
+	}
+    }
+
+    clear_input_line $test
+}
+
+# Test that completing LINE with the complete command completes to
+# nothing.
+
+proc test_gdb_complete_cmd_none { line } {
+    gdb_test_no_output "complete $line" "cmd complete \"$line\""
+}
+
+# Test that completing LINE with the complete command completes to
+# COMPLETE_LINE_RE.
+
+proc test_gdb_complete_cmd_unique { input_line complete_line_re } {
+    global gdb_prompt
+
+    set cmd "complete $input_line"
+    set cmd_re [string_to_regexp $cmd]
+    set test "cmd complete \"$input_line\""
+    gdb_test_multiple $cmd $test {
+	-re "^$cmd_re\r\n$complete_line_re\r\n$gdb_prompt $" {
+	    pass $test
+	}
+    }
+}
+
+# Test that completing "CMD_PREFIX + COMPLETION_WORD" with the
+# complete command displays the COMPLETION_LIST completion list.  Each
+# entry in the list should be prefixed by CMD_PREFIX.
+
+proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list start_quote_char end_quote_char } {
+    global gdb_prompt
+
+    set expected_re [make_cmd_completion_list_re $cmd_prefix $completion_list $start_quote_char $end_quote_char]
+    set cmd_re [string_to_regexp "complete $cmd_prefix$completion_word"]
+    set test "cmd complete \"$cmd_prefix$completion_word\""
+    gdb_test_multiple "complete $cmd_prefix$completion_word" $test {
+	-re "^$cmd_re\r\n$expected_re$gdb_prompt $" {
+	    pass $test
+	}
+    }
+}
+
+proc test_gdb_complete_menu { line expected_output } {
+
+    set test "menu complete $line"
+#    send_gdb "$expr\033?"
+#    send_gdb "$expr^\[?"
+    send_gdb "$expr"
+    send_gdb "\x1b"
+    send_gdb "?"
+    gdb_test_multiple "" "$test" {
+	-re "$expected_output" {
+	    pass "$test"
+	}
+    }
+}
+
+# Test that completing LINE completes to nothing.
+
+proc test_gdb_complete_none { input_line } {
+    test_gdb_complete_tab_none $input_line
+    test_gdb_complete_cmd_none $input_line
+}
+
+# Test that completing INPUT_LINE completes to COMPLETE_LINE.
+#
+# APPEND_CHAR is the character expected to be appended after
+# EXPECTED_OUTPUT when TAB completing.  Normally that's a whitespace,
+# but in some cases it's some other character, like a colon.
+#
+# If MAX_COMPLETIONS is true, then we expect the completion to hit the
+# max-completions limit.  Since we're expecting a unique completion
+# match, this will only be visible in the "complete" command output.
+# Tab completion will just auto-complete the only match and won't
+# display a match list.
+
+proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_completions 0}} {
+    set complete_line_re [string_to_regexp $complete_line]
+    set append_char_re [string_to_regexp $append_char]
+    test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re
+
+    # Trim INPUT_LINE and COMPLETE LINE, for the case we're completing
+    # a command with leading whitespace.  Leading command whitespace
+    # is discarded by GDB.
+    set input_line [string trimleft $input_line]
+    set expected_output_re [string trimleft $complete_line_re]
+    if {$append_char_re != " "} {
+	append expected_output_re $append_char_re
+    }
+    if {$max_completions} {
+	set max_completion_reached_msg \
+	    "*** List may be truncated, max-completions reached. ***"
+	set input_line_re \
+	    [string_to_regexp $input_line]
+	set max_completion_reached_msg_re \
+	    [string_to_regexp $max_completion_reached_msg]
+
+	append expected_output_re \
+	    "\r\n$input_line_re $max_completion_reached_msg_re"
+    }
+
+    test_gdb_complete_cmd_unique $input_line $expected_output_re
+}
+
+proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {
+    set append_char_re [string_to_regexp $append_char]
+    test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re
+
+    # Trim INPUT_LINE and COMPLETE LINE, for the case we're completing
+    # a command with leading whitespace.  Leading command whitespace
+    # is discarded by GDB.
+    set input_line [string trimleft $input_line]
+    set expected_output_re [string trimleft $complete_line_re]
+    if {$append_char_re != " "} {
+	append expected_output_re $append_char_re
+    }
+    if {$max_completions} {
+	set max_completion_reached_msg \
+	    "*** List may be truncated, max-completions reached. ***"
+	set input_line_re \
+	    [string_to_regexp $input_line]
+	set max_completion_reached_msg_re \
+	    [string_to_regexp $max_completion_reached_msg]
+
+	append expected_output_re \
+	    "\r\n$input_line_re $max_completion_reached_msg_re"
+    }
+
+    test_gdb_complete_cmd_unique $input_line $expected_output_re
+}
+
+# Test that completing "CMD_PREFIX + COMPLETION_WORD" adds
+# ADD_COMPLETED_LINE to the input line, and that it displays
+# COMPLETION_LIST as completion match list.  COMPLETION_WORD is the
+# completion word.
+
+proc test_gdb_complete_multiple { cmd_prefix completion_word add_completed_line completion_list {start_quote_char ""} {end_quote_char ""}} {
+    test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list
+    test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char
+}
+
+# Test completing all the substring prefixes of COMPLETION from
+# [0..START) to [0..END) complete to COMPLETION.  If END is ommitted,
+# default to the length of COMPLETION.
+
+proc test_complete_prefix_range {completion start {end -1}} {
+    if {$end == -1} {
+	set end [string length $completion]
+    }
+
+    for {set i $start} {$i < $end} {incr i} {
+	set line [string range $completion 0 $i]
+	test_gdb_complete_unique "$line" "$completion"
+    }
+}
+
+proc test_complete_prefix_range_input {input completion_re start {end -1}} {
+    if {$end == -1} {
+	set end [string length $input]
+    }
+
+    for {set i $start} {$i < $end} {incr i} {
+	set line [string range $input 0 $i]
+	test_gdb_complete_unique_re "$line" $completion_re
+    }
+}
+
+# Find NEEDLE in HAYSTACK and return the index _after_ NEEDLE.  E.g.,
+# searching for "(" in "foo(int)" returns 4, which would be useful if
+# you want to find the "(" to try completing "foo(".
+
+proc index_after {needle haystack} {
+    set start [string first $needle $haystack]
+    if {$start == -1} {
+	error "could not find \"$needle\" in \"$haystack\""
+    }
+    return [expr $start + [string length $needle]]
+}
+
+# Create a breakpoint using BREAK_COMMAND, and return the number of
+# locations found.
+
+proc create_bp {break_command} {
+    global gdb_prompt
+    global decimal hex
+
+    set found_locations -1
+
+    set test "set breakpoint"
+    gdb_test_multiple "$break_command" $test {
+	-re "\\\(\($decimal\) locations\\\)\r\n$gdb_prompt $" {
+	    set found_locations "$expect_out(1,string)"
+	}
+	-re "Breakpoint $decimal at $hex: file .*, line .*$gdb_prompt $" {
+	    set found_locations 1
+	}
+	-re "Make breakpoint pending on future shared library load.*y or .n.. $" {
+	    send_gdb "n\n"
+	    gdb_test_multiple "" "$test (prompt)" {
+		-re "$gdb_prompt $" {
+		}
+	    }
+	    set found_locations 0
+	}
+	-re "invalid explicit location argument, \[^\r\n\]*\r\n$gdb_prompt $" {
+	    set found_locations 0
+	}
+	-re "Function \[^\r\n\]* not defined in \[^\r\n\]*\r\n$gdb_prompt $" {
+	    set found_locations 0
+	}
+    }
+    return $found_locations
+}
+
+# Check that trying to create a breakpoint from linespec fails.
+
+proc check_setting_bp_fails {break_command} {
+    with_test_prefix "\"$break_command\" creates no bp locations" {
+	set found_locations [create_bp $break_command]
+	gdb_assert {$found_locations == 0} "matches"
+	if {$found_locations != 0} {
+	    delete_breakpoints
+	}
+    }
+}
+
+# Return true if lists A and B have the same elements.  Order of
+# elements does not matter.
+
+proc gdb_leq {a b} {
+    return [expr {[lsort $a] eq [lsort $b]}]
+}
+
+# Check that creating the breakpoint at LINESPEC finds the same
+# breakpoint locations as completing LINESPEC.  COMPLETION_LIST is
+# expected completion match list.
+
+proc check_bp_locations_match_list {break_command completion_list} {
+    global gdb_prompt
+    global hex
+
+    with_test_prefix "compare \"$break_command\" completion list with bp location list" {
+	set num_locations [create_bp $break_command]
+
+	set found_list ""
+
+	set any "\[^\r\n\]*"
+
+	gdb_test_multiple "info breakpoint \$bpnum" "info breakpoint" {
+	    -re "in \(\[^\r\n\]*\) at " {
+		# A function location.
+		set found_location "$expect_out(1,string)"
+		lappend found_list $found_location
+		exp_continue
+	    }
+	    -re "breakpoint${any}keep${any}y${any}$hex\[ \t]*\(${any}\)\r\n" {
+		# A label location.
+		set found_location "$expect_out(1,string)"
+		lappend found_list $found_location
+		exp_continue
+	    }
+	    -re "$gdb_prompt $" {
+	    }
+	}
+
+	gdb_assert {[gdb_leq $found_list $completion_list]} "matches"
+
+	delete_breakpoints
+    }
+}
+
+# Build linespec and explicit locations out of all the combinations of
+# SOURCES, FUNCTIONS and LABELS, with all combinations of possible
+# quoting and whitespace around separators, and run BODY_LINESPEC and
+# BODY_EXPLICIT in the context of the caller for each combination.  A
+# variable named "location" is set in the callers context with the
+# currently iterated location.
+
+proc foreach_location_functions { sources functions body_linespec body_explicit } {
+    global maybe_quoted_list
+    upvar source source
+    upvar function function
+    upvar source_sep source_sep
+    upvar location location
+
+    foreach source $sources {
+	# Test with and without source quoting.
+	foreach sqc $maybe_quoted_list {
+	    if {$source == "" && $sqc != ""} {
+		# Invalid combination.
+		continue
+	    }
+
+	    # Test with and without function quoting.
+	    foreach fqc $maybe_quoted_list {
+		# Test known and unknown functions.
+		foreach function $functions {
+		    # Linespec version.  Test with and without spacing
+		    # after the source/colon colon separator.
+		    foreach source_sep {"" ":" ": "} {
+			# Skip invalid combinations.
+			if {$source == "" && $source_sep != ""} {
+			    continue
+			}
+			if {$source != "" && $source_sep == ""} {
+			    continue
+			}
+
+			set location "${sqc}${source}${sqc}${source_sep}${fqc}$function${fqc}"
+			uplevel 1 $body_linespec
+		    }
+
+		    # Explicit locations version.
+		    if {$source != ""} {
+			set loc_src "-source ${sqc}${source}${sqc} "
+		    } else {
+			set loc_src ""
+		    }
+
+		    set location "${loc_src}-function ${fqc}$function${fqc}"
+		    uplevel 1 $body_explicit
+		}
+	    }
+	}
+    }
+}
+
+# Same as foreach_locations_functions, but also iterate over
+# combinations of labels.
+proc foreach_location_labels { sources functions labels body_linespec body_explicit } {
+    global maybe_quoted_list
+    upvar source source
+    upvar function function
+    upvar label label
+    upvar source_sep source_sep
+    upvar label_sep label_sep
+    upvar location location
+
+    # Test both with a known source file and without a source file
+    # component.
+    foreach_location_functions \
+	$sources \
+	$functions \
+	{
+	    # Linespec version.  Test various spacing around the label
+	    # colon separator.
+	    set saved_location ${location}
+	    foreach label_sep {":" " :" ": " " : "} {
+		# Test both known and unknown label.
+		foreach label $labels {
+		    set location "${saved_location}${label_sep}$label"
+		    uplevel 1 $body_linespec
+		}
+	    }
+	} \
+	{
+	    # Explicit locations version.
+	    set saved_location ${location}
+	    foreach label $labels {
+		set location "${saved_location} -label $label"
+		uplevel 1 $body_explicit
+	    }
+	}
+}
-- 
2.5.5

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

* [PATCH 15/40] Rewrite/enhance explicit locations completer, parse left->right
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (19 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 28/40] lookup_name_info::make_ignore_params Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-07-14 20:55   ` Keith Seitz
  2017-06-02 12:23 ` [PATCH 38/40] Use TOLOWER in SYMBOL_HASH_NEXT Pedro Alves
                   ` (19 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

One of the most annoying (to me) things about GDB's completion is when
you have overloads in your program, and you want to set a breakpoint
in one of them:

 void function(int);  // set breakpoint here.
 void function(long);

 (gdb) b -f func[TAB]
 (gdb) b -f function(       # ok, gdb completed as much as possible.
 (gdb) b -f function([TAB]  # show me the overloads, please.
 <_all_ symbols in the program are shown...>

E.g., when debugging GDB, that'd be:

 (gdb) b -f function([TAB]
 (anonymous namespace)::get_global()::global  pt_insn_get_offset@plt                       scm_new_port_table_entry
 asprintf                                     pt_pkt_alloc_decoder                         scm_new_port_table_entry@plt
 asprintf@plt                                 pt_pkt_alloc_decoder@plt                     scm_out_of_range
 bt_ctf_get_char_array                        pt_pkt_sync_forward                          scm_out_of_range@plt
 bt_ctf_get_char_array@plt                    pt_pkt_sync_forward@plt                      scm_putc
 bt_ctf_get_uint64                            pwrite                                       scm_putc@plt
 bt_ctf_get_uint64@plt                        pwrite@plt                                   scm_reverse_x
 bt_ctf_iter_read_event                       PyErr_Restore                                scm_reverse_x@plt
 bt_ctf_iter_read_event@plt                   PyErr_Restore@plt                            scm_set_port_filename_x
 <snip...>

Now that's a load of completely useless completions.

The reason GDB offers those is that the completer relies on readline
figuring out the completion word point in the input line based on the
language's word break characters, which include "(".  So readline
tells the completer to complete on "", the string that is after '('.
Likewise, if you type "function(i[TAB]" to try to complete to "int",
you're out of luck.  GDB shows you all the symbols in the program that
start with "i"...  This makes sense for the expression completer, as
what you'd want to type is e.g., a global variable, say:

(gdb) print function(i[TAB]

but, it makes no sense when specifying a function name for a
breakpoint location.

To get around that limitation, users need to quote the function name,
like:

 (gdb) b -f 'function([TAB]
 function(int)      function(long)
 (gdb) b 'function(i[TAB]
 (gdb) b 'function(int)' # now completes correctly!

Note that the quoting is only necessary for completion.  Creating the
breakpoint does not require the quoting:

 (gdb) b -f function(int) [RET]
 Breakpoint 1 at ....

This patch removes this limitation.

(
Actually, it's a necessary patch, though not sufficient.  That'll
start working correctly by the end of the series.  With this patch, if try it,
you'll see:

 (gdb) b -f function(i[TAB]
 (gdb) b -f function

i.e., gdb strips everything after the "(".  That's caused by some code
in symtab.c that'll be eliminated further down the series.  These
patches are all unfortunately interrelated, which is also the reason
new tests only appear much later in the series.
But let's ignore that reality for the remainder of the description.
)

So... this patch gets rid of the need for quoting.

It does that by adding a way for a completer to control the exact
completion word point that readline should start the completion
request for, instead of letting readline try to figure it out using
the current language's word break chars array, and often failing.

In the case above, we want the completer to figure out that it's
completing a function name that starts with "function(i".  It now
does.

It took me a while to figure out a way to ask readline to "use this
exact word point", and for a while I feared that it'd be impossible
with current readline (and having to rely on master readline for core
functionality is something I'd like to avoid very much).  Eventually,
after several different attempts, I came up with what is described in
the comment above gdb_custom_word_point_brkchars in the patch.

With this patch, the handle_brkchars phase of the explicit location
completer advances the expected word point as it parses the input line
left to right, until it figures out exactly what we're completing,
instead of expecting readline to break the string using the word break
characters, and then having the completer heuristically fix up a bad
decision by parsing the input string backwards.  This allows correctly
knowning that we're completing a symbol name after -function, complete
functions without quoting, etc.

Later, we'll make use of this same mechanims to implement a proper
linespec completer that avoids need for quoting too.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_collect_symbol_completion_matches): Add
	complete_symbol_mode parameter.
	* cli/cli-cmds.c (complete_command): Get the completion result out
	of the handle_brkchars tracker if used a custom word point.
	* completer.c: Include "linespec.h".
	(enum explicit_location_match_type) <MATCH_LINE>: New enumerator.
	(advance_to_expression_complete_word_point): New.
	(completion_tracker::completes_to_completion_word): New.
	(complete_files_symbols): Pass down
	complete_symbol_mode::EXPRESSION.
	(explicit_options, probe_options): New.
	(collect_explicit_location_matches): Complete on the
	explictit_loc->foo instead of word.  Use
	linespec_complete_function.  Handle MATCH_LINE.  Handle offering
	keyword and options completions.
	(backup_text_ptr): Delete.
	(skip_keyword): New.
	(complete_explicit_location): Remove 'word' parameter.  Add
	language, quoted_arg_start and quoted_arg_end parameters.
	Rewrite, parsing left to right.
	(location_completer): Rewrite.
	(location_completer_handle_brkchars): New function.
	(symbol_completer): Pass down complete_symbol_mode::EXPRESSION.
	(enum complete_line_internal_reason): Adjust comments.
	(completion_tracker::discard_completions): New.
	(completer_handle_brkchars_func_for_completer): Handle
	location_completer.
	(gdb_custom_word_point_brkchars)
	(gdb_org_rl_basic_quote_characters): New.
	(gdb_completion_word_break_characters_throw)
	(completion_find_completion_word): Handle trackers that use a
	custom word point.
	(completion_tracker::advance_custom_word_point_by): New.
	(completion_tracker::build_completion_result): Don't rely on
	readline appending the quote char.
	(gdb_rl_attempted_completion_function_throw): Handle trackers that
	use a custom word point.
	(gdb_rl_attempted_completion_function): Restore
	rl_basic_quote_characters.
	* completer.h (class completion_tracker): Extend intro comment.
	(completion_tracker::set_quote_char)
	(completion_tracker::quote_char)
	(completion_tracker::set_use_custom_word_point)
	(completion_tracker::use_custom_word_point)
	(completion_tracker::custom_word_point)
	(completion_tracker::set_custom_word_point)
	(completion_tracker::advance_custom_word_point_by)
	(completion_tracker::completes_to_completion_word)
	(completion_tracker::discard_completions): New methods.
	(completion_tracker::m_quote_char)
	(completion_tracker::m_use_custom_word_point)
	(completion_tracker::m_custom_word_point): New fields.
	(advance_to_expression_complete_word_point): Declare.
	* f-lang.c (f_collect_symbol_completion_matches): Add
	complete_symbol_mode parameter.
	* language.h (struct language_defn)
	<la_collect_symbol_completion_matches>: Add complete_symbol_mode
	parameter.
	* linespec.c (linespec_keywords): Add NULL terminator.  Make extern.
	(linespec_complete_function): New function.
	(linespec_lexer_lex_keyword): Adjust.
	* linespec.h (linespec_keywords, linespec_complete_function): New
	declarations.
	* location.c (find_end_quote): New function.
	(explicit_location_lex_one): Add explicit_completion_info
	parameter.  Save quoting info.  Don't throw if being called for
	completion.  Don't handle Ada operators here.
	(is_cp_operator, skip_op_false_positives, first_of)
	(explicit_location_lex_one_function): New function.
	(string_to_explicit_location): Replace 'dont_throw' parameter with
	an explicit_completion_info pointer parameter.  Handle it.  Don't
	use explicit_location_lex_one to lex function names.  Use
	explicit_location_lex_one_function instead.
	* location.h (struct explicit_completion_info): New.
	(string_to_explicit_location): Replace 'dont_throw' parameter with
	an explicit_completion_info pointer parameter.
	* symtab.c (default_collect_symbol_completion_matches_break_on):
	Add complete_symbol_mode parameter.  Handle LINESPEC mode.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches): Add complete_symbol_mode
	parameter.
	(collect_symbol_completion_matches_type): Pass down
	complete_symbol_mode::EXPRESSION.
	(collect_file_symbol_completion_matches): Add complete_symbol_mode
	parameter.  Handle LINESPEC mode.
	* symtab.h (complete_symbol_mode): New.
	(default_collect_symbol_completion_matches_break_on)
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches)
	(collect_file_symbol_completion_matches): Add complete_symbol_mode
	parameter.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/completion.exp: Adjust expected output.
	* gdb.linespec/ls-errs.exp (do_test): Adjust expected output.
---
 gdb/ada-lang.c                         |   1 +
 gdb/cli/cli-cmds.c                     |  17 +-
 gdb/completer.c                        | 565 ++++++++++++++++++++++++++-------
 gdb/completer.h                        |  82 ++++-
 gdb/f-lang.c                           |   3 +-
 gdb/language.h                         |   1 +
 gdb/linespec.c                         |  31 +-
 gdb/linespec.h                         |  10 +
 gdb/location.c                         | 303 ++++++++++++++++--
 gdb/location.h                         |  29 +-
 gdb/symtab.c                           |  19 +-
 gdb/symtab.h                           |  15 +
 gdb/testsuite/gdb.base/completion.exp  |   2 +-
 gdb/testsuite/gdb.linespec/ls-errs.exp |  18 +-
 14 files changed, 929 insertions(+), 167 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 0979cf8..ab54c64 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6500,6 +6500,7 @@ symbol_completion_add (completion_tracker &tracker,
 
 static void
 ada_collect_symbol_completion_matches (completion_tracker &tracker,
+				       complete_symbol_mode mode,
 				       const char *text0, const char *word,
 				       enum type_code code)
 {
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 7717577..59e2695 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -266,6 +266,7 @@ complete_command (char *arg_entry, int from_tty)
 
   completion_tracker tracker_handle_brkchars;
   completion_tracker tracker_handle_completions;
+  completion_tracker *tracker;
 
   int quote_char = '\0';
   const char *word;
@@ -275,8 +276,17 @@ complete_command (char *arg_entry, int from_tty)
       word = completion_find_completion_word (tracker_handle_brkchars,
 					      arg, &quote_char);
 
-      /* Completers must be called twice.  */
-      complete_line (tracker_handle_completions, word, arg, strlen (arg));
+      /* Completers that provide a custom word point in the
+	 handle_brkchars phase also compute their completions then.
+	 Completers that leave the completion word handling to readline
+	 must be called twice.  */
+      if (tracker_handle_brkchars.use_custom_word_point ())
+	tracker = &tracker_handle_brkchars;
+      else
+	{
+	  complete_line (tracker_handle_completions, word, arg, strlen (arg));
+	  tracker = &tracker_handle_completions;
+	}
     }
   CATCH (ex, RETURN_MASK_ALL)
     {
@@ -286,8 +296,7 @@ complete_command (char *arg_entry, int from_tty)
   std::string arg_prefix (arg, word - arg);
 
   completion_result result
-    = (tracker_handle_completions.build_completion_result
-       (word, word - arg, strlen (arg)));
+    = tracker->build_completion_result (word, word - arg, strlen (arg));
 
   if (result.number_matches != 0)
     {
diff --git a/gdb/completer.c b/gdb/completer.c
index a1d3a43..0473d8c 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -29,6 +29,7 @@
 #include "arch-utils.h"
 #include "location.h"
 #include <algorithm>
+#include "linespec.h"
 #include "cli/cli-decode.h"
 
 /* FIXME: This is needed because of lookup_cmd_1 ().  We should be
@@ -44,6 +45,9 @@
 
 #include "completer.h"
 
+static void complete_expression (completion_tracker &tracker,
+				 const char *text, const char *word);
+
 /* Misc state that needs to be tracked across several different
    readline completer entry point calls, all related to a single
    completion invocation.  */
@@ -63,8 +67,9 @@ struct gdb_completer_state
 /* The current completion state.  */
 static gdb_completer_state current_completion;
 
-/* An enumeration of the various things a user might
-   attempt to complete for a location.  */
+/* An enumeration of the various things a user might attempt to
+   complete for a location.  If you change this, remember to update
+   explicit_options array below too.  */
 
 enum explicit_location_match_type
 {
@@ -74,6 +79,9 @@ enum explicit_location_match_type
     /* The name of a function or method.  */
     MATCH_FUNCTION,
 
+    /* A line number.  */
+    MATCH_LINE,
+
     /* The name of a label.  */
     MATCH_LABEL
 };
@@ -372,6 +380,48 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
   return line_buffer + point;
 }
 
+/* See completer.h.  */
+
+const char *
+advance_to_expression_complete_word_point (completion_tracker &tracker,
+					   const char *text)
+{
+  gdb_rl_completion_word_info info;
+
+  info.word_break_characters
+    = current_language->la_word_break_characters ();
+  info.quote_characters = gdb_completer_quote_characters;
+  info.basic_quote_characters = rl_basic_quote_characters;
+
+  const char *start
+    = gdb_rl_find_completion_word (&info, NULL, NULL, text);
+
+  tracker.advance_custom_word_point_by (start - text);
+
+  return start;
+}
+
+/* See completer.h.  */
+
+bool
+completion_tracker::completes_to_completion_word (const char *word)
+{
+  if (m_lowest_common_denominator_unique)
+    {
+      const char *lcd = m_lowest_common_denominator;
+
+      if (strncmp_iw (word, lcd, strlen (lcd)) == 0)
+	{
+	  /* Maybe skip the function and complete on keywords.  */
+	  size_t wordlen = strlen (word);
+	  if (word[wordlen - 1] == ' ')
+	    return true;
+	}
+    }
+
+  return false;
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -456,7 +506,9 @@ complete_files_symbols (completion_tracker &tracker,
      symbols as well as on files.  */
   if (colon)
     {
-      collect_file_symbol_completion_matches (tracker, symbol_start, word,
+      collect_file_symbol_completion_matches (tracker,
+					      complete_symbol_mode::EXPRESSION,
+					      symbol_start, word,
 					      file_to_match);
       xfree (file_to_match);
     }
@@ -464,7 +516,9 @@ complete_files_symbols (completion_tracker &tracker,
     {
       size_t text_len = strlen (text);
 
-      collect_symbol_completion_matches (tracker, symbol_start, word);
+      collect_symbol_completion_matches (tracker,
+					 complete_symbol_mode::EXPRESSION,
+					 symbol_start, word);
       /* If text includes characters which cannot appear in a file
 	 name, they cannot be asking for completion on files.  */
       if (strcspn (text,
@@ -505,10 +559,32 @@ complete_files_symbols (completion_tracker &tracker,
       /* No completions at all.  As the final resort, try completing
 	 on the entire text as a symbol.  */
       collect_symbol_completion_matches (tracker,
+					 complete_symbol_mode::EXPRESSION,
 					 orig_text, word);
     }
 }
 
+/* The explicit location options.  Note that indexes into this array
+   must match the explicit_location_match_type enumerators.  */
+static const char *const explicit_options[] =
+  {
+    "-source",
+    "-function",
+    "-line",
+    "-label",
+    NULL
+  };
+
+/* The probe modifier options.  These can appear before a location in
+   breakpoint commands.  */
+static const char *const probe_options[] =
+  {
+    "-probe",
+    "-probe-stap",
+    "-probe-dtrace",
+    NULL
+  };
+
 /* Returns STRING if not NULL, the empty string otherwise.  */
 
 static const char *
@@ -524,18 +600,22 @@ static void
 collect_explicit_location_matches (completion_tracker &tracker,
 				   struct event_location *location,
 				   enum explicit_location_match_type what,
-				   const char *word)
+				   const char *word,
+				   const struct language_defn *language)
 {
   const struct explicit_location *explicit_loc
     = get_explicit_location (location);
 
+  /* Note, in the various MATCH_* below, we complete on
+     explicit_loc->foo instead of WORD, because only the former will
+     have already skipped past any quote char.  */
   switch (what)
     {
     case MATCH_SOURCE:
       {
 	const char *source = string_or_empty (explicit_loc->source_filename);
 	completion_list matches
-	  = make_source_files_completion_list (source, word);
+	  = make_source_files_completion_list (source, source);
 	tracker.add_completions (std::move (matches));
       }
       break;
@@ -543,18 +623,15 @@ collect_explicit_location_matches (completion_tracker &tracker,
     case MATCH_FUNCTION:
       {
 	const char *function = string_or_empty (explicit_loc->function_name);
-	if (explicit_loc->source_filename != NULL)
-	  {
-	    const char *filename = explicit_loc->source_filename;
-
-	    collect_file_symbol_completion_matches (tracker,
-						    function, word, filename);
-	  }
-       else
-	 collect_symbol_completion_matches (tracker, function, word);
+	linespec_complete_function (tracker, function,
+				    explicit_loc->source_filename);
       }
       break;
 
+    case MATCH_LINE:
+      /* Nothing to offer.  */
+      break;
+
     case MATCH_LABEL:
       /* Not supported.  */
       break;
@@ -562,127 +639,296 @@ collect_explicit_location_matches (completion_tracker &tracker,
     default:
       gdb_assert_not_reached ("unhandled explicit_location_match_type");
     }
+
+  if (tracker.completes_to_completion_word (word))
+    {
+      tracker.discard_completions ();
+      tracker.advance_custom_word_point_by (strlen (word));
+      complete_on_enum (tracker, explicit_options, "", "");
+      complete_on_enum (tracker, linespec_keywords, "", "");
+    }
+  else if (!tracker.have_completions ())
+    {
+      /* Maybe we have an unterminated linespec keyword at the tail of
+	 the string.  Try completing on that.  */
+      size_t wordlen = strlen (word);
+      const char *keyword = word + wordlen;
+
+      if (wordlen > 0 && keyword[-1] != ' ')
+	{
+	  while (keyword > word && *keyword != ' ')
+	    keyword--;
+	  /* Don't complete on keywords if we'd be completing on the
+	     whole explicit linespec option.  E.g., "b -function
+	     thr<tab>" should not complete to the "thread"
+	     keyword.  */
+	  if (keyword != word)
+	    {
+	      keyword = skip_spaces_const (keyword);
+
+	      tracker.advance_custom_word_point_by (keyword - word);
+	      complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+	    }
+	}
+      else if (wordlen > 0 && keyword[-1] == ' ')
+	{
+	  /* Assume that we're maybe past the explicit location
+	     argument, and we didn't manage to find any match because
+	     the user wants to create a pending breakpoint.  Offer the
+	     keyword and explicit location options as possible
+	     completions.  */
+	  tracker.advance_custom_word_point_by (keyword - word);
+	  complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+	  complete_on_enum (tracker, explicit_options, keyword, keyword);
+	}
+    }
 }
 
-/* A convenience macro to (safely) back up P to the previous word.  */
+/* If the next word in *TEXT_P is any of the keywords in KEYWORDS,
+   then advance both TEXT_P and the word point in the tracker past the
+   keyword and return the (0-based) index in the KEYWORDS array that
+   matched.  Otherwise, return -1.  */
 
-static const char *
-backup_text_ptr (const char *p, const char *text)
+static int
+skip_keyword (completion_tracker &tracker,
+	      const char * const *keywords, const char **text_p)
 {
-  while (p > text && isspace (*p))
-    --p;
-  for (; p > text && !isspace (p[-1]); --p)
-    ;
+  const char *text = *text_p;
+  const char *after = skip_to_space_const (text);
+  size_t len = after - text;
 
-  return p;
+  if (text[len] != ' ')
+    return -1;
+
+  int found = -1;
+  for (int i = 0; keywords[i] != NULL; i++)
+    {
+      if (strncmp (keywords[i], text, len) == 0)
+	{
+	  if (found == -1)
+	    found = i;
+	  else
+	    return -1;
+	}
+    }
+
+  if (found != -1)
+    {
+      tracker.advance_custom_word_point_by (len + 1);
+      text += len + 1;
+      *text_p = text;
+      return found;
+    }
+
+  return -1;
 }
 
 /* A completer function for explicit locations.  This function
-   completes both options ("-source", "-line", etc) and values.  */
+   completes both options ("-source", "-line", etc) and values.  If
+   completing a quoted string, then QUOTED_ARG_START and
+   QUOTED_ARG_END point to the quote characters.  LANGUAGE is the
+   current language.  */
 
 static void
 complete_explicit_location (completion_tracker &tracker,
 			    struct event_location *location,
-			    const char *text, const char *word)
+			    const char *text,
+			    const language_defn *language,
+			    const char *quoted_arg_start,
+			    const char *quoted_arg_end)
 {
-  const char *p;
-  VEC (char_ptr) *matches = NULL;
+  if (*text != '-')
+    return;
 
-  /* Find the beginning of the word.  This is necessary because
-     we need to know if we are completing an option name or value.  We
-     don't get the leading '-' from the completer.  */
-  p = backup_text_ptr (word, text);
+  int keyword = skip_keyword (tracker, explicit_options, &text);
 
-  if (*p == '-')
+  if (keyword == -1)
+    complete_on_enum (tracker, explicit_options, text, text);
+  else
     {
-      /* Completing on option name.  */
-      static const char *const keywords[] =
+      /* Completing on value.  */
+      enum explicit_location_match_type what
+	= (explicit_location_match_type) keyword;
+
+      if (quoted_arg_start != NULL && quoted_arg_end != NULL)
 	{
-	  "source",
-	  "function",
-	  "line",
-	  "label",
-	  NULL
-	};
+	  if (quoted_arg_end[1] == '\0')
+	    {
+	      /* If completing a quoted string with the cursor right
+		 at the terminating quote char, complete the
+		 completion word without interpretation, so that
+		 readline advances the cursor one whitespace past the
+		 quote, even if there's no match.  This makes these
+		 cases behave the same:
+
+		   before: "b -function function()"
+		   after:  "b -function function() "
+
+		   before: "b -function 'function()'"
+		   after:  "b -function 'function()' "
+
+		 and trusts the user in this case:
+
+		   before: "b -function 'not_loaded_function_yet()'"
+		   after:  "b -function 'not_loaded_function_yet()' "
+	      */
+	      gdb::unique_xmalloc_ptr<char> text_copy
+		(xstrdup (text));
+	      tracker.add_completion (std::move (text_copy));
+	    }
+	  else if (quoted_arg_end[1] == ' ')
+	    {
+	      /* We're maybe past the explicit location argument.
+		 Skip the argument without interpretion, assuming the
+		 user may want to create pending breakpoint.  Offer
+		 the keyword and explicit location options as possible
+		 completions.  */
+	      tracker.advance_custom_word_point_by (strlen (text));
+	      complete_on_enum (tracker, linespec_keywords, "", "");
+	      complete_on_enum (tracker, explicit_options, "", "");
+	    }
+	  return;
+	}
 
-      /* Skip over the '-'.  */
-      ++p;
+      /* Now gather matches  */
+      collect_explicit_location_matches (tracker, location, what, text,
+					 language);
+    }
+}
 
-      complete_on_enum (tracker, keywords, p, p);
-      return;
+/* A completer for locations.  */
+
+void
+location_completer (struct cmd_list_element *ignore,
+		    completion_tracker &tracker,
+		    const char *text, const char *word_entry)
+{
+  int found_probe_option = -1;
+
+  /* If we have a probe modifier, skip it.  This can only appear as
+     first argument.  Until we have a specific completer for probes,
+     falling back to the linespec completer for the remainder of the
+     line is better than nothing.  */
+  if (text[0] == '-' && text[1] == 'p')
+    found_probe_option = skip_keyword (tracker, probe_options, &text);
+
+  const char *option_text = text;
+  int saved_word_point = tracker.custom_word_point ();
+
+  const char *copy = text;
+
+  explicit_completion_info completion_info;
+  event_location_up location
+    = string_to_explicit_location (&copy, current_language,
+				   &completion_info);
+  if (completion_info.quoted_arg_start != NULL
+      && completion_info.quoted_arg_end == NULL)
+    {
+      /* Found an unbalanced quote.  */
+      tracker.set_quote_char (*completion_info.quoted_arg_start);
+      tracker.advance_custom_word_point_by (1);
     }
-  else
+
+  if (location != NULL)
     {
-      /* Completing on value (or unknown).  Get the previous word to see what
-	 the user is completing on.  */
-      size_t len, offset;
-      const char *new_word, *end;
-      enum explicit_location_match_type what;
-      struct explicit_location *explicit_loc
-	= get_explicit_location (location);
-
-      /* Backup P to the previous word, which should be the option
-	 the user is attempting to complete.  */
-      offset = word - p;
-      end = --p;
-      p = backup_text_ptr (p, text);
-      len = end - p;
-
-      if (strncmp (p, "-source", len) == 0)
+      if (*copy != '\0')
 	{
-	  what = MATCH_SOURCE;
-	  new_word = explicit_loc->source_filename + offset;
+	  tracker.advance_custom_word_point_by (copy - text);
+	  text = copy;
+
+	  /* We found a terminator at the tail end of the string,
+	     which means we're past the explicit location options.  We
+	     may have a keyword to complete on.  If we have a whole
+	     keyword, then complete whatever comes after as an
+	     expression.  This is mainly for the "if" keyword.  If the
+	     "thread" and "task" keywords gain their own completers,
+	     they should be used here.  */
+	  int keyword = skip_keyword (tracker, linespec_keywords, &text);
+
+	  if (keyword == -1)
+	    {
+	      complete_on_enum (tracker, linespec_keywords, text, text);
+	    }
+	  else
+	    {
+	      const char *word
+		= advance_to_expression_complete_word_point (tracker, text);
+	      complete_expression (tracker, text, word);
+	    }
 	}
-      else if (strncmp (p, "-function", len) == 0)
+      else
 	{
-	  what = MATCH_FUNCTION;
-	  new_word = explicit_loc->function_name + offset;
+	  tracker.advance_custom_word_point_by (completion_info.last_option
+						- text);
+	  text = completion_info.last_option;
+
+	  complete_explicit_location (tracker, location.get (), text,
+				      current_language,
+				      completion_info.quoted_arg_start,
+				      completion_info.quoted_arg_end);
+
 	}
-      else if (strncmp (p, "-label", len) == 0)
+    }
+  else
+    {
+      /* This is an address or linespec location.  */
+      if (*text == '*')
 	{
-	  what = MATCH_LABEL;
-	  new_word = explicit_loc->label_name + offset;
+	  tracker.advance_custom_word_point_by (1);
+	  text++;
+	  const char *word
+	    = advance_to_expression_complete_word_point (tracker, text);
+	  complete_expression (tracker, text, word);
 	}
       else
 	{
-	  /* The user isn't completing on any valid option name,
-	     e.g., "break -source foo.c [tab]".  */
-	  return;
+	  /* Fall back to the old linespec completer, for now.  */
+
+	  if (word_entry == NULL)
+	    {
+	     /* We're in the handle_brkchars phase.  */
+	      tracker.set_use_custom_word_point (false);
+	      return;
+	    }
+
+	  complete_files_symbols (tracker, text, word_entry);
 	}
+    }
 
-      /* If the user hasn't entered a search expression, e.g.,
-	 "break -function <TAB><TAB>", new_word will be NULL, but
-	 search routines require non-NULL search words.  */
-      if (new_word == NULL)
-	new_word = "";
+  /* Add matches for option names, if either:
 
-      /* Now gather matches  */
-      collect_explicit_location_matches (tracker, location, what, new_word);
+     - Some completer above found some matches, but the word point did
+       not advance (e.g., "b <tab>" finds all functions, or "b -<tab>"
+       matches all objc selectors), or;
+
+     - Some completer above advanced the word point, but found no
+       matches.
+  */
+  if ((text[0] == '-' || text[0] == '\0')
+      && (!tracker.have_completions ()
+	  || tracker.custom_word_point () == saved_word_point))
+    {
+      tracker.set_custom_word_point (saved_word_point);
+      text = option_text;
+
+      if (found_probe_option == -1)
+	complete_on_enum (tracker, probe_options, text, text);
+      complete_on_enum (tracker, explicit_options, text, text);
     }
 }
 
-/* A completer for locations.  */
+/* The corresponding completer_handle_brkchars
+   implementation.  */
 
-void
-location_completer (struct cmd_list_element *ignore,
-		    completion_tracker &tracker,
-		    const char *text, const char *word)
+static void
+location_completer_handle_brkchars (struct cmd_list_element *ignore,
+				    completion_tracker &tracker,
+				    const char *text,
+				    const char *word_ignored)
 {
-  const char *copy = text;
+  tracker.set_use_custom_word_point (true);
 
-  event_location_up location = string_to_explicit_location (&copy,
-							    current_language,
-							    1);
-  if (location != NULL)
-    complete_explicit_location (tracker, location.get (),
-				text, word);
-  else
-    {
-      /* This is an address or linespec location.
-	 Right now both of these are handled by the (old) linespec
-	 completer.  */
-      complete_files_symbols (tracker, text, word);
-    }
+  location_completer (ignore, tracker, text, NULL);
 }
 
 /* Helper for expression_completer which recursively adds field and
@@ -844,7 +1090,8 @@ symbol_completer (struct cmd_list_element *ignore,
 		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
-  collect_symbol_completion_matches (tracker, text, word);
+  collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+				     text, word);
 }
 
 /* Here are some useful test cases for completion.  FIXME: These
@@ -873,10 +1120,24 @@ symbol_completer (struct cmd_list_element *ignore,
 enum complete_line_internal_reason
 {
   /* Preliminary phase, called by gdb_completion_word_break_characters
-     function, is used to determine the correct set of chars that are
-     word delimiters depending on the current command in line_buffer.
-     No completion list should be generated; the return value should
-     be NULL.  This is checked by an assertion.  */
+     function, is used to either:
+
+     #1 - Determine the set of chars that are word delimiters
+	  depending on the current command in line_buffer.
+
+     #2 - Manually advance RL_POINT to the "word break" point instead
+	  of letting readline do it (based on too-simple character
+	  matching).
+
+     Simpler completers that just pass a brkchars array to readline
+     (#1 above) must defer generating the completions to the main
+     phase (below).  No completion list should be generated in this
+     phase.
+
+     OTOH, completers that manually advance the word point(#2 above)
+     must set "use_custom_word_point" in the tracker and generate
+     their completion in this phase.  Note that this is the convenient
+     thing to do since they'll be parsing the input line anyway.  */
   handle_brkchars,
 
   /* Main phase, called by complete_line function, is used to get the
@@ -1010,6 +1271,8 @@ complete_line_internal_1 (completion_tracker &tracker,
       p++;
     }
 
+  tracker.advance_custom_word_point_by (p - tmp_command);
+
   if (!c)
     {
       /* It is an unrecognized command.  So there are no
@@ -1189,6 +1452,22 @@ completion_tracker::completion_tracker ()
 				      NULL, xcalloc, xfree);
 }
 
+void
+completion_tracker::discard_completions ()
+{
+  xfree (m_lowest_common_denominator);
+  m_lowest_common_denominator = NULL;
+
+  m_lowest_common_denominator_unique = false;
+
+  m_entries_vec.clear ();
+
+  htab_delete (m_entries_hash);
+  m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+				      htab_hash_string, (htab_eq) streq,
+				      NULL, xcalloc, xfree);
+}
+
 completion_tracker::~completion_tracker ()
 {
   xfree (m_lowest_common_denominator);
@@ -1419,12 +1698,27 @@ completer_handle_brkchars_func_for_completer (completer_ftype *fn)
   if (fn == filename_completer)
     return filename_completer_handle_brkchars;
 
+  if (fn == location_completer)
+    return location_completer_handle_brkchars;
+
   if (fn == command_completer)
     return command_completer_handle_brkchars;
 
   return default_completer_handle_brkchars;
 }
 
+/* Used as brkchars when we want to tell readline we have a custom
+   word point.  We do that by making our rl_completion_word_break_hook
+   set RL_POINT to the desired word point, and return the character at
+   the word break point as the break char.  This is two bytes in order
+   to fit one break character plus the terminating null.  */
+static char gdb_custom_word_point_brkchars[2];
+
+/* Since rl_basic_quote_characters is not completer-specific, we save
+   its original value here, in order to be able to restore it in
+   gdb_rl_attempted_completion_function.  */
+static const char *gdb_org_rl_basic_quote_characters = rl_basic_quote_characters;
+
 /* Get the list of chars that are considered as word breaks
    for the current command.  */
 
@@ -1441,6 +1735,27 @@ gdb_completion_word_break_characters_throw ()
   complete_line_internal (tracker, NULL, rl_line_buffer,
 			  rl_point, handle_brkchars);
 
+  if (tracker.use_custom_word_point ())
+    {
+      gdb_assert (tracker.custom_word_point () > 0);
+      rl_point = tracker.custom_word_point () - 1;
+      gdb_custom_word_point_brkchars[0] = rl_line_buffer[rl_point];
+      rl_completer_word_break_characters = gdb_custom_word_point_brkchars;
+      rl_completer_quote_characters = NULL;
+
+      /* Clear this too, so that if we're completing a quoted string,
+	 readline doesn't consider the quote character a delimiter.
+	 If we didn't do this, readline would auto-complete {b
+	 'fun<tab>} to {'b 'function()'}, i.e., add the terminating
+	 \', but, it wouldn't append the separator space either, which
+	 is not desirable.  So instead we take care of appending the
+	 quote character to the LCD ourselves, in
+	 gdb_rl_attempted_completion_function.  Since this global is
+	 not just completer-specific, we'll restore it back to the
+	 default in gdb_rl_attempted_completion_function.  */
+      rl_basic_quote_characters = NULL;
+    }
+
   return rl_completer_word_break_characters;
 }
 
@@ -1476,6 +1791,13 @@ completion_find_completion_word (completion_tracker &tracker, const char *text,
 
   complete_line_internal (tracker, NULL, text, point, handle_brkchars);
 
+  if (tracker.use_custom_word_point ())
+    {
+      gdb_assert (tracker.custom_word_point () > 0);
+      *quote_char = tracker.quote_char ();
+      return text + tracker.custom_word_point ();
+    }
+
   gdb_rl_completion_word_info info;
 
   info.word_break_characters = rl_completer_word_break_characters;
@@ -1517,6 +1839,12 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
     }
 }
 
+void
+completion_tracker::advance_custom_word_point_by (size_t len)
+{
+  m_custom_word_point += len;
+}
+
 /* Build a new C string that is a copy or LCD with the whitespace of
    ORIG/ORIG_LEN preserved.
 
@@ -1602,6 +1930,13 @@ completion_tracker::build_completion_result (const char *text,
 
   if (m_lowest_common_denominator_unique)
     {
+      /* We don't rely on readline appending the quote char as
+	 delimiter as then readline wouldn't append the ' ' after the
+	 completion.  */
+      char buf[2] = { quote_char () };
+
+      match_list[0] = reconcat (match_list[0], match_list[0],
+				buf, (char *) NULL);
       match_list[1] = NULL;
 
       /* If we already have a space at the end of the match, tell
@@ -1720,14 +2055,20 @@ completion_result::reset_match_list ()
 static char **
 gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
 {
-  /* Completers must be called twice.  If rl_point (i.e., END) is at
-     column 0, then readline skips the the handle_brkchars phase, and
-     so we create a tracker now in that case too.  */
-  delete current_completion.tracker;
-  current_completion.tracker = new completion_tracker ();
+  /* Completers that provide a custom word point in the
+     handle_brkchars phase also compute their completions then.
+     Completers that leave the completion word handling to readline
+     must be called twice.  If rl_point (i.e., END) is at column 0,
+     then readline skips the the handle_brkchars phase, and so we
+     create a tracker now in that case too.  */
+  if (end == 0 || !current_completion.tracker->use_custom_word_point ())
+    {
+      delete current_completion.tracker;
+      current_completion.tracker = new completion_tracker ();
 
-  complete_line (*current_completion.tracker, text,
-		 rl_line_buffer, rl_point);
+      complete_line (*current_completion.tracker, text,
+		     rl_line_buffer, rl_point);
+    }
 
   completion_tracker &tracker = *current_completion.tracker;
 
@@ -1745,6 +2086,10 @@ gdb_rl_attempted_completion_function_throw (const char *text, int start, int end
 char **
 gdb_rl_attempted_completion_function (const char *text, int start, int end)
 {
+  /* Restore globals that might have been tweaked in
+     gdb_completion_word_break_characters.  */
+  rl_basic_quote_characters = gdb_org_rl_basic_quote_characters;
+
   /* If we end up returning NULL, either on error, or simple because
      there are no matches, inhibit readline's default filename
      completer.  */
diff --git a/gdb/completer.h b/gdb/completer.h
index 207781d..eab9c69 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -133,6 +133,12 @@ public:
      up in the event the user requests to complete on something vague
      that necessitates the time consuming expansion of many symbol
      tables.
+
+   - The custom word point to hand over to readline, for completers
+     that parse the input string in order to dynamically adjust
+     themselves depending on exactly what they're completing.  E.g.,
+     the linespec completer needs to bypass readline's too-simple word
+     breaking algorithm.
 */
 class completion_tracker
 {
@@ -153,10 +159,52 @@ public:
      LIST.  */
   void add_completions (completion_list &&list);
 
+  /* Set the quote char to be appended after a unique completion is
+     added to the input line.  Set to '\0' to clear.  See
+     m_quote_char's description.  */
+  void set_quote_char (int quote_char)
+  { m_quote_char = quote_char; }
+
+  /* The quote char to be appended after a unique completion is added
+     to the input line.  Returns '\0' if no quote char has been set.
+     See m_quote_char's description.  */
+  int quote_char () { return m_quote_char; }
+
+  /* Tell the tracker that the current completer wants to provide a
+     custom word point instead of a list of a break chars, in the
+     handle_brkchars phase.  Such completers must also compute their
+     completions then.  */
+  void set_use_custom_word_point (bool enable)
+  { m_use_custom_word_point = enable; }
+
+  /* Whether the current completer computes a custom word point.  */
+  bool use_custom_word_point () const
+  { return m_use_custom_word_point; }
+
+  /* The custom word point.  */
+  int custom_word_point () const
+  { return m_custom_word_point; }
+
+  /* Set the custom word point to POINT.  */
+  void set_custom_word_point (int point)
+  { m_custom_word_point = point; }
+
+  /* Advance the custom word point by LEN.  */
+  void advance_custom_word_point_by (size_t len);
+
+  /* Return true if we only have one completion, and it matches
+     exactly the completion word.  I.e., completing results in what we
+     already have.  */
+  bool completes_to_completion_word (const char *word);
+
   /* True if we have any completion match recorded.  */
   bool have_completions () const
   { return !m_entries_vec.empty (); }
 
+  /* Discard the current completion match list and the current
+     LCD.  */
+  void discard_completions ();
+
   /* Build a completion_result containing the list of completion
      matches to hand over to readline.  The parameters are as in
      rl_attempted_completion_function.  */
@@ -185,7 +233,30 @@ private:
      searching too early.  */
   htab_t m_entries_hash;
 
-  /* Our idea of lowest common denominator to hand over to readline.  */
+  /* If non-zero, then this is the quote char that needs to be
+     appended after completion (iff we have a unique completion).  We
+     don't rely on readline appending the quote char as delimiter as
+     then readline wouldn't append the ' ' after the completion.
+     I.e., we want this:
+
+      before tab: "b 'function("
+      after tab:  "b 'function()' "
+  */
+  int m_quote_char = '\0';
+
+  /* If true, the completer has its own idea of "word" point, and
+     doesn't want to rely on readline computing it based on brkchars.
+     Set in the handle_brkchars phase.  */
+  bool m_use_custom_word_point = false;
+
+  /* The completer's idea of where the "word" we were looking at is
+     relative to RL_LINE_BUFFER.  This is advanced in the
+     handle_brkchars phase as the completer discovers potential
+     completable words.  */
+  int m_custom_word_point = 0;
+
+  /* Our idea of lowest common denominator to hand over to readline.
+     See intro.  */
   char *m_lowest_common_denominator = NULL;
 
   /* If true, the LCD is unique.  I.e., all completion candidates had
@@ -207,6 +278,15 @@ extern const char *completion_find_completion_word (completion_tracker &tracker,
 						    const char *text,
 						    int *quote_char);
 
+
+/* Assuming TEXT is an expression in the current language, find the
+   completion word point for TEXT, emulating the algorithm readline
+   uses to find the word point, using the current language's word
+   break characters.  */
+
+const char *advance_to_expression_complete_word_point
+  (completion_tracker &tracker, const char *text);
+
 extern char **gdb_rl_attempted_completion_function (const char *text,
 						    int start, int end);
 
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index e1184ee..937ebff 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -230,10 +230,11 @@ f_word_break_characters (void)
 
 static void
 f_collect_symbol_completion_matches (completion_tracker &tracker,
+				     complete_symbol_mode mode,
 				     const char *text, const char *word,
 				     enum type_code code)
 {
-  default_collect_symbol_completion_matches_break_on (tracker,
+  default_collect_symbol_completion_matches_break_on (tracker, mode,
 						      text, word, ":", code);
 }
 
diff --git a/gdb/language.h b/gdb/language.h
index 7ce4f7f..75b9438 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -330,6 +330,7 @@ struct language_defn
        symbols whose type has a code of CODE should be matched.  */
     void (*la_collect_symbol_completion_matches)
       (completion_tracker &tracker,
+       complete_symbol_mode mode,
        const char *text,
        const char *word,
        enum type_code code);
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 25ebdca..20ac71d 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -216,9 +216,9 @@ enum ls_token_type
 };
 typedef enum ls_token_type linespec_token_type;
 
-/* List of keywords  */
-
-static const char * const linespec_keywords[] = { "if", "thread", "task" };
+/* List of keywords.  This is NULL-terminated so that it can be used
+   as enum completer.  */
+const char * const linespec_keywords[] = { "if", "thread", "task", NULL };
 #define IF_KEYWORD_INDEX 0
 
 /* A token of the linespec lexer  */
@@ -400,7 +400,7 @@ linespec_lexer_lex_keyword (const char *p)
 
   if (p != NULL)
     {
-      for (i = 0; i < ARRAY_SIZE (linespec_keywords); ++i)
+      for (i = 0; linespec_keywords[i] != NULL; ++i)
 	{
 	  int len = strlen (linespec_keywords[i]);
 
@@ -421,7 +421,7 @@ linespec_lexer_lex_keyword (const char *p)
 		{
 		  p += len;
 		  p = skip_spaces_const (p);
-		  for (j = 0; j < ARRAY_SIZE (linespec_keywords); ++j)
+		  for (j = 0; linespec_keywords[j] != NULL; ++j)
 		    {
 		      int nextlen = strlen (linespec_keywords[j]);
 
@@ -1714,7 +1714,7 @@ linespec_parse_basic (linespec_parser *parser)
 	      if (token.type != LSTOKEN_NUMBER)
 		unexpected_linespec_error (parser);
 
-	      /* Record the lione offset and get the next token.  */
+	      /* Record the line offset and get the next token.  */
 	      name = copy_token_string (token);
 	      cleanup = make_cleanup (xfree, name);
 
@@ -2451,6 +2451,25 @@ linespec_lex_to_end (char **stringp)
   do_cleanups (cleanup);
 }
 
+/* See linespec.h.  */
+
+void
+linespec_complete_function (completion_tracker &tracker,
+			    const char *function,
+			    const char *source_filename)
+{
+  complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
+
+  if (source_filename != NULL)
+    {
+      collect_file_symbol_completion_matches (tracker, mode,
+					      function, function,
+					      source_filename);
+    }
+  else
+    collect_symbol_completion_matches (tracker, mode, function, function);
+}
+
 /* A helper function for decode_line_full and decode_line_1 to
    turn LOCATION into symtabs_and_lines.  */
 
diff --git a/gdb/linespec.h b/gdb/linespec.h
index f1244c4..d55ba12 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -184,6 +184,16 @@ extern const char *find_toplevel_char (const char *s, char c);
 
 extern void linespec_lex_to_end (char **stringp);
 
+extern const char * const linespec_keywords[];
+
+/* Complete a function symbol, in linespec mode.  If SOURCE_FILENAME
+   is non-NULL, limits completion to the list of functions defined in
+   source files that match SOURCE_FILENAME.  */
+
+extern void linespec_complete_function (completion_tracker &tracker,
+					const char *function,
+					const char *source_filename);
+
 /* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
    advancing EXP_PTR past any parsed text.  */
 
diff --git a/gdb/location.c b/gdb/location.c
index d711d7b..19d3232 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -415,13 +415,44 @@ event_location_to_string (struct event_location *location)
   return EL_STRING (location);
 }
 
+/* Find an instance of the quote character C in the string S that is
+   outside of all single- and double-quoted strings (i.e., any quoting
+   other than C).  */
+
+static const char *
+find_end_quote (const char *s, char end_quote_char)
+{
+  /* zero if we're not in quotes;
+     '"' if we're in a double-quoted string;
+     '\'' if we're in a single-quoted string.  */
+  char nested_quote_char = '\0';
+
+  for (const char *scan = s; *scan != '\0'; scan++)
+    {
+      if (nested_quote_char != '\0')
+	{
+	  if (*scan == nested_quote_char)
+	    nested_quote_char = '\0';
+	  else if (scan[0] == '\\' && *(scan + 1) != '\0')
+	    scan++;
+	}
+      else if (*scan == end_quote_char && nested_quote_char == '\0')
+	return scan;
+      else if (*scan == '"' || *scan == '\'')
+	nested_quote_char = *scan;
+    }
+
+  return 0;
+}
+
 /* A lexer for explicit locations.  This function will advance INP
    past any strings that it lexes.  Returns a malloc'd copy of the
    lexed string or NULL if no lexing was done.  */
 
 static gdb::unique_xmalloc_ptr<char>
 explicit_location_lex_one (const char **inp,
-			   const struct language_defn *language)
+			   const struct language_defn *language,
+			   explicit_completion_info *completion_info)
 {
   const char *start = *inp;
 
@@ -431,21 +462,27 @@ explicit_location_lex_one (const char **inp,
   /* If quoted, skip to the ending quote.  */
   if (strchr (get_gdb_linespec_parser_quote_characters (), *start))
     {
-      char quote_char = *start;
+      if (completion_info != NULL)
+	completion_info->quoted_arg_start = start;
 
-      /* If the input is not an Ada operator, skip to the matching
-	 closing quote and return the string.  */
-      if (!(language->la_language == language_ada
-	    && quote_char == '\"' && is_ada_operator (start)))
-	{
-	  const char *end = find_toplevel_char (start + 1, quote_char);
+      const char *end = find_end_quote (start + 1, *start);
 
-	  if (end == NULL)
+      if (end == NULL)
+	{
+	  if (completion_info == NULL)
 	    error (_("Unmatched quote, %s."), start);
-	  *inp = end + 1;
+
+	  end = start + strlen (start);
+	  *inp = end;
 	  return gdb::unique_xmalloc_ptr<char> (savestring (start + 1,
-							    *inp - start - 2));
+							    *inp - start - 1));
 	}
+
+      if (completion_info != NULL)
+	completion_info->quoted_arg_end = end;
+      *inp = end + 1;
+      return gdb::unique_xmalloc_ptr<char> (savestring (start + 1,
+							*inp - start - 2));
     }
 
   /* If the input starts with '-' or '+', the string ends with the next
@@ -486,12 +523,180 @@ explicit_location_lex_one (const char **inp,
   return NULL;
 }
 
+/* Return true if COMMA points past "operator".  START is the start of
+   the line that COMMAND points to, hence when reading backwards, we
+   must not read any character before START.  */
+
+static bool
+is_cp_operator (const char *start, const char *comma)
+{
+  if (comma != NULL
+      && (comma - start) >= CP_OPERATOR_LEN)
+    {
+      const char *p = comma;
+
+      while (p > start && isspace (p[-1]))
+	p--;
+      if (p - start >= CP_OPERATOR_LEN)
+	{
+	  p-= CP_OPERATOR_LEN;
+	  if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+	      && (p == start
+		  || !(isalnum (p[-1]) || p[-1] == '_')))
+	    {
+	      return true;
+	    }
+	}
+    }
+  return false;
+}
+
+/* When scanning the input string looking for the next explicit
+   location option/delimiter, we jump to the next option by looking
+   for ",", and "-".  Such a character can also appear in C++ symbols
+   like "operator," and "operator-".  So when we find such a
+   character, we call this function to check if we found such a
+   symbol, meaning we had a false positive for an option string.  In
+   that case, we keep looking for the next delimiter, until we find
+   one that is not a false positive, or we reach end of string.  FOUND
+   is the character that scanning found (either '-' or ','), and START
+   is the start of the line that FOUND points to, hence when reading
+   backwards, we must not read any character before START.  Returns a
+   pointer to the next non-false-positive delimiter character, or NULL
+   if none was found.  */
+
+static const char *
+skip_op_false_positives (const char *start, const char *found)
+{
+  while (found != NULL && is_cp_operator (start, found))
+    {
+      if (found[0] == '-' && found[1] == '-')
+	start = found + 2;
+      else
+	start = found + 1;
+      found = find_toplevel_char (start, *found);
+    }
+
+  return found;
+}
+
+/* Assuming both FIRST and NEW_TOK point into the same string, return
+   the pointer that is closer to the start of the string.  If FIRST is
+   NULL, returns NEW_TOK.  If NEW_TOK is NULL, returns FIRST.  */
+
+static const char *
+first_of (const char *first, const char *new_tok)
+{
+  if (first == NULL)
+    return new_tok;
+  else if (new_tok != NULL && new_tok < first)
+    return new_tok;
+  else
+    return first;
+}
+
+/* A lexer for functions in explicit locations.  This function will
+   advance INP past a function until the next option, or until end of
+   string.  Returns a malloc'd copy of the lexed string or NULL if no
+   lexing was done.  */
+
+static gdb::unique_xmalloc_ptr<char>
+explicit_location_lex_one_function (const char **inp,
+				    const struct language_defn *language,
+				    explicit_completion_info *completion_info)
+{
+  const char *start = *inp;
+
+  if (*start == '\0')
+    return NULL;
+
+  /* If quoted, skip to the ending quote.  */
+  if (strchr (get_gdb_linespec_parser_quote_characters (), *start))
+    {
+      char quote_char = *start;
+
+      /* If the input is not an Ada operator, skip to the matching
+	 closing quote and return the string.  */
+      if (!(language->la_language == language_ada
+	    && quote_char == '\"' && is_ada_operator (start)))
+	{
+	  if (completion_info != NULL)
+	    completion_info->quoted_arg_start = start;
+
+	  const char *end = find_toplevel_char (start + 1, quote_char);
+
+	  if (end == NULL)
+	    {
+	      if (completion_info == NULL)
+		error (_("Unmatched quote, %s."), start);
+
+	      end = start + strlen (start);
+	      *inp = end;
+	      char *saved = savestring (start + 1, *inp - start - 1);
+	      return gdb::unique_xmalloc_ptr<char> (saved);
+	    }
+
+	  if (completion_info != NULL)
+	    completion_info->quoted_arg_end = end;
+	  *inp = end + 1;
+	  char *saved = savestring (start + 1, *inp - start - 2);
+	  return gdb::unique_xmalloc_ptr<char> (saved);
+	}
+    }
+
+  const char *comma = find_toplevel_char (start, ',');
+
+  /* If we have "-function -myfunction", or perhaps better example,
+     "-function -[BasicClass doIt]" (objc selector), treat
+     "-myfunction" as the function name.  I.e., skip the first char if
+     it is an hyphen.  Don't skip the first char always, because we
+     may have C++ "operator<", and find_toplevel_char needs to see the
+     'o' in that case.  */
+  const char *hyphen
+    = (*start == '-'
+       ? find_toplevel_char (start + 1, '-')
+       : find_toplevel_char (start, '-'));
+
+  /* Check for C++ "operator," and "operator-".  */
+  comma = skip_op_false_positives (start, comma);
+  hyphen = skip_op_false_positives (start, hyphen);
+
+  /* Pick the one that appears first.  */
+  const char *end = first_of (hyphen, comma);
+
+  /* See if a linespec keyword appears first.  */
+  const char *s = start;
+  const char *ws = find_toplevel_char (start, ' ');
+  while (ws != NULL && linespec_lexer_lex_keyword (ws + 1) == NULL)
+    {
+      s = ws + 1;
+      ws = find_toplevel_char (s, ' ');
+    }
+  if (ws != NULL)
+    end = first_of (end, ws + 1);
+
+  /* If we don't have any terminator, then take the whole string.  */
+  if (end == NULL)
+    end = start + strlen (start);
+
+  /* Trim whitespace at the end.  */
+  while (end > start && end[-1] == ' ')
+    end--;
+
+  *inp = end;
+
+  if (*inp - start > 0)
+    return gdb::unique_xmalloc_ptr<char> (savestring (start, *inp - start));
+
+  return NULL;
+}
+
 /* See description in location.h.  */
 
 event_location_up
 string_to_explicit_location (const char **argp,
 			     const struct language_defn *language,
-			     int dont_throw)
+			     explicit_completion_info *completion_info)
 {
   event_location_up location;
 
@@ -514,6 +719,14 @@ string_to_explicit_location (const char **argp,
       int len;
       const char *start;
 
+      /* Clear these on each iteration, since they should be filled
+	 with info about the last option.  */
+      if (completion_info != NULL)
+	{
+	  completion_info->quoted_arg_start = NULL;
+	  completion_info->quoted_arg_end = NULL;
+	}
+
       /* If *ARGP starts with a keyword, stop processing
 	 options.  */
       if (linespec_lexer_lex_keyword (*argp) != NULL)
@@ -522,40 +735,68 @@ string_to_explicit_location (const char **argp,
       /* Mark the start of the string in case we need to rewind.  */
       start = *argp;
 
+      if (completion_info != NULL)
+	completion_info->last_option = start;
+
       /* Get the option string.  */
       gdb::unique_xmalloc_ptr<char> opt
-	= explicit_location_lex_one (argp, language);
+	= explicit_location_lex_one (argp, language, NULL);
 
-      *argp = skip_spaces_const (*argp);
+      /* Use the length of the option to allow abbreviations.  */
+      len = strlen (opt.get ());
 
       /* Get the argument string.  */
-      gdb::unique_xmalloc_ptr<char> oarg
-	= explicit_location_lex_one (argp, language);
-      bool have_oarg = oarg != NULL;
       *argp = skip_spaces_const (*argp);
 
-      /* Use the length of the option to allow abbreviations.  */
-      len = strlen (opt.get ());
+      /* All options have a required argument.  Checking for this
+	 required argument is deferred until later.  */
+      gdb::unique_xmalloc_ptr<char> oarg;
+      /* True if we have an argument.  This is required because we'll
+	 move from OARG before checking whether we have an
+	 argument.  */
+      bool have_oarg = false;
+
+      /* Convenience to consistently set both OARG/HAVE_OARG from
+	 ARG.  */
+      auto set_oarg = [&] (gdb::unique_xmalloc_ptr<char> arg)
+	{
+	  oarg = std::move (arg);
+	  have_oarg = oarg != NULL;
+	};
 
-      /* All options have a required argument.  Checking for this required
-	 argument is deferred until later.  */
       if (strncmp (opt.get (), "-source", len) == 0)
-	EL_EXPLICIT (location)->source_filename = oarg.release ();
+	{
+	  set_oarg (explicit_location_lex_one (argp, language,
+					       completion_info));
+	  EL_EXPLICIT (location)->source_filename = oarg.release ();
+	}
       else if (strncmp (opt.get (), "-function", len) == 0)
-	EL_EXPLICIT (location)->function_name = oarg.release ();
+	{
+	  set_oarg (explicit_location_lex_one_function (argp, language,
+							completion_info));
+	  EL_EXPLICIT (location)->function_name = oarg.release ();
+	}
       else if (strncmp (opt.get (), "-line", len) == 0)
 	{
+	  set_oarg (explicit_location_lex_one (argp, language, NULL));
+	  *argp = skip_spaces_const (*argp);
 	  if (have_oarg)
-	    EL_EXPLICIT (location)->line_offset
-	      = linespec_parse_line_offset (oarg.get ());
+	    {
+	      EL_EXPLICIT (location)->line_offset
+		= linespec_parse_line_offset (oarg.get ());
+	      continue;
+	    }
 	}
       else if (strncmp (opt.get (), "-label", len) == 0)
-	EL_EXPLICIT (location)->label_name = oarg.release ();
+	{
+	  set_oarg (explicit_location_lex_one (argp, language, completion_info));
+	  EL_EXPLICIT (location)->label_name = oarg.release ();
+	}
       /* Only emit an "invalid argument" error for options
 	 that look like option strings.  */
       else if (opt.get ()[0] == '-' && !isdigit (opt.get ()[1]))
 	{
-	  if (!dont_throw)
+	  if (completion_info == NULL)
 	    error (_("invalid explicit location argument, \"%s\""), opt.get ());
 	}
       else
@@ -567,11 +808,13 @@ string_to_explicit_location (const char **argp,
 	  return location;
 	}
 
+      *argp = skip_spaces_const (*argp);
+
       /* It's a little lame to error after the fact, but in this
 	 case, it provides a much better user experience to issue
 	 the "invalid argument" error before any missing
 	 argument error.  */
-      if (!have_oarg && !dont_throw)
+      if (!have_oarg && completion_info == NULL)
 	error (_("missing argument for \"%s\""), opt.get ());
     }
 
@@ -581,7 +824,7 @@ string_to_explicit_location (const char **argp,
       && EL_EXPLICIT (location)->function_name == NULL
       && EL_EXPLICIT (location)->label_name == NULL
       && (EL_EXPLICIT (location)->line_offset.sign == LINE_OFFSET_UNKNOWN)
-      && !dont_throw)
+      && completion_info == NULL)
     {
       error (_("Source filename requires function, label, or "
 	       "line offset."));
@@ -639,7 +882,7 @@ string_to_event_location (char **stringp,
 
   /* Try an explicit location.  */
   orig = arg = *stringp;
-  event_location_up location = string_to_explicit_location (&arg, language, 0);
+  event_location_up location = string_to_explicit_location (&arg, language, NULL);
   if (location != NULL)
     {
       /* It was a valid explicit location.  Advance STRINGP to
diff --git a/gdb/location.h b/gdb/location.h
index 7e1f012..58536e0 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -218,20 +218,37 @@ extern event_location_up
   string_to_event_location_basic (char **argp,
 				  const struct language_defn *language);
 
+/* Structure filled in by string_to_explicit_location to aid the
+   completer.  */
+struct explicit_completion_info
+{
+  /* Pointer to the last option found.  E.g., in "b -sou src.c -fun
+     func", LAST_OPTION is left pointing at "-fun func".  */
+  const char *last_option = NULL;
+
+  /* These point to the start and end of a quoted argument, iff the
+     last argument was quoted.  If parsing finds an incomplete quoted
+     string (e.g., "break -function 'fun"), then QUOTED_ARG_START is
+     set to point to the opening \', and QUOTED_ARG_END is left NULL.
+     If the last option is not quoted, then both are set to NULL. */
+  const char *quoted_arg_start = NULL;
+  const char *quoted_arg_end = NULL;
+};
+
 /* Attempt to convert the input string in *ARGP into an explicit location.
    ARGP is advanced past any processed input.  Returns an event_location
    (malloc'd) if an explicit location was successfully found in *ARGP,
    NULL otherwise.
 
-   IF !DONT_THROW, this function may call error() if *ARGP looks like
-   properly formed input, e.g., if it is called with missing argument
-   parameters or invalid options.  If DONT_THROW is non-zero, this function
-   will not throw any exceptions.  */
+   If COMPLETION_INFO is NULL, this function may call error() if *ARGP
+   looks like improperly formed input, e.g., if it is called with
+   missing argument parameters or invalid options.  If COMPLETION_INFO
+   is not NULL, this function will not throw any exceptions.  */
 
 extern event_location_up
   string_to_explicit_location (const char **argp,
-			       const struct language_defn *langauge,
-			       int dont_throw);
+			       const struct language_defn *language,
+			       explicit_completion_info *completion_info);
 
 /* A convenience function for testing for unset locations.  */
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 91e8b90..7c7ff7f 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4995,6 +4995,7 @@ add_symtab_completions (struct compunit_symtab *cust,
 void
 default_collect_symbol_completion_matches_break_on
   (completion_tracker &tracker,
+   complete_symbol_mode mode,
    const char *text, const char *word,
    const char *break_on, enum type_code code)
 {
@@ -5013,8 +5014,12 @@ default_collect_symbol_completion_matches_break_on
   const char *sym_text;
   /* Length of sym_text.  */
   int sym_text_len;
+  struct cleanup *cleanups;
 
   /* Now look for the symbol we are supposed to complete on.  */
+  if (mode == complete_symbol_mode::LINESPEC)
+    sym_text = text;
+  else
   {
     const char *p;
     char quote_found;
@@ -5220,10 +5225,11 @@ default_collect_symbol_completion_matches_break_on
 
 void
 default_collect_symbol_completion_matches (completion_tracker &tracker,
+					   complete_symbol_mode mode,
 					   const char *text, const char *word,
 					   enum type_code code)
 {
-  return default_collect_symbol_completion_matches_break_on (tracker,
+  return default_collect_symbol_completion_matches_break_on (tracker, mode,
 							     text, word, "",
 							     code);
 }
@@ -5234,9 +5240,10 @@ default_collect_symbol_completion_matches (completion_tracker &tracker,
 
 void
 collect_symbol_completion_matches (completion_tracker &tracker,
+				   complete_symbol_mode mode,
 				   const char *text, const char *word)
 {
-  current_language->la_collect_symbol_completion_matches (tracker,
+  current_language->la_collect_symbol_completion_matches (tracker, mode,
 							  text, word,
 							  TYPE_CODE_UNDEF);
 }
@@ -5249,10 +5256,12 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 					const char *text, const char *word,
 					enum type_code code)
 {
+  complete_symbol_mode mode = complete_symbol_mode::EXPRESSION;
+
   gdb_assert (code == TYPE_CODE_UNION
 	      || code == TYPE_CODE_STRUCT
 	      || code == TYPE_CODE_ENUM);
-  current_language->la_collect_symbol_completion_matches (tracker,
+  current_language->la_collect_symbol_completion_matches (tracker, mode,
 							  text, word, code);
 }
 
@@ -5261,6 +5270,7 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 
 void
 collect_file_symbol_completion_matches (completion_tracker &tracker,
+					complete_symbol_mode mode,
 					const char *text, const char *word,
 					const char *srcfile)
 {
@@ -5271,6 +5281,9 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
 
   /* Now look for the symbol we are supposed to complete on.
      FIXME: This should be language-specific.  */
+  if (mode == complete_symbol_mode::LINESPEC)
+    sym_text = text;
+  else
   {
     const char *p;
     char quote_found;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index fb08479..fffe0f87 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1499,22 +1499,37 @@ extern void forget_cached_source_info (void);
 
 extern void select_source_symtab (struct symtab *);
 
+/* The reason we're calling into a completion match list collector
+   function.  */
+enum class complete_symbol_mode
+  {
+    /* Completing an expression.  */
+    EXPRESSION,
+
+    /* Completing a linespec.  */
+    LINESPEC,
+  };
+
 extern void default_collect_symbol_completion_matches_break_on
   (completion_tracker &tracker,
+   complete_symbol_mode mode,
    const char *text, const char *word, const char *break_on,
    enum type_code code);
 extern void default_collect_symbol_completion_matches
   (completion_tracker &tracker,
+   complete_symbol_mode,
    const char *,
    const char *,
    enum type_code);
 extern void collect_symbol_completion_matches (completion_tracker &tracker,
+					       complete_symbol_mode,
 					       const char *, const char *);
 extern void collect_symbol_completion_matches_type (completion_tracker &tracker,
 						    const char *, const char *,
 						    enum type_code);
 
 extern void collect_file_symbol_completion_matches (completion_tracker &tracker,
+						    complete_symbol_mode,
 						    const char *,
 						    const char *,
 						    const char *);
diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
index 6597ea7..f03bfc3 100644
--- a/gdb/testsuite/gdb.base/completion.exp
+++ b/gdb/testsuite/gdb.base/completion.exp
@@ -790,7 +790,7 @@ gdb_test_multiple "" $test {
     -re "break\.c.*break1\.c.*$gdb_prompt " {
 	send_gdb "1\t\n"
 	gdb_test_multiple "" $test {
-	    -re ".*Function \"$srcfile2\" not defined\..*$gdb_prompt " {
+	    -re "malformed linespec error: unexpected end of input\r\n$gdb_prompt " {
 		pass $test
 	    }
 	    -re "$gdb_prompt p$" {
diff --git a/gdb/testsuite/gdb.linespec/ls-errs.exp b/gdb/testsuite/gdb.linespec/ls-errs.exp
index 1f78ca6..7942f1f 100644
--- a/gdb/testsuite/gdb.linespec/ls-errs.exp
+++ b/gdb/testsuite/gdb.linespec/ls-errs.exp
@@ -167,11 +167,14 @@ proc do_test {lang} {
 	test_break "-source $x -line 3" invalid_file [string trim $x \"']
     }
 
-    # Test that option lexing stops at whitespace boundaries
+    # Test that option lexing stops at whitespace boundaries, except
+    # when lexing function names, where we want to handle setting
+    # breakpoints on e.g., "int template_function<int>()".
     test_break "-source this file has spaces.c -line 3" invalid_file "this"
-    test_break "-function function whitespace" invalid_function "function"
-    test_break "-source $srcfile -function function whitespace" \
-	       invalid_function_f "function" $srcfile
+    test_break "-function ret_type tmpl_function" \
+	invalid_function "ret_type tmpl_function"
+    test_break "-source $srcfile -function ret_type tmpl_function" \
+	       invalid_function_f "ret_type tmpl_function" $srcfile
 
     test_break "-function main -label label whitespace" \
 	       invalid_label "label" "main"
@@ -232,7 +235,12 @@ proc do_test {lang} {
     foreach x {"3" "+100" "-100" "foo"} {
 	test_break "main 3" invalid_function "main 3"
 	test_break "-function \"main $x\"" invalid_function "main $x"
-	test_break "main:here $x" invalid_label "here $x" "main"
+	if {$x == "foo"} {
+	    test_break "main:here $x" unexpected_opt "string" $x
+	} else {
+	    test_break "main:here $x" unexpected_opt "number" $x
+	}
+
 	test_break "-function main -label \"here $x\"" \
 		   invalid_label "here $x" "main"
     }
-- 
2.5.5

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

* [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (17 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 27/40] Make cp_remove_params return a unique_ptr Pedro Alves
@ 2017-06-02 12:23 ` Pedro Alves
  2017-07-14 17:23   ` Keith Seitz
  2018-03-05 21:43   ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Simon Marchi
  2017-06-02 12:23 ` [PATCH 28/40] lookup_name_info::make_ignore_params Pedro Alves
                   ` (21 subsequent siblings)
  40 siblings, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:23 UTC (permalink / raw)
  To: gdb-patches

This patch reworkds the whole completion machinery, and prepares it
for later enhancements.

Adds a new "completion_tracker" class that is meant to hold everything
about the state of the current completion operation.

This class now has the responsibility of tracking the list of
completion matches, and checking whether the max completions limit has
been reached.  You can look at this as this patch starting out by
C++fying the existing "completion_tracker" in symtab.c (it's just an
htab_t typedef currently), moving it to completer.h/c, and then making
it a class/generalizing/enhancing it.

Unlike with the current tracking, completion_tracker now checks
whether the limit has been reached on each completion match list
insertion.  This both simplifies the max-completions handling code
(maybe_add_completion_enum is gone, for example), and is a
prerequisite for follow up patches.

The current completion_tracker is only used for symbol completions,
and the symbol code gets at the current instance via globals.  This
patch cleans that up by adding a completion_tracker reference to the
signature of the completion functions, and passing the tracker around
everywhere necessary.

Then, the patch changes how the completion match list is handed over
to readline.  Currently, we're using the rl_completion_entry_function
readline entry point, and the patch switches to
rl_attempted_completion_function.  A following patch will want to let
GDB itself decide the common completion prefix between all matches
(what readline calls the "lowest common denominator"), instead of
having readline compute it, and that's not possible with the
rl_completion_entry_function entry point.  Also,
rl_attempted_completion_function lets GDB hand over the match list to
readline as an array in one go instead of passing down matches one by
one, so from that angle it's a nicer entry point anyway.

Lastly, the patch catches exceptions around the readline entry points,
because we can't let C++ exceptions cross readline.  We handle that in
the readline input entry point, but the completion entry point isn't
guarded, so GDB can abort if completion throws.  E.g., in current
master:

 (gdb) b -function "fun<tab>
 terminate called after throwing an instance of 'gdb_exception_RETURN_MASK_ERROR'
 Aborted (core dumped)

This patch fixes that.  This will be exercised in the new tests added
later on in the series.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (symbol_completion_match): Adjust comments.
	(symbol_completion_add): Replace vector parameter with
	completion_tracker parameter.  Use it.
	(ada_make_symbol_completion_list): Rename to...
	(ada_collect_symbol_completion_matches): ... this.  Add
	completion_tracker parameter and use it.
	(ada_language_defn): Adjust.
	* break-catch-syscall.c (catch_syscall_completer): Adjust
	prototype and work with completion_tracker instead of VEC.
	* breakpoint.c (condition_completer): Adjust prototype and work
	with completion_tracker instead of VEC.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Adjust to renames.
	* cli/cli-cmds.c (complete_command): Rework using
	completion_tracker.  Catch exceptions when completing.
	* cli/cli-decode.c (integer_unlimited_completer)
	(complete_on_cmdlist, complete_on_enum): Adjust prototype and work
	with completion_tracker instead of VEC.
	* command.h (struct completion_tracker): Forward declare.
	(completer_ftype, completer_handle_brkchars_ftype): Change
	types.
	(complete_on_cmdlist, complete_on_enum): Adjust.
	* completer.c: Include <algorithm>.
	(struct gdb_completer_state): New.
	(current_completion): New global.
	(readline_line_completion_function): Delete.
	(noop_completer, filename_completer)
	(filename_completer_handle_brkchars, complete_files_symbols)
	(linespec_location_completer): Adjust to work with a
	completion_tracker instead of a VEC.
	(string_or_empty): New.
	(collect_explicit_location_matches): Adjust to work with a
	completion_tracker instead of a VEC.
	(explicit_location_completer): Rename to ...
	(complete_explicit_location): ... this and adjust to work with a
	completion_tracker instead of a VEC.
	(location_completer): Adjust to work with a completion_tracker
	instead of a VEC.
	(add_struct_fields): Adjust to work with a completion_list instead
	of VEC.
	(expression_completer): Rename to ...
	(complete_expression): ... this and adjust to work with a
	completion_tracker instead of a VEC.  Use complete_files_symbols.
	(expression_completer): Reimplement on top of complete_expression.
	(symbol_completer): Adjust to work with a completion_tracker
	instead of a VEC.
	(enum complete_line_internal_reason): Add describing comments.
	(complete_line_internal_normal_command): Adjust to work with a
	completion_tracker instead of a VEC.
	(complete_line_internal): Rename to ...
	(complete_line_internal_1): ... this and adjust to work with a
	completion_tracker instead of a VEC.  Assert TEXT is NULL in the
	handle_brkchars phase.
	(new_completion_tracker): Delete.
	(complete_line_internal): Reimplement as TRY/CATCH wrapper around
	complete_line_internal_1.
	(free_completion_tracker): Delete.
	(INITIAL_COMPLETION_HTAB_SIZE): New.
	(completion_tracker::completion_tracker)
	(completion_tracker::~completion_tracker): New.
	(maybe_add_completion): Delete.
	(completion_tracker::maybe_add_completion)
	(completion_tracker::add_completion)
	(completion_tracker::add_completions): New.
	(complete_line): Adjust to work with a completion_tracker instead
	of a VEC.  Don't create a completion_tracker_t or check for max
	completions here.
	(command_completer, command_completer_handle_brkchars)
	(signal_completer, reg_or_group_completer_1)
	(reg_or_group_completer, default_completer_handle_brkchars):
	Adjust to work with a completion_tracker.
	(gdb_completion_word_break_characters_throw): New.
	(gdb_completion_word_break_characters): Reimplement.
	(line_completion_function): Delete.
	(completion_tracker::recompute_lowest_common_denominator)
	(expand_preserving_ws)
	(completion_tracker::build_completion_result)
	(completion_result::completion_result)
	(completion_result::completion_result)
	(completion_result::~completion_result)
	(completion_result::completion_result)
	(completion_result::release_match_list, compare_cstrings)
	(completion_result::sort_match_list)
	(completion_result::reset_match_list)
	(gdb_rl_attempted_completion_function_throw)
	(gdb_rl_attempted_completion_function): New.
	* completer.h (completion_list, struct completion_result)
	(class completion_tracker): New.
	(complete_line): Add completion_tracker parameter.
	(readline_line_completion_function): Delete.
	(gdb_rl_attempted_completion_function): New.
	(noop_completer, filename_completer, expression_completer)
	(location_completer, symbol_completer, command_completer)
	(signal_completer, reg_or_group_completer): Update prototypes.
	(completion_tracker_t, new_completion_tracker)
	(make_cleanup_free_completion_tracker): Delete.
	(enum maybe_add_completion_enum): Delete.
	(maybe_add_completion): Delete.
	* corefile.c (complete_set_gnutarget): Adjust to work with a
	completion_tracker instead of a VEC.
	* cp-abi.c (cp_abi_completer): Adjust to work with a
	completion_tracker instead of a VEC.
	* d-lang.c (d_language_defn): Adjust.
	* disasm.c (disassembler_options_completer): Adjust to work with a
	completion_tracker instead of a VEC.
	* f-lang.c (f_make_symbol_completion_list): Rename to ...
	(f_collect_symbol_completion_matches): ... this.  Adjust to work
	with a completion_tracker instead of a VEC.
	(f_language_defn): Adjust.
	* go-lang.c (go_language_defn): Adjust.
	* guile/scm-cmd.c (cmdscm_add_completion, cmdscm_completer):
	Adjust to work with a completion_tracker instead of a VEC.
	* infrun.c (handle_completer): Likewise.
	* interps.c (interpreter_completer): Likewise.
	* interps.h (interpreter_completer): Likewise.
	* language.c (unknown_language_defn, auto_language_defn)
	(local_language_defn): Adjust.
	* language.h (language_defn::la_make_symbol_completion_list):
	Rename to ...
	(language_defn::la_collect_symbol_completion_matches): ... this
	and adjust to work with a completion_tracker instead of a VEC.
	* m2-lang.c (m2_language_defn): Adjust.
	* objc-lang.c (objc_language_defn): Adjust.
	* opencl-lang.c (opencl_language_defn): Adjust.
	* p-lang.c (pascal_language_defn): Adjust.
	* python/py-cmd.c (cmdpy_completer_helper): Handle NULL word.
	(cmdpy_completer_handle_brkchars, cmdpy_completer): Adjust to work
	with a completion_tracker.
	* rust-lang.c (rust_language_defn): Adjust.
	* symtab.c (free_completion_list, do_free_completion_list)
	(return_val, completion_tracker): Delete.
	(completion_list_add_name, completion_list_add_symbol)
	(completion_list_add_msymbol, completion_list_objc_symbol)
	(completion_list_add_fields, add_symtab_completions): Add
	completion_tracker parameter and use it.
	(default_make_symbol_completion_list_break_on_1): Rename to...
	(default_collect_symbol_completion_matches_break_on): ... this.
	Add completion_tracker parameter and use it instead of allocating
	a completion tracker here.
	(default_make_symbol_completion_list_break_on): Delete old
	implementation.
	(default_make_symbol_completion_list): Delete.
	(default_collect_symbol_completion_matches): New.
	(make_symbol_completion_list): Delete.
	(collect_symbol_completion_matches): New.
	(make_symbol_completion_type): Rename to ...
	(collect_symbol_completion_matches_type): ... this.  Add
	completion_tracker parameter and use it instead of VEC.
	(make_file_symbol_completion_list_1): Rename to...
	(collect_file_symbol_completion_matches): ... this.  Add
	completion_tracker parameter and use it instead of VEC.
	(make_file_symbol_completion_list): Delete.
	(add_filename_to_list): Use completion_list instead of a VEC.
	(add_partial_filename_data::list): Now a completion_list.
	(make_source_files_completion_list): Work with a completion_list
	instead of a VEC.
	* symtab.h: Include "completer.h".
	(default_make_symbol_completion_list_break_on)
	(default_make_symbol_completion_list, make_symbol_completion_list)
	(make_symbol_completion_type, make_file_symbol_completion_list)
	(make_source_files_completion_list): Delete.
	(default_collect_symbol_completion_matches_break_on)
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches)
	(collect_symbol_completion_matches_type)
	(collect_file_symbol_completion_matches)
	(make_source_files_completion_list): New.
	* top.c (init_main): Don't install a rl_completion_entry_function
	hook.  Install a rl_attempted_completion_function hook instead.
	* tui/tui-layout.c (layout_completer): Adjust to work with a
	completion_tracker.
	* tui/tui-regs.c (tui_reggroup_completer):
	* tui/tui-win.c (window_name_completer, focus_completer)
	(winheight_completer): Adjust to work with a completion_tracker.
	* value.c: Include "completer.h".
	(complete_internalvar): Adjust to work with a completion_tracker.
	* value.h (complete_internalvar): Likewise.
---
 gdb/ada-lang.c            |  35 +-
 gdb/break-catch-syscall.c |  25 +-
 gdb/breakpoint.c          |  18 +-
 gdb/c-lang.c              |   8 +-
 gdb/cli/cli-cmds.c        |  54 +--
 gdb/cli/cli-decode.c      |  35 +-
 gdb/command.h             |  20 +-
 gdb/completer.c           | 920 ++++++++++++++++++++++++++++------------------
 gdb/completer.h           | 239 ++++++++----
 gdb/corefile.c            |   5 +-
 gdb/cp-abi.c              |   5 +-
 gdb/d-lang.c              |   2 +-
 gdb/disasm.c              |   6 +-
 gdb/f-lang.c              |  12 +-
 gdb/go-lang.c             |   4 +-
 gdb/guile/scm-cmd.c       |  38 +-
 gdb/infrun.c              |  13 +-
 gdb/interps.c             |   8 +-
 gdb/interps.h             |   7 +-
 gdb/language.c            |   6 +-
 gdb/language.h            |  12 +-
 gdb/m2-lang.c             |   2 +-
 gdb/objc-lang.c           |   2 +-
 gdb/opencl-lang.c         |   2 +-
 gdb/p-lang.c              |   2 +-
 gdb/python/py-cmd.c       |  45 ++-
 gdb/rust-lang.c           |   2 +-
 gdb/symtab.c              | 265 +++++--------
 gdb/symtab.h              |  31 +-
 gdb/top.c                 |   2 +-
 gdb/tui/tui-layout.c      |   5 +-
 gdb/tui/tui-regs.c        |  11 +-
 gdb/tui/tui-win.c         |  28 +-
 gdb/value.c               |  17 +-
 gdb/value.h               |   3 +-
 35 files changed, 1050 insertions(+), 839 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 25c49c5..0979cf8 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6440,10 +6440,10 @@ symbol_completion_match (const char *sym_name,
   return sym_name;
 }
 
-/* A companion function to ada_make_symbol_completion_list().
+/* A companion function to ada_collect_symbol_completion_matches().
    Check if SYM_NAME represents a symbol which name would be suitable
-   to complete TEXT (TEXT_LEN is the length of TEXT), in which case
-   it is appended at the end of the given string vector SV.
+   to complete TEXT (TEXT_LEN is the length of TEXT), in which case it
+   is added as a completion match to TRACKER.
 
    ORIG_TEXT is the string original string from the user command
    that needs to be completed.  WORD is the entire command on which
@@ -6456,8 +6456,8 @@ symbol_completion_match (const char *sym_name,
    encoded).  */
 
 static void
-symbol_completion_add (VEC(char_ptr) **sv,
-                       const char *sym_name,
+symbol_completion_add (completion_tracker &tracker,
+		       const char *sym_name,
                        const char *text, int text_len,
                        const char *orig_text, const char *word,
                        int wild_match_p, int encoded_p)
@@ -6492,21 +6492,21 @@ symbol_completion_add (VEC(char_ptr) **sv,
       strcat (completion, match);
     }
 
-  VEC_safe_push (char_ptr, *sv, completion);
+  tracker.add_completion (gdb::unique_xmalloc_ptr<char> (completion));
 }
 
-/* Return a list of possible symbol names completing TEXT0.  WORD is
-   the entire command on which completion is made.  */
+/* Add the list of possible symbol names completing TEXT0 to TRACKER.
+   WORD is the entire command on which completion is made.  */
 
-static VEC (char_ptr) *
-ada_make_symbol_completion_list (const char *text0, const char *word,
-				 enum type_code code)
+static void
+ada_collect_symbol_completion_matches (completion_tracker &tracker,
+				       const char *text0, const char *word,
+				       enum type_code code)
 {
   char *text;
   int text_len;
   int wild_match_p;
   int encoded_p;
-  VEC(char_ptr) *completions = VEC_alloc (char_ptr, 128);
   struct symbol *sym;
   struct compunit_symtab *s;
   struct minimal_symbol *msymbol;
@@ -6562,7 +6562,7 @@ ada_make_symbol_completion_list (const char *text0, const char *word,
   ALL_MSYMBOLS (objfile, msymbol)
   {
     QUIT;
-    symbol_completion_add (&completions, MSYMBOL_LINKAGE_NAME (msymbol),
+    symbol_completion_add (tracker, MSYMBOL_LINKAGE_NAME (msymbol),
 			   text, text_len, text0, word, wild_match_p,
 			   encoded_p);
   }
@@ -6577,7 +6577,7 @@ ada_make_symbol_completion_list (const char *text0, const char *word,
 
       ALL_BLOCK_SYMBOLS (b, iter, sym)
       {
-        symbol_completion_add (&completions, SYMBOL_LINKAGE_NAME (sym),
+        symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
                                text, text_len, text0, word,
                                wild_match_p, encoded_p);
       }
@@ -6592,7 +6592,7 @@ ada_make_symbol_completion_list (const char *text0, const char *word,
     b = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (s), GLOBAL_BLOCK);
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (&completions, SYMBOL_LINKAGE_NAME (sym),
+      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
                              text, text_len, text0, word,
                              wild_match_p, encoded_p);
     }
@@ -6607,14 +6607,13 @@ ada_make_symbol_completion_list (const char *text0, const char *word,
       continue;
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (&completions, SYMBOL_LINKAGE_NAME (sym),
+      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
                              text, text_len, text0, word,
                              wild_match_p, encoded_p);
     }
   }
 
   do_cleanups (old_chain);
-  return completions;
 }
 
                                 /* Field Access */
@@ -14042,7 +14041,7 @@ const struct language_defn ada_language_defn = {
   0,                            /* c-style arrays */
   1,                            /* String lower bound */
   ada_get_gdb_completer_word_break_characters,
-  ada_make_symbol_completion_list,
+  ada_collect_symbol_completion_matches,
   ada_language_arch_info,
   ada_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/break-catch-syscall.c b/gdb/break-catch-syscall.c
index 322f680..33f75d5 100644
--- a/gdb/break-catch-syscall.c
+++ b/gdb/break-catch-syscall.c
@@ -621,15 +621,14 @@ catching_syscall_number (int syscall_number)
 }
 
 /* Complete syscall names.  Used by "catch syscall".  */
-static VEC (char_ptr) *
+
+static void
 catch_syscall_completer (struct cmd_list_element *cmd,
+			 completion_tracker &tracker,
                          const char *text, const char *word)
 {
   struct gdbarch *gdbarch = get_current_arch ();
   struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
-  VEC (char_ptr) *group_retlist = NULL;
-  VEC (char_ptr) *syscall_retlist = NULL;
-  VEC (char_ptr) *retlist = NULL;
   const char **group_list = NULL;
   const char **syscall_list = NULL;
   const char *prefix;
@@ -645,8 +644,8 @@ catch_syscall_completer (struct cmd_list_element *cmd,
     {
       /* Perform completion inside 'group:' namespace only.  */
       group_list = get_syscall_group_names (gdbarch);
-      retlist = (group_list == NULL
-		 ? NULL : complete_on_enum (group_list, word, word));
+      if (group_list != NULL)
+	complete_on_enum (tracker, group_list, word, word);
     }
   else
     {
@@ -663,21 +662,15 @@ catch_syscall_completer (struct cmd_list_element *cmd,
 	  make_cleanup (xfree, prefixed_group);
 	}
 
-      syscall_retlist = ((syscall_list == NULL)
-			 ? NULL : complete_on_enum (syscall_list, word, word));
-      group_retlist = ((group_list == NULL)
-		       ? NULL : complete_on_enum (group_list, word, word));
-
-      retlist = VEC_merge (char_ptr, syscall_retlist, group_retlist);
+      if (syscall_list != NULL)
+	complete_on_enum (tracker, syscall_list, word, word);
+      if (group_list != NULL)
+	complete_on_enum (tracker, group_list, word, word);
     }
 
-  VEC_free (char_ptr, syscall_retlist);
-  VEC_free (char_ptr, group_retlist);
   xfree (syscall_list);
   xfree (group_list);
   do_cleanups (cleanups);
-
-  return retlist;
 }
 
 static void
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 150b08c..70c0e02 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -1043,8 +1043,9 @@ set_breakpoint_condition (struct breakpoint *b, const char *exp,
 
 /* Completion for the "condition" command.  */
 
-static VEC (char_ptr) *
+static void
 condition_completer (struct cmd_list_element *cmd,
+		     completion_tracker &tracker,
 		     const char *text, const char *word)
 {
   const char *space;
@@ -1060,9 +1061,9 @@ condition_completer (struct cmd_list_element *cmd,
       if (text[0] == '$')
 	{
 	  /* We don't support completion of history indices.  */
-	  if (isdigit (text[1]))
-	    return NULL;
-	  return complete_internalvar (&text[1]);
+	  if (!isdigit (text[1]))
+	    complete_internalvar (tracker, &text[1]);
+	  return;
 	}
 
       /* We're completing the breakpoint number.  */
@@ -1075,15 +1076,18 @@ condition_completer (struct cmd_list_element *cmd,
 	  xsnprintf (number, sizeof (number), "%d", b->number);
 
 	  if (strncmp (number, text, len) == 0)
-	    VEC_safe_push (char_ptr, result, xstrdup (number));
+	    {
+	      gdb::unique_xmalloc_ptr<char> copy (xstrdup (number));
+	      tracker.add_completion (std::move (copy));
+	    }
 	}
 
-      return result;
+      return;
     }
 
   /* We're completing the expression part.  */
   text = skip_spaces_const (space);
-  return expression_completer (cmd, text, word);
+  expression_completer (cmd, tracker, text, word);
 }
 
 /* condition N EXP -- set break condition of breakpoint N to EXP.  */
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index de8868b..695b237 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -865,7 +865,7 @@ const struct language_defn c_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   c_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
@@ -1009,7 +1009,7 @@ const struct language_defn cplus_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   cplus_language_arch_info,
   default_print_array_index,
   cp_pass_by_reference,
@@ -1062,7 +1062,7 @@ const struct language_defn asm_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   c_language_arch_info, 	/* FIXME: la_language_arch_info.  */
   default_print_array_index,
   default_pass_by_reference,
@@ -1115,7 +1115,7 @@ const struct language_defn minimal_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   c_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 2a5b128..7756fcc 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -238,6 +238,7 @@ help_command (char *command, int from_tty)
   help_cmd (command, gdb_stdout);
 }
 \f
+
 /* Note: The "complete" command is used by Emacs to implement completion.
    [Is that why this function writes output with *_unfiltered?]  */
 
@@ -246,8 +247,6 @@ complete_command (char *arg_entry, int from_tty)
 {
   const char *arg = arg_entry;
   int argpoint;
-  char *arg_prefix;
-  VEC (char_ptr) *completions;
 
   dont_repeat ();
 
@@ -279,43 +278,46 @@ complete_command (char *arg_entry, int from_tty)
       point--;
     }
 
-  arg_prefix = (char *) alloca (point - arg + 1);
-  memcpy (arg_prefix, arg, point - arg);
-  arg_prefix[point - arg] = 0;
-
-  completions = complete_line (point, arg, argpoint);
+  completion_tracker tracker_handle_completions;
 
-  if (completions)
+  TRY
+    {
+      complete_line (tracker_handle_completions, point, arg, strlen (arg));
+    }
+  CATCH (ex, RETURN_MASK_ALL)
     {
-      int ix, size = VEC_length (char_ptr, completions);
-      char *item, *prev = NULL;
+      return;
+    }
 
-      qsort (VEC_address (char_ptr, completions), size,
-	     sizeof (char *), compare_strings);
+  std::string arg_prefix (arg, point - arg);
 
-      /* We do extra processing here since we only want to print each
-	 unique item once.  */
-      for (ix = 0; VEC_iterate (char_ptr, completions, ix, item); ++ix)
+  completion_result result
+    = (tracker_handle_completions.build_completion_result
+       (point, point - arg, strlen (arg)));
+
+  if (result.number_matches != 0)
+    {
+      if (result.number_matches == 1)
+	printf_unfiltered ("%s%s\n", arg_prefix.c_str (), result.match_list[0]);
+      else
 	{
-	  if (prev == NULL || strcmp (item, prev) != 0)
+	  result.sort_match_list ();
+
+	  for (size_t i = 0; i < result.number_matches; i++)
 	    {
-	      printf_unfiltered ("%s%s\n", arg_prefix, item);
-	      xfree (prev);
-	      prev = item;
+	      printf_unfiltered ("%s%s",
+				 arg_prefix.c_str (),
+				 result.match_list[i + 1]);
+	      printf_unfiltered ("\n");
 	    }
-	  else
-	    xfree (item);
 	}
 
-      xfree (prev);
-      VEC_free (char_ptr, completions);
-
-      if (size == max_completions)
+      if (result.number_matches == max_completions)
 	{
 	  /* ARG_PREFIX and POINT are included in the output so that emacs
 	     will include the message in the output.  */
 	  printf_unfiltered (_("%s%s %s\n"),
-			     arg_prefix, point,
+			     arg_prefix.c_str (), point,
 			     get_max_completions_reached_message ());
 	}
     }
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index f163581..6d2d79c 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -657,8 +657,9 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class theclass
 /* Completes on literal "unlimited".  Used by integer commands that
    support a special "unlimited" value.  */
 
-static VEC (char_ptr) *
+static void
 integer_unlimited_completer (struct cmd_list_element *ignore,
+			     completion_tracker &tracker,
 			     const char *text, const char *word)
 {
   static const char * const keywords[] =
@@ -667,7 +668,7 @@ integer_unlimited_completer (struct cmd_list_element *ignore,
       NULL,
     };
 
-  return complete_on_enum (keywords, text, word);
+  complete_on_enum (tracker, keywords, text, word);
 }
 
 /* Add element named NAME to both the set and show command LISTs (the
@@ -1768,13 +1769,13 @@ lookup_cmd_composition (const char *text,
    "foo" and we want to complete to "foobar".  If WORD is "oo", return
    "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
 
-VEC (char_ptr) *
+void
 complete_on_cmdlist (struct cmd_list_element *list,
+		     completion_tracker &tracker,
 		     const char *text, const char *word,
 		     int ignore_help_classes)
 {
   struct cmd_list_element *ptr;
-  VEC (char_ptr) *matchlist = NULL;
   int textlen = strlen (text);
   int pass;
   int saw_deprecated_match = 0;
@@ -1783,8 +1784,10 @@ complete_on_cmdlist (struct cmd_list_element *list,
      commands.  If we see no matching commands in the first pass, and
      if we did happen to see a matching deprecated command, we do
      another loop to collect those.  */
-  for (pass = 0; matchlist == 0 && pass < 2; ++pass)
+  for (pass = 0; pass < 2; ++pass)
     {
+      bool got_matches = false;
+
       for (ptr = list; ptr; ptr = ptr->next)
 	if (!strncmp (ptr->name, text, textlen)
 	    && !ptr->abbrev_flag
@@ -1817,32 +1820,34 @@ complete_on_cmdlist (struct cmd_list_element *list,
 		match[text - word] = '\0';
 		strcat (match, ptr->name);
 	      }
-	    VEC_safe_push (char_ptr, matchlist, match);
+	    tracker.add_completion (gdb::unique_xmalloc_ptr<char> (match));
+	    got_matches = true;
 	  }
+
+      if (got_matches)
+	break;
+
       /* If we saw no matching deprecated commands in the first pass,
 	 just bail out.  */
       if (!saw_deprecated_match)
 	break;
     }
-
-  return matchlist;
 }
 
 /* Helper function for SYMBOL_COMPLETION_FUNCTION.  */
 
-/* Return a vector of char pointers which point to the different
-   possible completions in CMD of TEXT.
+/* Add the different possible completions in ENUMLIST of TEXT.
 
    WORD points in the same buffer as TEXT, and completions should be
    returned relative to this position.  For example, suppose TEXT is "foo"
    and we want to complete to "foobar".  If WORD is "oo", return
    "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
 
-VEC (char_ptr) *
-complete_on_enum (const char *const *enumlist,
+void
+complete_on_enum (completion_tracker &tracker,
+		  const char *const *enumlist,
 		  const char *text, const char *word)
 {
-  VEC (char_ptr) *matchlist = NULL;
   int textlen = strlen (text);
   int i;
   const char *name;
@@ -1867,10 +1872,8 @@ complete_on_enum (const char *const *enumlist,
 	    match[text - word] = '\0';
 	    strcat (match, name);
 	  }
-	VEC_safe_push (char_ptr, matchlist, match);
+	tracker.add_completion (gdb::unique_xmalloc_ptr<char> (match));
       }
-
-  return matchlist;
 }
 
 
diff --git a/gdb/command.h b/gdb/command.h
index 2a190d4..3a4a449 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -21,6 +21,8 @@
 #include "gdb_vecs.h"
 #include "common/scoped_restore.h"
 
+struct completion_tracker;
+
 /* This file defines the public interface for any code wanting to
    create commands.  */
 
@@ -174,7 +176,7 @@ typedef void cmd_sfunc_ftype (char *args, int from_tty,
 extern void set_cmd_sfunc (struct cmd_list_element *cmd,
 			   cmd_sfunc_ftype *sfunc);
 
-/* A completion routine.  Return a list of possible completions.
+/* A completion routine.  Add possible completions to tracker.
 
    TEXT is the text beyond what was matched for the command itself
    (leading whitespace is skipped).  It stops where we are supposed to
@@ -183,11 +185,13 @@ extern void set_cmd_sfunc (struct cmd_list_element *cmd,
    relative to this position.  For example, suppose TEXT is "foo" and
    we want to complete to "foobar".  If WORD is "oo", return "oobar";
    if WORD is "baz/foo", return "baz/foobar".  */
-typedef VEC (char_ptr) *completer_ftype (struct cmd_list_element *,
-					 const char *text, const char *word);
+typedef void completer_ftype (struct cmd_list_element *,
+			      completion_tracker &tracker,
+			      const char *text, const char *word);
 
 /* Same, but for set_cmd_completer_handle_brkchars.  */
 typedef void completer_handle_brkchars_ftype (struct cmd_list_element *,
+					      completion_tracker &tracker,
 					      const char *text, const char *word);
 
 extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
@@ -259,11 +263,13 @@ extern struct cmd_list_element *add_info (const char *,
 extern struct cmd_list_element *add_info_alias (const char *, const char *,
 						int);
 
-extern VEC (char_ptr) *complete_on_cmdlist (struct cmd_list_element *,
-					    const char *, const char *, int);
+extern void complete_on_cmdlist (struct cmd_list_element *,
+				 completion_tracker &tracker,
+				 const char *, const char *, int);
 
-extern VEC (char_ptr) *complete_on_enum (const char *const *enumlist,
-					 const char *, const char *);
+extern void complete_on_enum (completion_tracker &tracker,
+			      const char *const *enumlist,
+			      const char *, const char *);
 
 /* Functions that implement commands about CLI commands.  */
 
diff --git a/gdb/completer.c b/gdb/completer.c
index fe69faa..c6e1e28 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -28,7 +28,7 @@
 #include "user-regs.h"
 #include "arch-utils.h"
 #include "location.h"
-
+#include <algorithm>
 #include "cli/cli-decode.h"
 
 /* FIXME: This is needed because of lookup_cmd_1 ().  We should be
@@ -44,6 +44,25 @@
 
 #include "completer.h"
 
+/* Misc state that needs to be tracked across several different
+   readline completer entry point calls, all related to a single
+   completion invocation.  */
+
+struct gdb_completer_state
+{
+  /* The current completion's completion tracker.  This is a global
+     because a tracker can be shared between the handle_brkchars and
+     handle_completion phases, which involves different readline
+     callbacks.  */
+  completion_tracker *tracker = NULL;
+
+  /* Whether the current completion was aborted.  */
+  bool aborted = false;
+};
+
+/* The current completion state.  */
+static gdb_completer_state current_completion;
+
 /* An enumeration of the various things a user might
    attempt to complete for a location.  */
 
@@ -60,10 +79,6 @@ enum explicit_location_match_type
 };
 
 /* Prototypes for local functions.  */
-static
-char *line_completion_function (const char *text, int matches, 
-				char *line_buffer,
-				int point);
 
 /* readline uses the word breaks for two things:
    (1) In figuring out where to point the TEXT parameter to the
@@ -113,28 +128,21 @@ get_gdb_completer_quote_characters (void)
   return gdb_completer_quote_characters;
 }
 
-/* Line completion interface function for readline.  */
-
-char *
-readline_line_completion_function (const char *text, int matches)
-{
-  return line_completion_function (text, matches, 
-				   rl_line_buffer, rl_point);
-}
-
 /* This can be used for functions which don't want to complete on
    symbols but don't want to complete on anything else either.  */
-VEC (char_ptr) *
+
+void
 noop_completer (struct cmd_list_element *ignore, 
+		completion_tracker &tracker,
 		const char *text, const char *prefix)
 {
-  return NULL;
 }
 
 /* Complete on filenames.  */
 
-VEC (char_ptr) *
-filename_completer (struct cmd_list_element *ignore, 
+void
+filename_completer (struct cmd_list_element *ignore,
+		    completion_tracker &tracker,
 		    const char *text, const char *word)
 {
   int subsequent_name;
@@ -180,7 +188,7 @@ filename_completer (struct cmd_list_element *ignore,
 	  strcat (q, p);
 	  xfree (p);
 	}
-      VEC_safe_push (char_ptr, return_val, q);
+      tracker.add_completion (gdb::unique_xmalloc_ptr<char> (q));
     }
 #if 0
   /* There is no way to do this just long enough to affect quote
@@ -190,7 +198,6 @@ filename_completer (struct cmd_list_element *ignore,
      with respect to inserting quotes.  */
   rl_completer_word_break_characters = "";
 #endif
-  return return_val;
 }
 
 /* The corresponding completer_handle_brkchars
@@ -198,6 +205,7 @@ filename_completer (struct cmd_list_element *ignore,
 
 static void
 filename_completer_handle_brkchars (struct cmd_list_element *ignore,
+				    completion_tracker &tracker,
 				    const char *text, const char *word)
 {
   set_rl_completer_word_break_characters
@@ -213,13 +221,12 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
    This is intended to be used in commands that set breakpoints
    etc.  */
 
-static VEC (char_ptr) *
-linespec_location_completer (struct cmd_list_element *ignore,
-			     const char *text, const char *word)
+static void
+complete_files_symbols (completion_tracker &tracker,
+			const char *text, const char *word)
 {
-  int n_syms, n_files, ix;
-  VEC (char_ptr) *fn_list = NULL;
-  VEC (char_ptr) *list = NULL;
+  int ix;
+  completion_list fn_list;
   const char *p;
   int quote_found = 0;
   int quoted = *text == '\'' || *text == '"';
@@ -228,7 +235,6 @@ linespec_location_completer (struct cmd_list_element *ignore,
   char *file_to_match = NULL;
   const char *symbol_start = text;
   const char *orig_text = text;
-  size_t text_len;
 
   /* Do we have an unquoted colon, as in "break foo.c:bar"?  */
   for (p = text; *p != '\0'; ++p)
@@ -269,7 +275,6 @@ linespec_location_completer (struct cmd_list_element *ignore,
 
   if (quoted)
     text++;
-  text_len = strlen (text);
 
   /* Where is the file name?  */
   if (colon)
@@ -291,44 +296,23 @@ linespec_location_completer (struct cmd_list_element *ignore,
      symbols as well as on files.  */
   if (colon)
     {
-      list = make_file_symbol_completion_list (symbol_start, word,
-					       file_to_match);
+      collect_file_symbol_completion_matches (tracker, symbol_start, word,
+					      file_to_match);
       xfree (file_to_match);
     }
   else
     {
-      list = make_symbol_completion_list (symbol_start, word);
+      size_t text_len = strlen (text);
+
+      collect_symbol_completion_matches (tracker, symbol_start, word);
       /* If text includes characters which cannot appear in a file
 	 name, they cannot be asking for completion on files.  */
-      if (strcspn (text, 
+      if (strcspn (text,
 		   gdb_completer_file_name_break_characters) == text_len)
 	fn_list = make_source_files_completion_list (text, text);
     }
 
-  n_syms = VEC_length (char_ptr, list);
-  n_files = VEC_length (char_ptr, fn_list);
-
-  /* Catenate fn_list[] onto the end of list[].  */
-  if (!n_syms)
-    {
-      VEC_free (char_ptr, list); /* Paranoia.  */
-      list = fn_list;
-      fn_list = NULL;
-    }
-  else
-    {
-      char *fn;
-
-      for (ix = 0; VEC_iterate (char_ptr, fn_list, ix, fn); ++ix)
-	VEC_safe_push (char_ptr, list, fn);
-      VEC_free (char_ptr, fn_list);
-    }
-
-  if (n_syms && n_files)
-    {
-      /* Nothing.  */
-    }
-  else if (n_files)
+  if (!fn_list.empty () && !tracker.have_completions ())
     {
       char *fn;
 
@@ -347,31 +331,41 @@ linespec_location_completer (struct cmd_list_element *ignore,
 	 completion, because rl_complete will prepend "/foo/" to each
 	 candidate completion.  The loop below removes that leading
 	 part.  */
-      for (ix = 0; VEC_iterate (char_ptr, list, ix, fn); ++ix)
+      for (const auto &fn_up: fn_list)
 	{
-	  memmove (fn, fn + (word - text),
-		   strlen (fn) + 1 - (word - text));
+	  char *fn = fn_up.get ();
+	  memmove (fn, fn + (word - text), strlen (fn) + 1 - (word - text));
 	}
     }
-  else if (!n_syms)
+
+  tracker.add_completions (std::move (fn_list));
+
+  if (!tracker.have_completions ())
     {
       /* No completions at all.  As the final resort, try completing
 	 on the entire text as a symbol.  */
-      list = make_symbol_completion_list (orig_text, word);
+      collect_symbol_completion_matches (tracker,
+					 orig_text, word);
     }
+}
+
+/* Returns STRING if not NULL, the empty string otherwise.  */
 
-  return list;
+static const char *
+string_or_empty (const char *string)
+{
+  return string != NULL ? string : "";
 }
 
 /* A helper function to collect explicit location matches for the given
    LOCATION, which is attempting to match on WORD.  */
 
-static VEC (char_ptr) *
-collect_explicit_location_matches (struct event_location *location,
+static void
+collect_explicit_location_matches (completion_tracker &tracker,
+				   struct event_location *location,
 				   enum explicit_location_match_type what,
 				   const char *word)
 {
-  VEC (char_ptr) *matches = NULL;
   const struct explicit_location *explicit_loc
     = get_explicit_location (location);
 
@@ -379,26 +373,25 @@ collect_explicit_location_matches (struct event_location *location,
     {
     case MATCH_SOURCE:
       {
-	const char *text = (explicit_loc->source_filename == NULL
-			    ? "" : explicit_loc->source_filename);
-
-	matches = make_source_files_completion_list (text, word);
+	const char *source = string_or_empty (explicit_loc->source_filename);
+	completion_list matches
+	  = make_source_files_completion_list (source, word);
+	tracker.add_completions (std::move (matches));
       }
       break;
 
     case MATCH_FUNCTION:
       {
-	const char *text = (explicit_loc->function_name == NULL
-			    ? "" : explicit_loc->function_name);
-
+	const char *function = string_or_empty (explicit_loc->function_name);
 	if (explicit_loc->source_filename != NULL)
 	  {
 	    const char *filename = explicit_loc->source_filename;
 
-	    matches = make_file_symbol_completion_list (text, word, filename);
+	    collect_file_symbol_completion_matches (tracker,
+						    function, word, filename);
 	  }
-	else
-	  matches = make_symbol_completion_list (text, word);
+       else
+	 collect_symbol_completion_matches (tracker, function, word);
       }
       break;
 
@@ -409,8 +402,6 @@ collect_explicit_location_matches (struct event_location *location,
     default:
       gdb_assert_not_reached ("unhandled explicit_location_match_type");
     }
-
-  return matches;
 }
 
 /* A convenience macro to (safely) back up P to the previous word.  */
@@ -429,10 +420,10 @@ backup_text_ptr (const char *p, const char *text)
 /* A completer function for explicit locations.  This function
    completes both options ("-source", "-line", etc) and values.  */
 
-static VEC (char_ptr) *
-explicit_location_completer (struct cmd_list_element *ignore,
-			     struct event_location *location,
-			     const char *text, const char *word)
+static void
+complete_explicit_location (completion_tracker &tracker,
+			    struct event_location *location,
+			    const char *text, const char *word)
 {
   const char *p;
   VEC (char_ptr) *matches = NULL;
@@ -457,7 +448,8 @@ explicit_location_completer (struct cmd_list_element *ignore,
       /* Skip over the '-'.  */
       ++p;
 
-      return complete_on_enum (keywords, p, p);
+      complete_on_enum (tracker, keywords, p, p);
+      return;
     }
   else
     {
@@ -495,7 +487,7 @@ explicit_location_completer (struct cmd_list_element *ignore,
 	{
 	  /* The user isn't completing on any valid option name,
 	     e.g., "break -source foo.c [tab]".  */
-	  return NULL;
+	  return;
 	}
 
       /* If the user hasn't entered a search expression, e.g.,
@@ -505,43 +497,40 @@ explicit_location_completer (struct cmd_list_element *ignore,
 	new_word = "";
 
       /* Now gather matches  */
-      matches = collect_explicit_location_matches (location, what, new_word);
+      collect_explicit_location_matches (tracker, location, what, new_word);
     }
-
-  return matches;
 }
 
 /* A completer for locations.  */
 
-VEC (char_ptr) *
+void
 location_completer (struct cmd_list_element *ignore,
+		    completion_tracker &tracker,
 		    const char *text, const char *word)
 {
-  VEC (char_ptr) *matches = NULL;
   const char *copy = text;
 
   event_location_up location = string_to_explicit_location (&copy,
 							    current_language,
 							    1);
   if (location != NULL)
-    matches = explicit_location_completer (ignore, location.get (),
-					   text, word);
+    complete_explicit_location (tracker, location.get (),
+				text, word);
   else
     {
       /* This is an address or linespec location.
 	 Right now both of these are handled by the (old) linespec
 	 completer.  */
-      matches = linespec_location_completer (ignore, text, word);
+      complete_files_symbols (tracker, text, word);
     }
-
-  return matches;
 }
 
 /* Helper for expression_completer which recursively adds field and
-   method names from TYPE, a struct or union type, to the array
-   OUTPUT.  */
+   method names from TYPE, a struct or union type, to the OUTPUT
+   list.  */
+
 static void
-add_struct_fields (struct type *type, VEC (char_ptr) **output,
+add_struct_fields (struct type *type, completion_list &output,
 		   char *fieldname, int namelen)
 {
   int i;
@@ -560,8 +549,7 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
 	    {
 	      if (! strncmp (TYPE_FIELD_NAME (type, i), 
 			     fieldname, namelen))
-		VEC_safe_push (char_ptr, *output,
-			       xstrdup (TYPE_FIELD_NAME (type, i)));
+		output.emplace_back (xstrdup (TYPE_FIELD_NAME (type, i)));
 	    }
 	  else if (TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_UNION)
 	    {
@@ -585,7 +573,7 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
 	    }
 	  /* Omit constructors from the completion list.  */
 	  if (!type_name || strcmp (type_name, name))
-	    VEC_safe_push (char_ptr, *output, xstrdup (name));
+	    output.emplace_back (xstrdup (name));
 	}
     }
 }
@@ -593,13 +581,13 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
 /* Complete on expressions.  Often this means completing on symbol
    names, but some language parsers also have support for completing
    field names.  */
-VEC (char_ptr) *
-expression_completer (struct cmd_list_element *ignore, 
-		      const char *text, const char *word)
+
+static void
+complete_expression (completion_tracker &tracker,
+		     const char *text, const char *word)
 {
   struct type *type = NULL;
   char *fieldname;
-  const char *p;
   enum type_code code = TYPE_CODE_UNDEF;
 
   /* Perform a tentative parse of the expression, to see whether a
@@ -611,7 +599,7 @@ expression_completer (struct cmd_list_element *ignore,
     }
   CATCH (except, RETURN_MASK_ERROR)
     {
-      return NULL;
+      return;
     }
   END_CATCH
 
@@ -629,11 +617,12 @@ expression_completer (struct cmd_list_element *ignore,
 	  || TYPE_CODE (type) == TYPE_CODE_STRUCT)
 	{
 	  int flen = strlen (fieldname);
-	  VEC (char_ptr) *result = NULL;
+	  completion_list result;
 
-	  add_struct_fields (type, &result, fieldname, flen);
+	  add_struct_fields (type, result, fieldname, flen);
 	  xfree (fieldname);
-	  return result;
+	  tracker.add_completions (std::move (result));
+	  return;
 	}
     }
   else if (fieldname && code != TYPE_CODE_UNDEF)
@@ -641,21 +630,26 @@ expression_completer (struct cmd_list_element *ignore,
       VEC (char_ptr) *result;
       struct cleanup *cleanup = make_cleanup (xfree, fieldname);
 
-      result = make_symbol_completion_type (fieldname, fieldname, code);
+      collect_symbol_completion_matches_type (tracker, fieldname, fieldname,
+					      code);
       do_cleanups (cleanup);
-      return result;
+      return;
     }
   xfree (fieldname);
 
-  /* Commands which complete on locations want to see the entire
-     argument.  */
-  for (p = word;
-       p > text && p[-1] != ' ' && p[-1] != '\t';
-       p--)
-    ;
+  complete_files_symbols (tracker, text, word);
+}
+
+/* Complete on expressions.  Often this means completing on symbol
+   names, but some language parsers also have support for completing
+   field names.  */
 
-  /* Not ideal but it is what we used to do before...  */
-  return linespec_location_completer (ignore, text, word);
+void
+expression_completer (struct cmd_list_element *ignore,
+		      completion_tracker &tracker,
+		      const char *text, const char *word)
+{
+  complete_expression (tracker, text, word);
 }
 
 /* See definition in completer.h.  */
@@ -685,11 +679,12 @@ set_gdb_completion_word_break_characters (completer_ftype *fn)
 
 /* Complete on symbols.  */
 
-VEC (char_ptr) *
+void
 symbol_completer (struct cmd_list_element *ignore,
+		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
-  return make_symbol_completion_list (text, word);
+  collect_symbol_completion_matches (tracker, text, word);
 }
 
 /* Here are some useful test cases for completion.  FIXME: These
@@ -715,18 +710,30 @@ symbol_completer (struct cmd_list_element *ignore,
    "file ../gdb.stabs/we" "ird" (needs to not break word at slash)
  */
 
-typedef enum
+enum complete_line_internal_reason
 {
+  /* Preliminary phase, called by gdb_completion_word_break_characters
+     function, is used to determine the correct set of chars that are
+     word delimiters depending on the current command in line_buffer.
+     No completion list should be generated; the return value should
+     be NULL.  This is checked by an assertion.  */
   handle_brkchars,
+
+  /* Main phase, called by complete_line function, is used to get the
+     list of possible completions.  */
   handle_completions,
-  handle_help
-}
-complete_line_internal_reason;
+
+  /* Special case when completing a 'help' command.  In this case,
+     once sub-command completions are exhausted, we simply return
+     NULL.  */
+  handle_help,
+};
 
 /* Helper for complete_line_internal to simplify it.  */
 
-static VEC (char_ptr) *
-complete_line_internal_normal_command (const char *command, const char *word,
+static void
+complete_line_internal_normal_command (completion_tracker &tracker,
+				       const char *command, const char *word,
 				       const char *cmd_args,
 				       complete_line_internal_reason reason,
 				       struct cmd_list_element *c)
@@ -761,12 +768,11 @@ complete_line_internal_normal_command (const char *command, const char *word,
 	       (c->completer));
 	}
 
-      brkchars_fn (c, p, word);
+      brkchars_fn (c, tracker, p, word);
     }
 
   if (reason != handle_brkchars && c->completer != NULL)
-    return (*c->completer) (c, p, word);
-  return NULL;
+    (*c->completer) (c, tracker, p, word);
 }
 
 /* Internal function used to handle completions.
@@ -778,35 +784,19 @@ complete_line_internal_normal_command (const char *command, const char *word,
    text of the line.  POINT is the offset in that line of the cursor.
    You should pretend that the line ends at POINT.
 
-   REASON is of type complete_line_internal_reason.
-
-   If REASON is handle_brkchars:
-   Preliminary phase, called by gdb_completion_word_break_characters
-   function, is used to determine the correct set of chars that are
-   word delimiters depending on the current command in line_buffer.
-   No completion list should be generated; the return value should be
-   NULL.  This is checked by an assertion in that function.
+   See complete_line_internal_reason for description of REASON.  */
 
-   If REASON is handle_completions:
-   Main phase, called by complete_line function, is used to get the list
-   of posible completions.
-
-   If REASON is handle_help:
-   Special case when completing a 'help' command.  In this case,
-   once sub-command completions are exhausted, we simply return NULL.
- */
-
-static VEC (char_ptr) *
-complete_line_internal (const char *text, 
-			const char *line_buffer, int point,
-			complete_line_internal_reason reason)
+static void
+complete_line_internal_1 (completion_tracker &tracker,
+			  const char *text,
+			  const char *line_buffer, int point,
+			  complete_line_internal_reason reason)
 {
-  VEC (char_ptr) *list = NULL;
   char *tmp_command;
   const char *p;
   int ignore_help_classes;
   /* Pointer within tmp_command which corresponds to text.  */
-  char *word;
+  const char *word;
   struct cmd_list_element *c, *result_list;
 
   /* Choose the default set of word break characters to break
@@ -829,10 +819,18 @@ complete_line_internal (const char *text,
 
   strncpy (tmp_command, line_buffer, point);
   tmp_command[point] = '\0';
-  /* Since text always contains some number of characters leading up
-     to point, we can find the equivalent position in tmp_command
-     by subtracting that many characters from the end of tmp_command.  */
-  word = tmp_command + point - strlen (text);
+  if (reason == handle_brkchars)
+    {
+      gdb_assert (text == NULL);
+      word = NULL;
+    }
+  else
+    {
+      /* Since text always contains some number of characters leading up
+	 to point, we can find the equivalent position in tmp_command
+	 by subtracting that many characters from the end of tmp_command.  */
+      word = tmp_command + point - strlen (text);
+    }
 
   if (point == 0)
     {
@@ -856,7 +854,6 @@ complete_line_internal (const char *text,
     {
       /* It is an unrecognized command.  So there are no
 	 possible completions.  */
-      list = NULL;
     }
   else if (c == CMD_LIST_AMBIGUOUS)
     {
@@ -874,7 +871,6 @@ complete_line_internal (const char *text,
 	     example, "info t " or "info t foo" does not complete
 	     to anything, because "info t" can be "info target" or
 	     "info terminal".  */
-	  list = NULL;
 	}
       else
 	{
@@ -883,14 +879,14 @@ complete_line_internal (const char *text,
 	  if (result_list)
 	    {
 	      if (reason != handle_brkchars)
-		list = complete_on_cmdlist (*result_list->prefixlist, p,
-					    word, ignore_help_classes);
+		complete_on_cmdlist (*result_list->prefixlist, tracker, p,
+				     word, ignore_help_classes);
 	    }
 	  else
 	    {
 	      if (reason != handle_brkchars)
-		list = complete_on_cmdlist (cmdlist, p, word,
-					    ignore_help_classes);
+		complete_on_cmdlist (cmdlist, tracker, p, word,
+				     ignore_help_classes);
 	    }
 	  /* Ensure that readline does the right thing with respect to
 	     inserting quotes.  */
@@ -916,8 +912,8 @@ complete_line_internal (const char *text,
 		  /* It is a prefix command; what comes after it is
 		     a subcommand (e.g. "info ").  */
 		  if (reason != handle_brkchars)
-		    list = complete_on_cmdlist (*c->prefixlist, p, word,
-						ignore_help_classes);
+		    complete_on_cmdlist (*c->prefixlist, tracker, p, word,
+					 ignore_help_classes);
 
 		  /* Ensure that readline does the right thing
 		     with respect to inserting quotes.  */
@@ -925,11 +921,11 @@ complete_line_internal (const char *text,
 		    (gdb_completer_command_word_break_characters);
 		}
 	      else if (reason == handle_help)
-		list = NULL;
+		;
 	      else if (c->enums)
 		{
 		  if (reason != handle_brkchars)
-		    list = complete_on_enum (c->enums, p, word);
+		    complete_on_enum (tracker, c->enums, p, word);
 		  set_rl_completer_word_break_characters
 		    (gdb_completer_command_word_break_characters);
 		}
@@ -937,9 +933,9 @@ complete_line_internal (const char *text,
 		{
 		  /* It is a normal command; what comes after it is
 		     completed by the command's completer function.  */
-		  list = complete_line_internal_normal_command (tmp_command,
-								word, p,
-								reason, c);
+		  complete_line_internal_normal_command (tracker,
+							 tmp_command, word, p,
+							 reason, c);
 		}
 	    }
 	  else
@@ -961,8 +957,8 @@ complete_line_internal (const char *text,
 		}
 
 	      if (reason != handle_brkchars)
-		list = complete_on_cmdlist (result_list, q, word,
-					    ignore_help_classes);
+		complete_on_cmdlist (result_list, tracker, q, word,
+				     ignore_help_classes);
 
 	      /* Ensure that readline does the right thing
 		 with respect to inserting quotes.  */
@@ -971,7 +967,7 @@ complete_line_internal (const char *text,
 	    }
 	}
       else if (reason == handle_help)
-	list = NULL;
+	;
       else
 	{
 	  /* There is non-whitespace beyond the command.  */
@@ -980,93 +976,97 @@ complete_line_internal (const char *text,
 	    {
 	      /* It is an unrecognized subcommand of a prefix command,
 		 e.g. "info adsfkdj".  */
-	      list = NULL;
 	    }
 	  else if (c->enums)
 	    {
 	      if (reason != handle_brkchars)
-		list = complete_on_enum (c->enums, p, word);
+		complete_on_enum (tracker, c->enums, p, word);
 	    }
 	  else
 	    {
 	      /* It is a normal command.  */
-	      list = complete_line_internal_normal_command (tmp_command,
-							    word, p,
-							    reason, c);
+	      complete_line_internal_normal_command (tracker,
+						     tmp_command, word, p,
+						     reason, c);
 	    }
 	}
     }
-
-  return list;
 }
 
-/* See completer.h.  */
-
-int max_completions = 200;
-
-/* See completer.h.  */
+/* Wrapper around complete_line_internal_1 to handle
+   MAX_COMPLETIONS_REACHED_ERROR.  */
 
-completion_tracker_t
-new_completion_tracker (void)
+static void
+complete_line_internal (completion_tracker &tracker,
+			const char *text,
+			const char *line_buffer, int point,
+			complete_line_internal_reason reason)
 {
-  if (max_completions <= 0)
-    return NULL;
-
-  return htab_create_alloc (max_completions,
-			    htab_hash_string, (htab_eq) streq,
-			    NULL, xcalloc, xfree);
+  TRY
+    {
+      complete_line_internal_1 (tracker, text, line_buffer, point, reason);
+    }
+  CATCH (except, RETURN_MASK_ERROR)
+    {
+      if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
+	throw_exception (except);
+    }
 }
 
-/* Cleanup routine to free a completion tracker and reset the pointer
-   to NULL.  */
+/* See completer.h.  */
 
-static void
-free_completion_tracker (void *p)
-{
-  completion_tracker_t *tracker_ptr = (completion_tracker_t *) p;
+int max_completions = 200;
 
-  htab_delete (*tracker_ptr);
-  *tracker_ptr = NULL;
-}
+/* Initial size of the table.  It automagically grows from here.  */
+#define INITIAL_COMPLETION_HTAB_SIZE 200
 
 /* See completer.h.  */
 
-struct cleanup *
-make_cleanup_free_completion_tracker (completion_tracker_t *tracker_ptr)
+completion_tracker::completion_tracker ()
 {
-  if (*tracker_ptr == NULL)
-    return make_cleanup (null_cleanup, NULL);
+  m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+				      htab_hash_string, (htab_eq) streq,
+				      NULL, xcalloc, xfree);
+}
 
-  return make_cleanup (free_completion_tracker, tracker_ptr);
+completion_tracker::~completion_tracker ()
+{
+  xfree (m_lowest_common_denominator);
+  htab_delete (m_entries_hash);
 }
 
 /* See completer.h.  */
 
-enum maybe_add_completion_enum
-maybe_add_completion (completion_tracker_t tracker, char *name)
+bool
+completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
 {
   void **slot;
 
-  if (max_completions < 0)
-    return MAYBE_ADD_COMPLETION_OK;
   if (max_completions == 0)
-    return MAYBE_ADD_COMPLETION_MAX_REACHED;
+    return false;
 
-  gdb_assert (tracker != NULL);
+  if (htab_elements (m_entries_hash) >= max_completions)
+    return false;
 
-  if (htab_elements (tracker) >= max_completions)
-    return MAYBE_ADD_COMPLETION_MAX_REACHED;
+  slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
+  if (*slot == HTAB_EMPTY_ENTRY)
+    {
+      const char *match_for_lcd_str = name.get ();
 
-  slot = htab_find_slot (tracker, name, INSERT);
+      recompute_lowest_common_denominator (match_for_lcd_str);
 
-  if (*slot != HTAB_EMPTY_ENTRY)
-    return MAYBE_ADD_COMPLETION_DUPLICATE;
+      *slot = name.get ();
+      m_entries_vec.push_back (std::move (name));
+    }
 
-  *slot = name;
+  return true;
+}
 
-  return (htab_elements (tracker) < max_completions
-	  ? MAYBE_ADD_COMPLETION_OK
-	  : MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
+void
+completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
+{
+  if (!maybe_add_completion (std::move (name)))
+    throw_max_completions_reached_error ();
 }
 
 void
@@ -1075,10 +1075,16 @@ throw_max_completions_reached_error (void)
   throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
 }
 
-/* Generate completions all at once.  Returns a vector of unique strings
-   allocated with xmalloc.  Returns NULL if there are no completions
-   or if max_completions is 0.  If max_completions is non-negative, this will
-   return at most max_completions strings.
+void
+completion_tracker::add_completions (completion_list &&list)
+{
+  for (auto &candidate : list)
+    add_completion (std::move (candidate));
+}
+
+/* Generate completions all at once.  Does nothing if max_completions
+   is 0.  If max_completions is non-negative, this will collect at
+   most max_completions strings.
 
    TEXT is the caller's idea of the "word" we are looking at.
 
@@ -1088,77 +1094,32 @@ throw_max_completions_reached_error (void)
    POINT is the offset in that line of the cursor.  You
    should pretend that the line ends at POINT.  */
 
-VEC (char_ptr) *
-complete_line (const char *text, const char *line_buffer, int point)
+void
+complete_line (completion_tracker &tracker,
+	       const char *text, const char *line_buffer, int point)
 {
-  VEC (char_ptr) *list;
-  VEC (char_ptr) *result = NULL;
-  struct cleanup *cleanups;
-  completion_tracker_t tracker;
-  char *candidate;
-  int ix, max_reached;
-
   if (max_completions == 0)
-    return NULL;
-  list = complete_line_internal (text, line_buffer, point,
-				 handle_completions);
-  if (max_completions < 0)
-    return list;
-
-  tracker = new_completion_tracker ();
-  cleanups = make_cleanup_free_completion_tracker (&tracker);
-  make_cleanup_free_char_ptr_vec (list);
-
-  /* Do a final test for too many completions.  Individual completers may
-     do some of this, but are not required to.  Duplicates are also removed
-     here.  Otherwise the user is left scratching his/her head: readline and
-     complete_command will remove duplicates, and if removal of duplicates
-     there brings the total under max_completions the user may think gdb quit
-     searching too early.  */
-
-  for (ix = 0, max_reached = 0;
-       !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
-       ++ix)
-    {
-      enum maybe_add_completion_enum add_status;
-
-      add_status = maybe_add_completion (tracker, candidate);
-
-      switch (add_status)
-	{
-	  case MAYBE_ADD_COMPLETION_OK:
-	    VEC_safe_push (char_ptr, result, xstrdup (candidate));
-	    break;
-	  case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
-	    VEC_safe_push (char_ptr, result, xstrdup (candidate));
-	    max_reached = 1;
-	    break;
-      	  case MAYBE_ADD_COMPLETION_MAX_REACHED:
-	    gdb_assert_not_reached ("more than max completions reached");
-	  case MAYBE_ADD_COMPLETION_DUPLICATE:
-	    break;
-	}
-    }
-
-  do_cleanups (cleanups);
-
-  return result;
+    return;
+  complete_line_internal (tracker, text, line_buffer, point,
+			  handle_completions);
 }
 
 /* Complete on command names.  Used by "help".  */
 
-VEC (char_ptr) *
+void
 command_completer (struct cmd_list_element *ignore, 
+		   completion_tracker &tracker,
 		   const char *text, const char *word)
 {
-  return complete_line_internal (word, text, 
-				 strlen (text), handle_help);
+  complete_line_internal (tracker, word, text,
+			  strlen (text), handle_help);
 }
 
 /* The corresponding completer_handle_brkchars implementation.  */
 
 static void
 command_completer_handle_brkchars (struct cmd_list_element *ignore,
+				   completion_tracker &tracker,
 				   const char *text, const char *word)
 {
   set_rl_completer_word_break_characters
@@ -1167,11 +1128,11 @@ command_completer_handle_brkchars (struct cmd_list_element *ignore,
 
 /* Complete on signals.  */
 
-VEC (char_ptr) *
+void
 signal_completer (struct cmd_list_element *ignore,
+		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
-  VEC (char_ptr) *return_val = NULL;
   size_t len = strlen (word);
   int signum;
   const char *signame;
@@ -1189,10 +1150,11 @@ signal_completer (struct cmd_list_element *ignore,
 	continue;
 
       if (strncasecmp (signame, word, len) == 0)
-	VEC_safe_push (char_ptr, return_val, xstrdup (signame));
+	{
+	  gdb::unique_xmalloc_ptr<char> copy (xstrdup (signame));
+	  tracker.add_completion (std::move (copy));
+	}
     }
-
-  return return_val;
 }
 
 /* Bit-flags for selecting what the register and/or register-group
@@ -1208,12 +1170,11 @@ DEF_ENUM_FLAGS_TYPE (enum reg_completer_target, reg_completer_targets);
 /* Complete register names and/or reggroup names based on the value passed
    in TARGETS.  At least one bit in TARGETS must be set.  */
 
-static VEC (char_ptr) *
-reg_or_group_completer_1 (struct cmd_list_element *ignore,
+static void
+reg_or_group_completer_1 (completion_tracker &tracker,
 			  const char *text, const char *word,
 			  reg_completer_targets targets)
 {
-  VEC (char_ptr) *result = NULL;
   size_t len = strlen (word);
   struct gdbarch *gdbarch;
   const char *name;
@@ -1231,7 +1192,10 @@ reg_or_group_completer_1 (struct cmd_list_element *ignore,
 	   i++)
 	{
 	  if (*name != '\0' && strncmp (word, name, len) == 0)
-	    VEC_safe_push (char_ptr, result, xstrdup (name));
+	    {
+	      gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+	      tracker.add_completion (std::move (copy));
+	    }
 	}
     }
 
@@ -1245,38 +1209,42 @@ reg_or_group_completer_1 (struct cmd_list_element *ignore,
 	{
 	  name = reggroup_name (group);
 	  if (strncmp (word, name, len) == 0)
-	    VEC_safe_push (char_ptr, result, xstrdup (name));
+	    {
+	      gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+	      tracker.add_completion (std::move (copy));
+	    }
 	}
     }
-
-  return result;
 }
 
 /* Perform completion on register and reggroup names.  */
 
-VEC (char_ptr) *
+void
 reg_or_group_completer (struct cmd_list_element *ignore,
+			completion_tracker &tracker,
 			const char *text, const char *word)
 {
-  return reg_or_group_completer_1 (ignore, text, word,
-				   (complete_register_names
-				    | complete_reggroup_names));
+  reg_or_group_completer_1 (tracker, text, word,
+			    (complete_register_names
+			     | complete_reggroup_names));
 }
 
 /* Perform completion on reggroup names.  */
 
-VEC (char_ptr) *
+void
 reggroup_completer (struct cmd_list_element *ignore,
+		    completion_tracker &tracker,
 		    const char *text, const char *word)
 {
-  return reg_or_group_completer_1 (ignore, text, word,
-				   complete_reggroup_names);
+  reg_or_group_completer_1 (tracker, text, word,
+			    complete_reggroup_names);
 }
 
 /* The default completer_handle_brkchars implementation.  */
 
 static void
 default_completer_handle_brkchars (struct cmd_list_element *ignore,
+				   completion_tracker &tracker,
 				   const char *text, const char *word)
 {
   set_rl_completer_word_break_characters
@@ -1300,89 +1268,323 @@ completer_handle_brkchars_func_for_completer (completer_ftype *fn)
 /* Get the list of chars that are considered as word breaks
    for the current command.  */
 
-char *
-gdb_completion_word_break_characters (void)
+static char *
+gdb_completion_word_break_characters_throw ()
 {
-  VEC (char_ptr) *list;
+  /* New completion starting.  Get rid of the previous tracker and
+     start afresh.  */
+  delete current_completion.tracker;
+  current_completion.tracker = new completion_tracker ();
+
+  completion_tracker &tracker = *current_completion.tracker;
+
+  complete_line_internal (tracker, NULL, rl_line_buffer,
+			  rl_point, handle_brkchars);
 
-  list = complete_line_internal (rl_line_buffer, rl_line_buffer, rl_point,
-				 handle_brkchars);
-  gdb_assert (list == NULL);
   return rl_completer_word_break_characters;
 }
 
-/* Generate completions one by one for the completer.  Each time we
-   are called return another potential completion to the caller.
-   line_completion just completes on commands or passes the buck to
-   the command's completer function, the stuff specific to symbol
-   completion is in make_symbol_completion_list.
-
-   TEXT is the caller's idea of the "word" we are looking at.
+char *
+gdb_completion_word_break_characters ()
+{
+  /* New completion starting.  */
+  current_completion.aborted = false;
 
-   MATCHES is the number of matches that have currently been collected
-   from calling this completion function.  When zero, then we need to
-   initialize, otherwise the initialization has already taken place
-   and we can just return the next potential completion string.
+  TRY
+    {
+      return gdb_completion_word_break_characters_throw ();
+    }
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+      /* Set this to that gdb_rl_attempted_completion_function knows
+	 to abort early.  */
+      current_completion.aborted = true;
+    }
+  END_CATCH
 
-   LINE_BUFFER is available to be looked at; it contains the entire
-   text of the line.  POINT is the offset in that line of the cursor.
-   You should pretend that the line ends at POINT.
+  return NULL;
+}
 
-   Returns NULL if there are no more completions, else a pointer to a
-   string which is a possible completion, it is the caller's
-   responsibility to free the string.  */
+/* See completer.h.  */
 
-static char *
-line_completion_function (const char *text, int matches, 
-			  char *line_buffer, int point)
+void
+completion_tracker::recompute_lowest_common_denominator (const char *new_match)
 {
-  static VEC (char_ptr) *list = NULL;	/* Cache of completions.  */
-  static int index;			/* Next cached completion.  */
-  char *output = NULL;
-
-  if (matches == 0)
+  if (m_lowest_common_denominator == NULL)
+    {
+      /* We don't have a lowest common denominator yet, so simply take
+	 the whole NEW_MATCH as being it.  */
+      m_lowest_common_denominator = xstrdup (new_match);
+      m_lowest_common_denominator_unique = true;
+    }
+  else
     {
-      /* The caller is beginning to accumulate a new set of
-         completions, so we need to find all of them now, and cache
-         them for returning one at a time on future calls.  */
+      /* Find the common denominator between the currently-known
+	 lowest common denominator and NEW_MATCH.  That becomes the
+	 new lowest common denominator.  */
+      size_t i;
 
-      if (list)
+      for (i = 0;
+	   (new_match[i] != '\0'
+	    && new_match[i] == m_lowest_common_denominator[i]);
+	   i++)
+	;
+      if (m_lowest_common_denominator[i] != new_match[i])
 	{
-	  /* Free the storage used by LIST, but not by the strings
-	     inside.  This is because rl_complete_internal () frees
-	     the strings.  As complete_line may abort by calling
-	     `error' clear LIST now.  */
-	  VEC_free (char_ptr, list);
+	  m_lowest_common_denominator[i] = '\0';
+	  m_lowest_common_denominator_unique = false;
 	}
-      index = 0;
-      list = complete_line (text, line_buffer, point);
     }
+}
+
+/* Build a new C string that is a copy or LCD with the whitespace of
+   ORIG/ORIG_LEN preserved.
+
+   Say the user is completing a symbol name, with spaces, like:
+
+     "foo ( i"
+
+   and the resulting completion match is:
+
+     "foo(int)"
+
+   we want to end up with an inline line like:
+
+     "foo ( int)"
+      ^^^^^^^      => text from LCD [1], whitespace from ORIG preserved.
+	     ^^	   => new text from LCD
+
+   [1] - We must take characters from the LCD instead of the original
+   text, since some completions want to change upper/lowercase.  E.g.:
 
-  /* If we found a list of potential completions during initialization
-     then dole them out one at a time.  After returning the last one,
-     return NULL (and continue to do so) each time we are called after
-     that, until a new list is available.  */
+     "handle sig<>"
 
-  if (list)
+   completes to:
+
+     "handle SIG[QUIT|etc.]"
+*/
+
+static char *
+expand_preserving_ws (const char *orig, size_t orig_len,
+		      const char *lcd)
+{
+  const char *p_orig = orig;
+  const char *orig_end = orig + orig_len;
+  const char *p_lcd = lcd;
+  std::string res;
+
+  while (p_orig < orig_end)
     {
-      if (index < VEC_length (char_ptr, list))
+      if (*p_orig == ' ')
+	{
+	  while (p_orig < orig_end && *p_orig == ' ')
+	    res += *p_orig++;
+	  p_lcd = skip_spaces_const (p_lcd);
+	}
+      else
 	{
-	  output = VEC_index (char_ptr, list, index);
-	  index++;
+	  /* Take characters from the LCD instead of the original
+	     text, since some completions change upper/lowercase.
+	     E.g.:
+	       "handle sig<>"
+	     completes to:
+	       "handle SIG[QUIT|etc.]"
+	  */
+	  res += *p_lcd;
+	  p_orig++;
+	  p_lcd++;
 	}
     }
 
-#if 0
-  /* Can't do this because readline hasn't yet checked the word breaks
-     for figuring out whether to insert a quote.  */
-  if (output == NULL)
-    /* Make sure the word break characters are set back to normal for
-       the next time that readline tries to complete something.  */
-    rl_completer_word_break_characters =
-      current_language->la_word_break_characters();
-#endif
+  while (*p_lcd != '\0')
+    res += *p_lcd++;
+
+  return xstrdup (res.c_str ());
+}
+
+completion_result
+completion_tracker::build_completion_result (const char *text,
+					     int start, int end)
+{
+  completion_list &list = m_entries_vec;	/* The completions.  */
+
+  if (list.empty ())
+    return {};
+
+  /* +1 for the LCD, and +1 for NULL termination.  */
+  char **match_list = XNEWVEC (char *, 1 + list.size () + 1);
 
-  return (output);
+  /* Build replacement word, based on the LCD.  */
+
+  match_list[0]
+    = expand_preserving_ws (text, end - start,
+			    m_lowest_common_denominator);
+
+  if (m_lowest_common_denominator_unique)
+    {
+      match_list[1] = NULL;
+
+      /* If we already have a space at the end of the match, tell
+	 readline to skip appending another.  */
+      bool completion_suppress_append
+	= (match_list[0][strlen (match_list[0]) - 1] == ' ');
+
+      return completion_result (match_list, 1, completion_suppress_append);
+    }
+  else
+    {
+      int ix;
+
+      for (ix = 0; ix < list.size (); ++ix)
+	match_list[ix + 1] = list[ix].release ();
+      match_list[ix + 1] = NULL;
+
+      return completion_result (match_list, list.size (), false);
+    }
+}
+
+completion_result::completion_result ()
+  : match_list (NULL), number_matches (0),
+    completion_suppress_append (false)
+{}
+
+completion_result::completion_result (char **match_list_,
+				      size_t number_matches_,
+				      bool completion_suppress_append_)
+  : match_list (match_list_),
+    number_matches (number_matches_),
+    completion_suppress_append (completion_suppress_append_)
+{}
+
+completion_result::~completion_result ()
+{
+  reset_match_list ();
+}
+
+completion_result::completion_result (completion_result &&rhs)
+{
+  if (this == &rhs)
+    return;
+
+  reset_match_list ();
+  match_list = rhs.match_list;
+  rhs.match_list = NULL;
+  number_matches = rhs.number_matches;
+  rhs.number_matches = 0;
+}
+
+char **
+completion_result::release_match_list ()
+{
+  char **ret = match_list;
+  match_list = NULL;
+  return ret;
+}
+
+/* Compare C strings for std::sort.  */
+
+static bool
+compare_cstrings (const char *str1, const char *str2)
+{
+  return strcmp (str1, str2) < 0;
+}
+
+void
+completion_result::sort_match_list ()
+{
+  if (number_matches > 1)
+    {
+      /* Element 0 is special (it's the common prefix), leave it
+	 be.  */
+      std::sort (&match_list[1],
+		 &match_list[number_matches + 1],
+		 compare_cstrings);
+    }
+}
+
+void
+completion_result::reset_match_list ()
+{
+  if (match_list != NULL)
+    {
+      for (char **p = match_list; *p != NULL; p++)
+	xfree (*p);
+      xfree (match_list);
+      match_list = NULL;
+    }
+}
+
+/* Helper for gdb_rl_attempted_completion_function, which does most of
+   the work.  This is called by readline to build the match list
+   array, and determining the lowest common denominator.  The real
+   matches list starts at match[1], while match[0] is the slot holding
+   readline's idea of the lowest common denominator of all matches,
+   which is what readline replaces the completion "word" with.
+
+   TEXT is the caller's idea of the "word" we are looking at, as
+   computed in the handle_brkchars phase.
+
+   START is the offset from RL_LINE_BUFFER where TEXT starts.  END is
+   the offset from RL_LINE_BUFFER where TEXT ends (i.e., where
+   rl_point is).
+
+   You should thus pretend that the line ends at END (relative to
+   RL_LINE_BUFFER).
+
+   RL_LINE_BUFFER contains the entire text of the line.  RL_POINT is
+   the offset in that line of the cursor.  You should pretend that the
+   line ends at POINT.
+
+   Returns NULL if there are no completions.  */
+
+static char **
+gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
+{
+  /* Completers must be called twice.  If rl_point (i.e., END) is at
+     column 0, then readline skips the the handle_brkchars phase, and
+     so we create a tracker now in that case too.  */
+  delete current_completion.tracker;
+  current_completion.tracker = new completion_tracker ();
+
+  complete_line (*current_completion.tracker, text,
+		 rl_line_buffer, rl_point);
+
+  completion_tracker &tracker = *current_completion.tracker;
+
+  completion_result result
+    = tracker.build_completion_result (text, start, end);
+
+  rl_completion_suppress_append = result.completion_suppress_append;
+  return result.release_match_list ();
+}
+
+/* Function installed as "rl_attempted_completion_function" readline
+   hook.  Wrapper around gdb_rl_attempted_completion_function_throw
+   that catches C++ exceptions, which can't cross readline.  */
+
+char **
+gdb_rl_attempted_completion_function (const char *text, int start, int end)
+{
+  /* If we end up returning NULL, either on error, or simple because
+     there are no matches, inhibit readline's default filename
+     completer.  */
+  rl_attempted_completion_over = 1;
+
+  /* If the handle_brkchars phase was aborted, don't try
+     completing.  */
+  if (current_completion.aborted)
+    return NULL;
+
+  TRY
+    {
+      return gdb_rl_attempted_completion_function_throw (text, start, end);
+    }
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+    }
+  END_CATCH
+
+  return NULL;
 }
 
 /* Skip over the possibly quoted word STR (as defined by the quote
diff --git a/gdb/completer.h b/gdb/completer.h
index 463a53d..e554bff 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -63,44 +63,184 @@ struct match_list_displayer
   mld_read_key_ftype *read_key;
 };
 
+/* A list of completion candidates.  Each element is a malloc string,
+   because ownership of the strings is transferred to readline, which
+   calls free on each element.  */
+typedef std::vector<gdb::unique_xmalloc_ptr<char>> completion_list;
+
+/* The final result of a completion that is handed over to either
+   readline or the "completion" command (which pretends to be
+   readline).  Mainly a wrapper for a readline-style match list array,
+   though other bits of info are included too.  */
+
+struct completion_result
+{
+  /* Create an empty result.  */
+  completion_result ();
+
+  /* Create a result.  */
+  completion_result (char **match_list, size_t number_matches,
+		     bool completion_suppress_append);
+
+  /* Destroy a result.  */
+  ~completion_result ();
+
+  /* Disable copying, since we don't need it.  */
+  completion_result (const completion_result &rhs) = delete;
+  void operator= (const completion_result &rhs) = delete;
+
+  /* Move a result.  */
+  completion_result (completion_result &&rhs);
+
+  /* Release ownership of the match list array.  */
+  char **release_match_list ();
+
+  /* Sort the match list.  */
+  void sort_match_list ();
+
+private:
+  /* Destroy the match list array and its contents.  */
+  void reset_match_list ();
+
+public:
+  /* (There's no point in making these fields private, since the whole
+     point of this wrapper is to build data in the layout expected by
+     readline.  Making them private would require adding getters for
+     the "complete" command, which would expose the same
+     implementation details anyway.)  */
+
+  /* The match list array, in the format that readline expects.
+     match_list[0] contains the common prefix.  The real match list
+     starts at index 1.  The list is NULL terminated.  If there's only
+     one match, then match_list[1] is NULL.  If there are no matches,
+     then this is NULL.  */
+  char **match_list;
+  /* The number of matched completions in MATCH_LIST.  Does not
+     include the NULL terminator or the common prefix.  */
+  size_t number_matches;
+
+  /* Whether readline should suppress appending a whitespace, when
+     there's only one possible completion.  */
+  bool completion_suppress_append;
+};
+
+/* Object used by completers to build a completion match list to hand
+   over to readline.  It tracks:
+
+   - How many unique completions have been generated, to terminate
+     completion list generation early if the list has grown to a size
+     so large as to be useless.  This helps avoid GDB seeming to lock
+     up in the event the user requests to complete on something vague
+     that necessitates the time consuming expansion of many symbol
+     tables.
+*/
+class completion_tracker
+{
+public:
+  completion_tracker ();
+  ~completion_tracker ();
+
+  /* Disable copy.  */
+  completion_tracker (const completion_tracker &rhs) = delete;
+  void operator= (const completion_tracker &rhs) = delete;
+
+  /* Add the completion NAME to the list of generated completions if
+     it is not there already.  If too many completions were already
+     found, this throws an error.  */
+  void add_completion (gdb::unique_xmalloc_ptr<char> name);
+
+  /* Add all completions matches in LIST.  Elements are moved out of
+     LIST.  */
+  void add_completions (completion_list &&list);
+
+  /* True if we have any completion match recorded.  */
+  bool have_completions () const
+  { return !m_entries_vec.empty (); }
+
+  /* Build a completion_result containing the list of completion
+     matches to hand over to readline.  The parameters are as in
+     rl_attempted_completion_function.  */
+  completion_result build_completion_result (const char *text,
+					     int start, int end);
+
+private:
+
+  /* Add the completion NAME to the list of generated completions if
+     it is not there already.  If false is returned, too many
+     completions were found.  */
+  bool maybe_add_completion (gdb::unique_xmalloc_ptr<char> name);
+
+  /* Given a new match, recompute the lowest common denominator (LCD)
+     to hand over to readline.  */
+  void recompute_lowest_common_denominator (const char *new_match);
+
+  /* The completion matches found so far, in a vector.  */
+  completion_list m_entries_vec;
+
+  /* The completion matches found so far, in a hash table, for
+     duplicate elimination as entries are added.  Otherwise the user
+     is left scratching his/her head: readline and complete_command
+     will remove duplicates, and if removal of duplicates there brings
+     the total under max_completions the user may think gdb quit
+     searching too early.  */
+  htab_t m_entries_hash;
+
+  /* Our idea of lowest common denominator to hand over to readline.  */
+  char *m_lowest_common_denominator = NULL;
+
+  /* If true, the LCD is unique.  I.e., all completion candidates had
+     the same string.  */
+  bool m_lowest_common_denominator_unique = false;
+};
+
 extern void gdb_display_match_list (char **matches, int len, int max,
 				    const struct match_list_displayer *);
 
 extern const char *get_max_completions_reached_message (void);
 
-extern VEC (char_ptr) *complete_line (const char *text,
-				      const char *line_buffer,
-				      int point);
+extern void complete_line (completion_tracker &tracker,
+			   const char *text,
+			   const char *line_buffer,
+			   int point);
 
-extern char *readline_line_completion_function (const char *text,
-						int matches);
+extern char **gdb_rl_attempted_completion_function (const char *text,
+						    int start, int end);
 
-extern VEC (char_ptr) *noop_completer (struct cmd_list_element *,
-				       const char *, const char *);
+extern void noop_completer (struct cmd_list_element *,
+			    completion_tracker &tracker,
+			    const char *, const char *);
 
-extern VEC (char_ptr) *filename_completer (struct cmd_list_element *,
-					   const char *, const char *);
+extern void filename_completer (struct cmd_list_element *,
+				completion_tracker &tracker,
+				const char *, const char *);
 
-extern VEC (char_ptr) *expression_completer (struct cmd_list_element *,
-					     const char *, const char *);
+extern void expression_completer (struct cmd_list_element *,
+				  completion_tracker &tracker,
+				  const char *, const char *);
 
-extern VEC (char_ptr) *location_completer (struct cmd_list_element *,
-					   const char *, const char *);
+extern void location_completer (struct cmd_list_element *,
+				completion_tracker &tracker,
+				const char *, const char *);
 
-extern VEC (char_ptr) *symbol_completer (struct cmd_list_element *,
-					 const char *, const char *);
+extern void symbol_completer (struct cmd_list_element *,
+			      completion_tracker &tracker,
+			      const char *, const char *);
 
-extern VEC (char_ptr) *command_completer (struct cmd_list_element *,
-					  const char *, const char *);
+extern void command_completer (struct cmd_list_element *,
+			       completion_tracker &tracker,
+			       const char *, const char *);
 
-extern VEC (char_ptr) *signal_completer (struct cmd_list_element *,
-					 const char *, const char *);
+extern void signal_completer (struct cmd_list_element *,
+			      completion_tracker &tracker,
+			      const char *, const char *);
 
-extern VEC (char_ptr) *reg_or_group_completer (struct cmd_list_element *,
-					       const char *, const char *);
+extern void reg_or_group_completer (struct cmd_list_element *,
+				    completion_tracker &tracker,
+				    const char *, const char *);
 
-extern VEC (char_ptr) *reggroup_completer (struct cmd_list_element *,
-					   const char *, const char *);
+extern void reggroup_completer (struct cmd_list_element *,
+				completion_tracker &tracker,
+				const char *, const char *);
 
 extern const char *get_gdb_completer_quote_characters (void);
 
@@ -134,59 +274,6 @@ extern const char *skip_quoted (const char *);
 
 extern int max_completions;
 
-/* Object to track how many unique completions have been generated.
-   Used to limit the size of generated completion lists.  */
-
-typedef htab_t completion_tracker_t;
-
-/* Create a new completion tracker.
-   The result is a hash table to track added completions, or NULL
-   if max_completions <= 0.  If max_completions < 0, tracking is disabled.
-   If max_completions == 0, the max is indeed zero.  */
-
-extern completion_tracker_t new_completion_tracker (void);
-
-/* Make a cleanup to free a completion tracker, and reset its pointer
-   to NULL.  */
-
-extern struct cleanup *make_cleanup_free_completion_tracker
-		      (completion_tracker_t *tracker_ptr);
-
-/* Return values for maybe_add_completion.  */
-
-enum maybe_add_completion_enum
-{
-  /* NAME has been recorded and max_completions has not been reached,
-     or completion tracking is disabled (max_completions < 0).  */
-  MAYBE_ADD_COMPLETION_OK,
-
-  /* NAME has been recorded and max_completions has been reached
-     (thus the caller can stop searching).  */
-  MAYBE_ADD_COMPLETION_OK_MAX_REACHED,
-
-  /* max-completions entries has been reached.
-     Whether NAME is a duplicate or not is not determined.  */
-  MAYBE_ADD_COMPLETION_MAX_REACHED,
-
-  /* NAME has already been recorded.
-     Note that this is never returned if completion tracking is disabled
-     (max_completions < 0).  */
-  MAYBE_ADD_COMPLETION_DUPLICATE
-};
-
-/* Add the completion NAME to the list of generated completions if
-   it is not there already.
-   If max_completions is negative, nothing is done, not even watching
-   for duplicates, and MAYBE_ADD_COMPLETION_OK is always returned.
-
-   If MAYBE_ADD_COMPLETION_MAX_REACHED is returned, callers are required to
-   record at least one more completion.  The final list will be pruned to
-   max_completions, but recording at least one more than max_completions is
-   the signal to the completion machinery that too many completions were
-   found.  */
-
-extern enum maybe_add_completion_enum
-  maybe_add_completion (completion_tracker_t tracker, char *name);
 
 /* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR.  */ 
 
diff --git a/gdb/corefile.c b/gdb/corefile.c
index 13a90b9..04244c3 100644
--- a/gdb/corefile.c
+++ b/gdb/corefile.c
@@ -485,8 +485,9 @@ set_gnutarget_command (char *ignore, int from_tty,
 
 /* A completion function for "set gnutarget".  */
 
-static VEC (char_ptr) *
+static void
 complete_set_gnutarget (struct cmd_list_element *cmd,
+			completion_tracker &tracker,
 			const char *text, const char *word)
 {
   static const char **bfd_targets;
@@ -504,7 +505,7 @@ complete_set_gnutarget (struct cmd_list_element *cmd,
       bfd_targets[last + 1] = NULL;
     }
 
-  return complete_on_enum (bfd_targets, text, word);
+  complete_on_enum (tracker, bfd_targets, text, word);
 }
 
 /* Set the gnutarget.  */
diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c
index b72f227..f4cf944 100644
--- a/gdb/cp-abi.c
+++ b/gdb/cp-abi.c
@@ -355,8 +355,9 @@ set_cp_abi_cmd (char *args, int from_tty)
 
 /* A completion function for "set cp-abi".  */
 
-static VEC (char_ptr) *
+static void
 cp_abi_completer (struct cmd_list_element *ignore,
+		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
   static const char **cp_abi_names;
@@ -371,7 +372,7 @@ cp_abi_completer (struct cmd_list_element *ignore,
       cp_abi_names[i] = NULL;
     }
 
-  return complete_on_enum (cp_abi_names, text, word);
+  complete_on_enum (tracker, cp_abi_names, text, word);
 }
 
 /* Show the currently selected C++ ABI.  */
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 434b2e6..74cceaa 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -239,7 +239,7 @@ static const struct language_defn d_language_defn =
   1,				/* C-style arrays.  */
   0,				/* String lower bound.  */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   d_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/disasm.c b/gdb/disasm.c
index 83f9871..76c4ff3 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -1030,8 +1030,9 @@ The following disassembler options are supported for use with the\n\
 
 /* A completion function for "set disassembler".  */
 
-static VEC (char_ptr) *
+static void
 disassembler_options_completer (struct cmd_list_element *ignore,
+				completion_tracker &tracker,
 				const char *text, const char *word)
 {
   struct gdbarch *gdbarch = get_current_arch ();
@@ -1044,9 +1045,8 @@ disassembler_options_completer (struct cmd_list_element *ignore,
       if (separator != NULL)
 	text = separator + 1;
       text = skip_spaces_const (text);
-      return complete_on_enum (opts->name, text, word);
+      complete_on_enum (tracker, opts->name, text, word);
     }
-  return NULL;
 }
 
 
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index a9663d4..e1184ee 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -228,11 +228,13 @@ f_word_break_characters (void)
 /* Consider the modules separator :: as a valid symbol name character
    class.  */
 
-static VEC (char_ptr) *
-f_make_symbol_completion_list (const char *text, const char *word,
-			       enum type_code code)
+static void
+f_collect_symbol_completion_matches (completion_tracker &tracker,
+				     const char *text, const char *word,
+				     enum type_code code)
 {
-  return default_make_symbol_completion_list_break_on (text, word, ":", code);
+  default_collect_symbol_completion_matches_break_on (tracker,
+						      text, word, ":", code);
 }
 
 static const char *f_extensions[] =
@@ -282,7 +284,7 @@ const struct language_defn f_language_defn =
   0,				/* arrays are first-class (not c-style) */
   1,				/* String lower bound */
   f_word_break_characters,
-  f_make_symbol_completion_list,
+  f_collect_symbol_completion_matches,
   f_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 3b80cec..f3b4f5c 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -600,13 +600,13 @@ static const struct language_defn go_language_defn =
   1,				/* C-style arrays.  */
   0,				/* String lower bound.  */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   go_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,
+  NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
   &default_varobj_ops,
   NULL,
diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c
index 0c6419d..5501d31 100644
--- a/gdb/guile/scm-cmd.c
+++ b/gdb/guile/scm-cmd.c
@@ -350,9 +350,8 @@ cmdscm_bad_completion_result (const char *msg, SCM completion)
    The result is a boolean indicating success.  */
 
 static int
-cmdscm_add_completion (SCM completion, VEC (char_ptr) **result)
+cmdscm_add_completion (SCM completion, completion_tracker &tracker)
 {
-  char *item;
   SCM except_scm;
 
   if (!scm_is_string (completion))
@@ -363,8 +362,9 @@ cmdscm_add_completion (SCM completion, VEC (char_ptr) **result)
       return 0;
     }
 
-  item = gdbscm_scm_to_string (completion, NULL, host_charset (), 1,
-			       &except_scm);
+  gdb::unique_xmalloc_ptr<char> item
+    (gdbscm_scm_to_string (completion, NULL, host_charset (), 1,
+			   &except_scm));
   if (item == NULL)
     {
       /* Inform the user, but otherwise ignore the entire result.  */
@@ -372,21 +372,21 @@ cmdscm_add_completion (SCM completion, VEC (char_ptr) **result)
       return 0;
     }
 
-  VEC_safe_push (char_ptr, *result, item);
+  tracker.add_completion (std::move (item));
 
   return 1;
 }
 
 /* Called by gdb for command completion.  */
 
-static VEC (char_ptr) *
+static void
 cmdscm_completer (struct cmd_list_element *command,
+		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
   command_smob *c_smob/*obj*/ = (command_smob *) get_cmd_context (command);
   SCM completer_result_scm;
   SCM text_scm, word_scm, result_scm;
-  VEC (char_ptr) *result = NULL;
 
   gdb_assert (c_smob != NULL);
   gdb_assert (gdbscm_is_procedure (c_smob->complete));
@@ -408,7 +408,7 @@ cmdscm_completer (struct cmd_list_element *command,
     {
       /* Inform the user, but otherwise ignore.  */
       gdbscm_print_gdb_exception (SCM_BOOL_F, completer_result_scm);
-      goto done;
+      return;
     }
 
   if (gdbscm_is_true (scm_list_p (completer_result_scm)))
@@ -419,11 +419,8 @@ cmdscm_completer (struct cmd_list_element *command,
 	{
 	  SCM next = scm_car (list);
 
-	  if (!cmdscm_add_completion (next, &result))
-	    {
-	      VEC_free (char_ptr, result);
-	      goto done;
-	    }
+	  if (!cmdscm_add_completion (next, tracker))
+	    break;
 
 	  list = scm_cdr (list);
 	}
@@ -437,17 +434,13 @@ cmdscm_completer (struct cmd_list_element *command,
 	{
 	  if (gdbscm_is_exception (next))
 	    {
-	      /* Inform the user, but otherwise ignore the entire result.  */
+	      /* Inform the user.  */
 	      gdbscm_print_gdb_exception (SCM_BOOL_F, completer_result_scm);
-	      VEC_free (char_ptr, result);
-	      goto done;
+	      break;
 	    }
 
-	  if (!cmdscm_add_completion (next, &result))
-	    {
-	      VEC_free (char_ptr, result);
-	      goto done;
-	    }
+	  if (cmdscm_add_completion (next, tracker))
+	    break;
 
 	  next = itscm_safe_call_next_x (iter, NULL);
 	}
@@ -458,9 +451,6 @@ cmdscm_completer (struct cmd_list_element *command,
       cmdscm_bad_completion_result (_("Bad completer result: "),
 				    completer_result_scm);
     }
-
- done:
-  return result;
 }
 
 /* Helper for gdbscm_make_command which locates the command list to use and
diff --git a/gdb/infrun.c b/gdb/infrun.c
index d0504de..a8b9011 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -8695,11 +8695,11 @@ Are you sure you want to change it? "),
 
 /* Complete the "handle" command.  */
 
-static VEC (char_ptr) *
+static void
 handle_completer (struct cmd_list_element *ignore,
+		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
-  VEC (char_ptr) *vec_signals, *vec_keywords, *return_val;
   static const char * const keywords[] =
     {
       "all",
@@ -8714,13 +8714,8 @@ handle_completer (struct cmd_list_element *ignore,
       NULL,
     };
 
-  vec_signals = signal_completer (ignore, text, word);
-  vec_keywords = complete_on_enum (keywords, word, word);
-
-  return_val = VEC_merge (char_ptr, vec_signals, vec_keywords);
-  VEC_free (char_ptr, vec_signals);
-  VEC_free (char_ptr, vec_keywords);
-  return return_val;
+  signal_completer (ignore, tracker, text, word);
+  complete_on_enum (tracker, keywords, word, word);
 }
 
 enum gdb_signal
diff --git a/gdb/interps.c b/gdb/interps.c
index af86390..4de7c4e 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -452,13 +452,13 @@ interpreter_exec_cmd (char *args, int from_tty)
 
 /* See interps.h.  */
 
-VEC (char_ptr) *
+void
 interpreter_completer (struct cmd_list_element *ignore,
+		       completion_tracker &tracker,
 		       const char *text, const char *word)
 {
   struct interp_factory *interp;
   int textlen;
-  VEC (char_ptr) *matches = NULL;
   int ix;
 
   textlen = strlen (text);
@@ -485,11 +485,9 @@ interpreter_completer (struct cmd_list_element *ignore,
 	      match[text - word] = '\0';
 	      strcat (match, interp->name);
 	    }
-	  VEC_safe_push (char_ptr, matches, match);
+	  tracker.add_completion (gdb::unique_xmalloc_ptr<char> (match));
 	}
     }
-
-  return matches;
 }
 
 struct interp *
diff --git a/gdb/interps.h b/gdb/interps.h
index 1b9580c..b20cf5c 100644
--- a/gdb/interps.h
+++ b/gdb/interps.h
@@ -138,9 +138,10 @@ extern void interp_pre_command_loop (struct interp *interp);
 
 /* List the possible interpreters which could complete the given
    text.  */
-extern VEC (char_ptr) *interpreter_completer (struct cmd_list_element *ignore,
-					      const char *text,
-					      const char *word);
+extern void interpreter_completer (struct cmd_list_element *ignore,
+				   completion_tracker &tracker,
+				   const char *text,
+				   const char *word);
 
 /* well-known interpreters */
 #define INTERP_CONSOLE		"console"
diff --git a/gdb/language.c b/gdb/language.c
index e9d9803..d30f4f0 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -855,7 +855,7 @@ const struct language_defn unknown_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   unknown_language_arch_info,	/* la_language_arch_info.  */
   default_print_array_index,
   default_pass_by_reference,
@@ -905,7 +905,7 @@ const struct language_defn auto_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   unknown_language_arch_info,	/* la_language_arch_info.  */
   default_print_array_index,
   default_pass_by_reference,
@@ -953,7 +953,7 @@ const struct language_defn local_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   unknown_language_arch_info,	/* la_language_arch_info.  */
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/language.h b/gdb/language.h
index cc0df8c..7ce4f7f 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -134,7 +134,7 @@ struct language_arch_info
    transformed for lookup.  */
 
 typedef int (*symbol_name_cmp_ftype) (const char *symbol_search_name,
-					  const char *lookup_name);
+				      const char *lookup_name);
 
 /* Structure tying together assorted information about a language.  */
 
@@ -323,14 +323,16 @@ struct language_defn
     /* The list of characters forming word boundaries.  */
     const char *(*la_word_break_characters) (void);
 
-    /* Should return a vector of all symbols which are possible
+    /* Add to the completion tracker all symbols which are possible
        completions for TEXT.  WORD is the entire command on which the
        completion is being made.  If CODE is TYPE_CODE_UNDEF, then all
        symbols should be examined; otherwise, only STRUCT_DOMAIN
        symbols whose type has a code of CODE should be matched.  */
-    VEC (char_ptr) *(*la_make_symbol_completion_list) (const char *text,
-						       const char *word,
-						       enum type_code code);
+    void (*la_collect_symbol_completion_matches)
+      (completion_tracker &tracker,
+       const char *text,
+       const char *word,
+       enum type_code code);
 
     /* The per-architecture (OS/ABI) language information.  */
     void (*la_language_arch_info) (struct gdbarch *,
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 35840bf..0035a52 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -388,7 +388,7 @@ const struct language_defn m2_language_defn =
   0,				/* arrays are first-class (not c-style) */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   m2_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 934d9c6..af27268 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -397,7 +397,7 @@ const struct language_defn objc_language_defn = {
   1,				/* C-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   c_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index df57b61..116d107 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1077,7 +1077,7 @@ const struct language_defn opencl_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   opencl_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index c4fe2f6..77913fc 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -451,7 +451,7 @@ const struct language_defn pascal_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   pascal_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index b9a866e..b9f6037 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -242,10 +242,21 @@ cmdpy_completer_helper (struct cmd_list_element *command,
 					 NULL));
   if (textobj == NULL)
     error (_("Could not convert argument to Python string."));
-  gdbpy_ref<> wordobj (PyUnicode_Decode (word, strlen (word), host_charset (),
-					 NULL));
-  if (wordobj == NULL)
-    error (_("Could not convert argument to Python string."));
+
+  gdbpy_ref<> wordobj;
+  if (word == NULL)
+    {
+      /* "brkchars" phase.  */
+      wordobj.reset (Py_None);
+      Py_INCREF (Py_None);
+    }
+  else
+    {
+      wordobj.reset (PyUnicode_Decode (word, strlen (word), host_charset (),
+				       NULL));
+      if (wordobj == NULL)
+	error (_("Could not convert argument to Python string."));
+    }
 
   gdbpy_ref<> resultobj (PyObject_CallMethodObjArgs ((PyObject *) obj,
 						     complete_cst,
@@ -267,6 +278,7 @@ cmdpy_completer_helper (struct cmd_list_element *command,
 
 static void
 cmdpy_completer_handle_brkchars (struct cmd_list_element *command,
+				 completion_tracker &tracker,
 				 const char *text, const char *word)
 {
   gdbpy_enter enter_py (get_current_arch (), current_language);
@@ -300,19 +312,18 @@ cmdpy_completer_handle_brkchars (struct cmd_list_element *command,
 	     adjust the break characters accordingly.  */
 	  brkchars_fn = (completer_handle_brkchars_func_for_completer
 			 (completers[value].completer));
-	  brkchars_fn (command, text, word);
+	  brkchars_fn (command, tracker, text, word);
 	}
     }
 }
 
 /* Called by gdb for command completion.  */
 
-static VEC (char_ptr) *
+static void
 cmdpy_completer (struct cmd_list_element *command,
+		 completion_tracker &tracker,
 		 const char *text, const char *word)
 {
-  VEC (char_ptr) *result = NULL;
-
   gdbpy_enter enter_py (get_current_arch (), current_language);
 
   /* Calling our helper to obtain the PyObject of the Python
@@ -320,12 +331,10 @@ cmdpy_completer (struct cmd_list_element *command,
   gdbpy_ref<> resultobj (cmdpy_completer_helper (command, text, word));
 
   /* If the result object of calling the Python function is NULL, it
-     means that there was an error.  In this case, just give up and
-     return NULL.  */
+     means that there was an error.  In this case, just give up.  */
   if (resultobj == NULL)
-    return NULL;
+    return;
 
-  result = NULL;
   if (PyInt_Check (resultobj.get ()))
     {
       /* User code may also return one of the completion constants,
@@ -338,15 +347,16 @@ cmdpy_completer (struct cmd_list_element *command,
 	  PyErr_Clear ();
 	}
       else if (value >= 0 && value < (long) N_COMPLETERS)
-	result = completers[value].completer (command, text, word);
+	completers[value].completer (command, tracker, text, word);
     }
   else
     {
       gdbpy_ref<> iter (PyObject_GetIter (resultobj.get ()));
 
       if (iter == NULL)
-	return NULL;
+	return;
 
+      bool got_matches = false;
       while (true)
 	{
 	  gdbpy_ref<> elt (PyIter_Next (iter.get ()));
@@ -366,16 +376,15 @@ cmdpy_completer (struct cmd_list_element *command,
 	      PyErr_Clear ();
 	      continue;
 	    }
-	  VEC_safe_push (char_ptr, result, item.release ());
+	  tracker.add_completion (std::move (item));
+	  got_matches = true;
 	}
 
       /* If we got some results, ignore problems.  Otherwise, report
 	 the problem.  */
-      if (result != NULL && PyErr_Occurred ())
+      if (got_matches && PyErr_Occurred ())
 	PyErr_Clear ();
     }
-
-  return result;
 }
 
 /* Helper for cmdpy_init which locates the command list to use and
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index d4cda1f..ef41f56 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2184,7 +2184,7 @@ static const struct language_defn rust_language_defn =
   1,				/* c-style arrays */
   0,				/* String lower bound */
   default_word_break_characters,
-  default_make_symbol_completion_list,
+  default_collect_symbol_completion_matches,
   rust_language_arch_info,
   default_print_array_index,
   default_pass_by_reference,
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 09c9411b..cd78a16 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4758,44 +4758,13 @@ compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
   return 1;
 }
 
-/* Free any memory associated with a completion list.  */
-
-static void
-free_completion_list (VEC (char_ptr) **list_ptr)
-{
-  int i;
-  char *p;
-
-  for (i = 0; VEC_iterate (char_ptr, *list_ptr, i, p); ++i)
-    xfree (p);
-  VEC_free (char_ptr, *list_ptr);
-}
-
-/* Callback for make_cleanup.  */
-
-static void
-do_free_completion_list (void *list)
-{
-  free_completion_list ((VEC (char_ptr) **) list);
-}
-
-static VEC (char_ptr) *return_val;
-
-/* Tracker for how many unique completions have been generated.  Used
-   to terminate completion list generation early if the list has grown
-   to a size so large as to be useless.  This helps avoid GDB seeming
-   to lock up in the event the user requests to complete on something
-   vague that necessitates the time consuming expansion of many symbol
-   tables.  */
-
-static completion_tracker_t completion_tracker;
-
 /*  Test to see if the symbol specified by SYMNAME (which is already
    demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
    characters.  If so, add it to the current completion list.  */
 
 static void
-completion_list_add_name (const char *symname,
+completion_list_add_name (completion_tracker &tracker,
+			  const char *symname,
 			  const char *sym_text, int sym_text_len,
 			  const char *text, const char *word)
 {
@@ -4808,7 +4777,6 @@ completion_list_add_name (const char *symname,
 
   {
     char *newobj;
-    enum maybe_add_completion_enum add_status;
 
     if (word == sym_text)
       {
@@ -4830,45 +4798,33 @@ completion_list_add_name (const char *symname,
 	strcat (newobj, symname);
       }
 
-    add_status = maybe_add_completion (completion_tracker, newobj);
+    gdb::unique_xmalloc_ptr<char> completion (newobj);
 
-    switch (add_status)
-      {
-      case MAYBE_ADD_COMPLETION_OK:
-	VEC_safe_push (char_ptr, return_val, newobj);
-	break;
-      case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
-	VEC_safe_push (char_ptr, return_val, newobj);
-	throw_max_completions_reached_error ();
-      case MAYBE_ADD_COMPLETION_MAX_REACHED:
-	xfree (newobj);
-	throw_max_completions_reached_error ();
-      case MAYBE_ADD_COMPLETION_DUPLICATE:
-	xfree (newobj);
-	break;
-      }
+    tracker.add_completion (std::move (completion));
   }
 }
 
 /* completion_list_add_name wrapper for struct symbol.  */
 
 static void
-completion_list_add_symbol (symbol *sym,
+completion_list_add_symbol (completion_tracker &tracker,
+			    symbol *sym,
 			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
-  completion_list_add_name (SYMBOL_NATURAL_NAME (sym),
+  completion_list_add_name (tracker, SYMBOL_NATURAL_NAME (sym),
 			    sym_text, sym_text_len, text, word);
 }
 
 /* completion_list_add_name wrapper for struct minimal_symbol.  */
 
 static void
-completion_list_add_msymbol (minimal_symbol *sym,
+completion_list_add_msymbol (completion_tracker &tracker,
+			     minimal_symbol *sym,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
-  completion_list_add_name (MSYMBOL_NATURAL_NAME (sym),
+  completion_list_add_name (tracker, MSYMBOL_NATURAL_NAME (sym),
 			    sym_text, sym_text_len, text, word);
 }
 
@@ -4876,7 +4832,8 @@ completion_list_add_msymbol (minimal_symbol *sym,
    again and feed all the selectors into the mill.  */
 
 static void
-completion_list_objc_symbol (struct minimal_symbol *msymbol,
+completion_list_objc_symbol (completion_tracker &tracker,
+			     struct minimal_symbol *msymbol,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
@@ -4894,7 +4851,8 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
 
   if (sym_text[0] == '[')
     /* Complete on shortened method method.  */
-    completion_list_add_name (method + 1, sym_text, sym_text_len, text, word);
+    completion_list_add_name (tracker, method + 1,
+			      sym_text, sym_text_len, text, word);
 
   while ((strlen (method) + 1) >= tmplen)
     {
@@ -4915,9 +4873,11 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
       memcpy (tmp, method, (category - method));
       tmp[category - method] = ' ';
       memcpy (tmp + (category - method) + 1, selector, strlen (selector) + 1);
-      completion_list_add_name (tmp, sym_text, sym_text_len, text, word);
+      completion_list_add_name (tracker, tmp,
+				sym_text, sym_text_len, text, word);
       if (sym_text[0] == '[')
-	completion_list_add_name (tmp + 1, sym_text, sym_text_len, text, word);
+	completion_list_add_name (tracker, tmp + 1,
+				  sym_text, sym_text_len, text, word);
     }
 
   if (selector != NULL)
@@ -4928,7 +4888,8 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
       if (tmp2 != NULL)
 	*tmp2 = '\0';
 
-      completion_list_add_name (tmp, sym_text, sym_text_len, text, word);
+      completion_list_add_name (tracker, tmp,
+				sym_text, sym_text_len, text, word);
     }
 }
 
@@ -4979,9 +4940,10 @@ language_search_unquoted_string (const char *text, const char *p)
 }
 
 static void
-completion_list_add_fields (struct symbol *sym, const char *sym_text,
-			    int sym_text_len, const char *text,
-			    const char *word)
+completion_list_add_fields (completion_tracker &tracker,
+			    struct symbol *sym,
+			    const char *sym_text, int sym_text_len,
+			    const char *text, const char *word)
 {
   if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
     {
@@ -4992,7 +4954,7 @@ completion_list_add_fields (struct symbol *sym, const char *sym_text,
       if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
 	for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
 	  if (TYPE_FIELD_NAME (t, j))
-	    completion_list_add_name (TYPE_FIELD_NAME (t, j),
+	    completion_list_add_name (tracker, TYPE_FIELD_NAME (t, j),
 				      sym_text, sym_text_len, text, word);
     }
 }
@@ -5001,6 +4963,7 @@ completion_list_add_fields (struct symbol *sym, const char *sym_text,
 
 static void
 add_symtab_completions (struct compunit_symtab *cust,
+			completion_tracker &tracker,
 			const char *sym_text, int sym_text_len,
 			const char *text, const char *word,
 			enum type_code code)
@@ -5022,18 +4985,18 @@ add_symtab_completions (struct compunit_symtab *cust,
 	  if (code == TYPE_CODE_UNDEF
 	      || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
-	    completion_list_add_symbol (sym,
+	    completion_list_add_symbol (tracker, sym,
 					sym_text, sym_text_len,
 					text, word);
 	}
     }
 }
 
-static void
-default_make_symbol_completion_list_break_on_1 (const char *text,
-						const char *word,
-						const char *break_on,
-						enum type_code code)
+void
+default_collect_symbol_completion_matches_break_on
+  (completion_tracker &tracker,
+   const char *text, const char *word,
+   const char *break_on, enum type_code code)
 {
   /* Problem: All of the symbols have to be copied because readline
      frees them.  I'm not going to worry about this; hopefully there
@@ -5050,7 +5013,6 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
   const char *sym_text;
   /* Length of sym_text.  */
   int sym_text_len;
-  struct cleanup *cleanups;
 
   /* Now look for the symbol we are supposed to complete on.  */
   {
@@ -5120,9 +5082,6 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
     }
   gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
 
-  completion_tracker = new_completion_tracker ();
-  cleanups = make_cleanup_free_completion_tracker (&completion_tracker);
-
   /* At this point scan through the misc symbol vectors and add each
      symbol you find to the list.  Eventually we want to ignore
      anything that isn't a text symbol (everything else will be
@@ -5133,18 +5092,21 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
       ALL_MSYMBOLS (objfile, msymbol)
 	{
 	  QUIT;
-	  completion_list_add_msymbol (msymbol, sym_text, sym_text_len, text,
-				       word);
 
-	  completion_list_objc_symbol (msymbol, sym_text, sym_text_len, text,
-				       word);
+	  completion_list_add_msymbol (tracker,
+				       msymbol, sym_text, sym_text_len,
+				       text, word);
+
+	  completion_list_objc_symbol (tracker,
+				       msymbol, sym_text, sym_text_len,
+				       text, word);
 	}
     }
 
   /* Add completions for all currently loaded symbol tables.  */
   ALL_COMPUNITS (objfile, cust)
-    add_symtab_completions (cust, sym_text, sym_text_len, text, word,
-			    code);
+    add_symtab_completions (cust, tracker,
+			    sym_text, sym_text_len, text, word, code);
 
   /* Look through the partial symtabs for all symbols which begin by
      matching SYM_TEXT.  Expand all CUs that you find to the list.  */
@@ -5158,6 +5120,7 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
 			   [&] (compunit_symtab *symtab) /* expansion notify */
 			     {
 			       add_symtab_completions (symtab,
+						       tracker,
 						       sym_text, sym_text_len,
 						       text, word, code);
 			     },
@@ -5180,14 +5143,17 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
 	  {
 	    if (code == TYPE_CODE_UNDEF)
 	      {
-		completion_list_add_symbol (sym, sym_text, sym_text_len, text,
+		completion_list_add_symbol (tracker, sym,
+					    sym_text, sym_text_len, text,
 					    word);
-		completion_list_add_fields (sym, sym_text, sym_text_len, text,
+		completion_list_add_fields (tracker, sym,
+					    sym_text, sym_text_len, text,
 					    word);
 	      }
 	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
-	      completion_list_add_symbol (sym, sym_text, sym_text_len, text,
+	      completion_list_add_symbol (tracker, sym,
+					  sym_text, sym_text_len, text,
 					  word);
 	  }
 
@@ -5205,11 +5171,13 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
     {
       if (surrounding_static_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
-	  completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
+	  completion_list_add_fields (tracker, sym,
+				      sym_text, sym_text_len, text, word);
 
       if (surrounding_global_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
-	  completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
+	  completion_list_add_fields (tracker, sym,
+				      sym_text, sym_text_len, text, word);
     }
 
   /* Skip macros if we are completing a struct tag -- arguable but
@@ -5225,7 +5193,7 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
 				 macro_source_file *,
 				 int)
 	{
-	  completion_list_add_name (macro_name,
+	  completion_list_add_name (tracker, macro_name,
 				    sym_text, sym_text_len,
 				    text, word);
 	};
@@ -5248,74 +5216,53 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
       /* User-defined macros are always visible.  */
       macro_for_each (macro_user_macros, add_macro_name);
     }
-
-  do_cleanups (cleanups);
 }
 
-VEC (char_ptr) *
-default_make_symbol_completion_list_break_on (const char *text,
-					      const char *word,
-					      const char *break_on,
-					      enum type_code code)
-{
-  struct cleanup *back_to;
-
-  return_val = NULL;
-  back_to = make_cleanup (do_free_completion_list, &return_val);
-
-  TRY
-    {
-      default_make_symbol_completion_list_break_on_1 (text, word,
-						      break_on, code);
-    }
-  CATCH (except, RETURN_MASK_ERROR)
-    {
-      if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
-	throw_exception (except);
-    }
-  END_CATCH
-
-  discard_cleanups (back_to);
-  return return_val;
-}
-
-VEC (char_ptr) *
-default_make_symbol_completion_list (const char *text, const char *word,
-				     enum type_code code)
+void
+default_collect_symbol_completion_matches (completion_tracker &tracker,
+					   const char *text, const char *word,
+					   enum type_code code)
 {
-  return default_make_symbol_completion_list_break_on (text, word, "", code);
+  return default_collect_symbol_completion_matches_break_on (tracker,
+							     text, word, "",
+							     code);
 }
 
 /* Return a vector of all symbols (regardless of class) which begin by
    matching TEXT.  If the answer is no symbols, then the return value
    is NULL.  */
 
-VEC (char_ptr) *
-make_symbol_completion_list (const char *text, const char *word)
+void
+collect_symbol_completion_matches (completion_tracker &tracker,
+				   const char *text, const char *word)
 {
-  return current_language->la_make_symbol_completion_list (text, word,
-							   TYPE_CODE_UNDEF);
+  current_language->la_collect_symbol_completion_matches (tracker,
+							  text, word,
+							  TYPE_CODE_UNDEF);
 }
 
-/* Like make_symbol_completion_list, but only return STRUCT_DOMAIN
-   symbols whose type code is CODE.  */
+/* Like collect_symbol_completion_matches, but only return
+   STRUCT_DOMAIN symbols whose type code is CODE.  */
 
-VEC (char_ptr) *
-make_symbol_completion_type (const char *text, const char *word,
-			     enum type_code code)
+void
+collect_symbol_completion_matches_type (completion_tracker &tracker,
+					const char *text, const char *word,
+					enum type_code code)
 {
   gdb_assert (code == TYPE_CODE_UNION
 	      || code == TYPE_CODE_STRUCT
 	      || code == TYPE_CODE_ENUM);
-  return current_language->la_make_symbol_completion_list (text, word, code);
+  current_language->la_collect_symbol_completion_matches (tracker,
+							  text, word, code);
 }
 
-/* Like make_symbol_completion_list, but returns a list of symbols
-   defined in a source file FILE.  */
+/* Like collect_symbol_completion_matches, but collects a list of
+   symbols defined in a source file FILE.  */
 
-static VEC (char_ptr) *
-make_file_symbol_completion_list_1 (const char *text, const char *word,
-				    const char *srcfile)
+void
+collect_file_symbol_completion_matches (completion_tracker &tracker,
+					const char *text, const char *word,
+					const char *srcfile)
 {
   /* The symbol we are completing on.  Points in same buffer as text.  */
   const char *sym_text;
@@ -5356,7 +5303,7 @@ make_file_symbol_completion_list_1 (const char *text, const char *word,
       /* A double-quoted string is never a symbol, nor does it make sense
          to complete it any other way.  */
       {
-	return NULL;
+	return;
       }
     else
       {
@@ -5372,42 +5319,11 @@ make_file_symbol_completion_list_1 (const char *text, const char *word,
   iterate_over_symtabs (srcfile, [&] (symtab *s)
     {
       add_symtab_completions (SYMTAB_COMPUNIT (s),
+			      tracker,
 			      sym_text, sym_text_len,
 			      text, word, TYPE_CODE_UNDEF);
       return false;
     });
-
-  return (return_val);
-}
-
-/* Wrapper around make_file_symbol_completion_list_1
-   to handle MAX_COMPLETIONS_REACHED_ERROR.  */
-
-VEC (char_ptr) *
-make_file_symbol_completion_list (const char *text, const char *word,
-				  const char *srcfile)
-{
-  struct cleanup *back_to, *cleanups;
-
-  completion_tracker = new_completion_tracker ();
-  cleanups = make_cleanup_free_completion_tracker (&completion_tracker);
-  return_val = NULL;
-  back_to = make_cleanup (do_free_completion_list, &return_val);
-
-  TRY
-    {
-      make_file_symbol_completion_list_1 (text, word, srcfile);
-    }
-  CATCH (except, RETURN_MASK_ERROR)
-    {
-      if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
-	throw_exception (except);
-    }
-  END_CATCH
-
-  discard_cleanups (back_to);
-  do_cleanups (cleanups);
-  return return_val;
 }
 
 /* A helper function for make_source_files_completion_list.  It adds
@@ -5416,7 +5332,7 @@ make_file_symbol_completion_list (const char *text, const char *word,
 
 static void
 add_filename_to_list (const char *fname, const char *text, const char *word,
-		      VEC (char_ptr) **list)
+		      completion_list *list)
 {
   char *newobj;
   size_t fnlen = strlen (fname);
@@ -5441,7 +5357,7 @@ add_filename_to_list (const char *fname, const char *text, const char *word,
       newobj[text - word] = '\0';
       strcat (newobj, fname);
     }
-  VEC_safe_push (char_ptr, *list, newobj);
+  list->emplace_back (newobj);
 }
 
 static int
@@ -5469,7 +5385,7 @@ struct add_partial_filename_data
   const char *text;
   const char *word;
   int text_len;
-  VEC (char_ptr) **list;
+  completion_list *list;
 };
 
 /* A callback for map_partial_symbol_filenames.  */
@@ -5503,17 +5419,16 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
 
 /* Return a vector of all source files whose names begin with matching
    TEXT.  The file names are looked up in the symbol tables of this
-   program.  If the answer is no matchess, then the return value is
-   NULL.  */
+   program.  */
 
-VEC (char_ptr) *
+completion_list
 make_source_files_completion_list (const char *text, const char *word)
 {
   struct compunit_symtab *cu;
   struct symtab *s;
   struct objfile *objfile;
   size_t text_len = strlen (text);
-  VEC (char_ptr) *list = NULL;
+  completion_list list;
   const char *base_name;
   struct add_partial_filename_data datum;
   struct cleanup *back_to;
@@ -5521,8 +5436,6 @@ make_source_files_completion_list (const char *text, const char *word)
   if (!have_full_symbols () && !have_partial_symbols ())
     return list;
 
-  back_to = make_cleanup (do_free_completion_list, &list);
-
   filename_seen_cache filenames_seen;
 
   ALL_FILETABS (objfile, cu, s)
@@ -5558,8 +5471,6 @@ make_source_files_completion_list (const char *text, const char *word)
   map_symbol_filenames (maybe_add_partial_symtab_filename, &datum,
 			0 /*need_fullname*/);
 
-  discard_cleanups (back_to);
-
   return list;
 }
 \f
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 5901cf9..fb08479 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -25,6 +25,7 @@
 #include "gdbtypes.h"
 #include "common/enum-flags.h"
 #include "common/function-view.h"
+#include "completer.h"
 
 /* Opaque declarations.  */
 struct ui_file;
@@ -1498,22 +1499,28 @@ extern void forget_cached_source_info (void);
 
 extern void select_source_symtab (struct symtab *);
 
-extern VEC (char_ptr) *default_make_symbol_completion_list_break_on
-  (const char *text, const char *word, const char *break_on,
+extern void default_collect_symbol_completion_matches_break_on
+  (completion_tracker &tracker,
+   const char *text, const char *word, const char *break_on,
    enum type_code code);
-extern VEC (char_ptr) *default_make_symbol_completion_list (const char *,
-							    const char *,
-							    enum type_code);
-extern VEC (char_ptr) *make_symbol_completion_list (const char *, const char *);
-extern VEC (char_ptr) *make_symbol_completion_type (const char *, const char *,
+extern void default_collect_symbol_completion_matches
+  (completion_tracker &tracker,
+   const char *,
+   const char *,
+   enum type_code);
+extern void collect_symbol_completion_matches (completion_tracker &tracker,
+					       const char *, const char *);
+extern void collect_symbol_completion_matches_type (completion_tracker &tracker,
+						    const char *, const char *,
 						    enum type_code);
 
-extern VEC (char_ptr) *make_file_symbol_completion_list (const char *,
-							 const char *,
-							 const char *);
+extern void collect_file_symbol_completion_matches (completion_tracker &tracker,
+						    const char *,
+						    const char *,
+						    const char *);
 
-extern VEC (char_ptr) *make_source_files_completion_list (const char *,
-							  const char *);
+extern completion_list
+  make_source_files_completion_list (const char *, const char *);
 
 /* symtab.c */
 
diff --git a/gdb/top.c b/gdb/top.c
index 3de8a26..7f41b6c 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -2022,7 +2022,7 @@ init_main (void)
 
   /* Setup important stuff for command line editing.  */
   rl_completion_word_break_hook = gdb_completion_word_break_characters;
-  rl_completion_entry_function = readline_line_completion_function;
+  rl_attempted_completion_function = gdb_rl_attempted_completion_function;
   set_rl_completer_word_break_characters (default_word_break_characters ());
   rl_completer_quote_characters = get_gdb_completer_quote_characters ();
   rl_completion_display_matches_hook = cli_display_match_list;
diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c
index e52f789..7ecfaec 100644
--- a/gdb/tui/tui-layout.c
+++ b/gdb/tui/tui-layout.c
@@ -353,14 +353,15 @@ tui_default_win_viewport_height (enum tui_win_type type,
 /* Complete possible layout names.  TEXT is the complete text entered so
    far, WORD is the word currently being completed.  */
 
-static VEC (char_ptr) *
+static void
 layout_completer (struct cmd_list_element *ignore,
+		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
   static const char *layout_names [] =
     { "src", "asm", "split", "regs", "next", "prev", NULL };
 
-  return complete_on_enum (layout_names, text, word);
+  complete_on_enum (tracker, layout_names, text, word);
 }
 
 /* Function to initialize gdb commands, for tui window layout
diff --git a/gdb/tui/tui-regs.c b/gdb/tui/tui-regs.c
index 3f9a007..c418203 100644
--- a/gdb/tui/tui-regs.c
+++ b/gdb/tui/tui-regs.c
@@ -668,24 +668,23 @@ tui_reg_command (char *args, int from_tty)
 /* Complete names of register groups, and add the special "prev" and "next"
    names.  */
 
-static VEC (char_ptr) *
+static void
 tui_reggroup_completer (struct cmd_list_element *ignore,
+			completion_tracker &tracker,
 			const char *text, const char *word)
 {
-  VEC (char_ptr) *result = NULL;
   static const char *extra[] = { "next", "prev", NULL };
   size_t len = strlen (word);
   const char **tmp;
 
-  result = reggroup_completer (ignore, text, word);
+  reggroup_completer (ignore, tracker, text, word);
 
+  /* XXXX use complete_on_enum instead?  */
   for (tmp = extra; *tmp != NULL; ++tmp)
     {
       if (strncmp (word, *tmp, len) == 0)
-	VEC_safe_push (char_ptr, result, xstrdup (*tmp));
+	tracker.add_completion (gdb::unique_xmalloc_ptr<char> (xstrdup (*tmp)));
     }
-
-  return result;
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
diff --git a/gdb/tui/tui-win.c b/gdb/tui/tui-win.c
index f49d7d5..e0df667 100644
--- a/gdb/tui/tui-win.c
+++ b/gdb/tui/tui-win.c
@@ -359,12 +359,12 @@ tui_set_var_cmd (char *null_args, int from_tty, struct cmd_list_element *c)
    window names 'next' and 'prev' will also be considered as possible
    completions of the window name.  */
 
-static VEC (char_ptr) *
-window_name_completer (int include_next_prev_p,
+static void
+window_name_completer (completion_tracker &tracker,
+		       int include_next_prev_p,
 		       const char *text, const char *word)
 {
   VEC (const_char_ptr) *completion_name_vec = NULL;
-  VEC (char_ptr) *matches_vec;
   int win_type;
 
   for (win_type = SRC_WIN; win_type < MAX_MAJOR_WINDOWS; win_type++)
@@ -398,39 +398,39 @@ window_name_completer (int include_next_prev_p,
     }
 
   VEC_safe_push (const_char_ptr, completion_name_vec, NULL);
-  matches_vec
-    = complete_on_enum (VEC_address (const_char_ptr, completion_name_vec),
-			text, word);
+  complete_on_enum (tracker,
+		    VEC_address (const_char_ptr, completion_name_vec),
+		    text, word);
 
   VEC_free (const_char_ptr, completion_name_vec);
-
-  return matches_vec;
 }
 
 /* Complete possible window names to focus on.  TEXT is the complete text
    entered so far, WORD is the word currently being completed.  */
 
-static VEC (char_ptr) *
+static void
 focus_completer (struct cmd_list_element *ignore,
-		  const char *text, const char *word)
+		 completion_tracker &tracker,
+		 const char *text, const char *word)
 {
-  return window_name_completer (1, text, word);
+  window_name_completer (tracker, 1, text, word);
 }
 
 /* Complete possible window names for winheight command.  TEXT is the
    complete text entered so far, WORD is the word currently being
    completed.  */
 
-static VEC (char_ptr) *
+static void
 winheight_completer (struct cmd_list_element *ignore,
+		     completion_tracker &tracker,
 		     const char *text, const char *word)
 {
   /* The first word is the window name.  That we can complete.  Subsequent
      words can't be completed.  */
   if (word != text)
-    return NULL;
+    return;
 
-  return window_name_completer (0, text, word);
+  window_name_completer (tracker, 0, text, word);
 }
 
 /* Function to initialize gdb commands, for tui window
diff --git a/gdb/value.c b/gdb/value.c
index be01f0f..ec615b3 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -41,6 +41,7 @@
 #include "cp-abi.h"
 #include "user-regs.h"
 #include <algorithm>
+#include "completer.h"
 
 /* Prototypes for exported functions.  */
 
@@ -2144,14 +2145,12 @@ lookup_only_internalvar (const char *name)
   return NULL;
 }
 
-/* Complete NAME by comparing it to the names of internal variables.
-   Returns a vector of newly allocated strings, or NULL if no matches
-   were found.  */
+/* Complete NAME by comparing it to the names of internal
+   variables.  */
 
-VEC (char_ptr) *
-complete_internalvar (const char *name)
+void
+complete_internalvar (completion_tracker &tracker, const char *name)
 {
-  VEC (char_ptr) *result = NULL;
   struct internalvar *var;
   int len;
 
@@ -2160,12 +2159,10 @@ complete_internalvar (const char *name)
   for (var = internalvars; var; var = var->next)
     if (strncmp (var->name, name, len) == 0)
       {
-	char *r = xstrdup (var->name);
+	gdb::unique_xmalloc_ptr<char> copy (xstrdup (var->name));
 
-	VEC_safe_push (char_ptr, result, r);
+	tracker.add_completion (std::move (copy));
       }
-
-  return result;
 }
 
 /* Create an internal variable with name NAME and with a void value.
diff --git a/gdb/value.h b/gdb/value.h
index a1d1609..162b756 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -928,7 +928,8 @@ extern struct internalvar *lookup_only_internalvar (const char *name);
 
 extern struct internalvar *create_internalvar (const char *name);
 
-extern VEC (char_ptr) *complete_internalvar (const char *name);
+extern void complete_internalvar (completion_tracker &tracker,
+				  const char *name);
 
 /* An internalvar can be dynamically computed by supplying a vector of
    function pointers to perform various operations.  */
-- 
2.5.5

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

* [PATCH 24/40] Per-language symbol name hashing algorithm
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (21 preceding siblings ...)
  2017-06-02 12:23 ` [PATCH 38/40] Use TOLOWER in SYMBOL_HASH_NEXT Pedro Alves
@ 2017-06-02 12:28 ` Pedro Alves
  2017-07-18 17:33   ` Keith Seitz
  2017-06-02 12:29 ` [PATCH 05/40] command.h: Include scoped_restore_command.h Pedro Alves
                   ` (17 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:28 UTC (permalink / raw)
  To: gdb-patches

Currently, we have a mess of symbol name hashing/comparison routines.
There's msymbol_hash for mangled names, and dict_hash and
msymbol_hash_iw for demangled names.  Then there's strcmp_iw,
strcmp_iw_ordered and Ada's full_match/wild_match, which all have to
agree with the hashing routines.  That's why dict_hash is really about
Ada names.  From the inconsistency department, minimal symbol hashing
doesn't go via dict_hash, so Ada's wild matching can't ever work with
minimal symbols.

This patch starts fixing this, by doing two things:

#1 - adds a language vector method to let each language decide how to
     compute a symbol name hash.

#2 - makes dictionaries know the language of the symbols they hold,
     and then use the dictionaries language to decide which hashing
     method to use.

For now, this is just scaffolding, since all languages install the
default method.  The series will make C++ install its own hashing
method later on, and will add per-language symbol name comparison
routines too.

This patch is based on a patch that Keith wrote for the libcc1/C++ WIP
support.

gdb/ChangeLog:
yyyy-mm-dd  Keith Seitz  <keiths@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_language_defn): Install
	default_search_name_hash.
	* buildsym.c (struct buildsym_compunit): <language>: New field.
	(finish_block_internal): Pass language when creating dictionaries.
	(start_buildsym_compunit, start_symtab): New language parameters.
	Use them.
	(restart_symtab): Pass down compilation unit's language.
	* buildsym.h (enum language): Forward declare.
	(start_symtab): New 'language' parameter.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Install
	default_search_name_hash.
	* coffread.c (coff_start_symtab): Adjust.
	* d-lang.c (d_language_defn): Install default_search_name_hash.
	* dbxread.c (struct symloc): Add 'pst_language' field.
	(PST_LANGUAGE): Define.
	(start_psymtab, read_ofile_symtab): Use it.
	(process_one_symbol): New 'language' parameter.  Pass it down.
	* dictionary.c (struct dictionary) <language>: New field.
	(DICT_LANGUAGE): Define.
	(dict_create_hashed, dict_create_hashed_expandable)
	(dict_create_linear, dict_create_linear_expandable): New parameter
	'language'.  Set the dictionary's language.
	(iter_match_first_hashed): Adjust to rename.
	(insert_symbol_hashed): Assert we don't see mismatching
	languages.  Adjust to rename.
	(dict_hash): Rename to ...
	(default_search_name_hash): ... this and make extern.
	* dictionary.h (struct language_defn): Forward declare.
	(dict_create_hashed): New parameter 'language'.
	* dwarf2read.c (dwarf2_start_symtab): Pass down language.
	* f-lang.c (f_language_defn): Install default_search_name_hash.
	* go-lang.c (go_language_defn): Install default_search_name_hash.
	* jit.c (finalize_symtab): Pass compunit's language to dictionary
	creation.
	* language.c (unknown_language_defn, auto_language_defn):
	* language.h (language_defn::la_search_name_hash): New field.
	(default_search_name_hash): Declare.
	* m2-lang.c (m2_language_defn): Install default_search_name_hash.
	* mdebugread.c (new_block): New parameter 'language'.
	* mdebugread.c (parse_symbol): Pass symbol language to block
	allocation.
	(psymtab_to_symtab_1): Pass down language.
	(new_symtab): Pass compunit's language to block allocation.
	* objc-lang.c (objc_language_defn): Install
	default_search_name_hash.
	* opencl-lang.c (opencl_language_defn):
	* p-lang.c (pascal_language_defn): Install
	default_search_name_hash.
	* rust-lang.c (rust_language_defn): Install
	default_search_name_hash.
	* stabsread.h (enum language): Forward declare.
	(process_one_symbol): Add 'language' parameter.
	* symtab.c (search_name_hash): New function.
	* symtab.h (search_name_hash): Declare.
	* xcoffread.c (read_xcoff_symtab): Pass language to start_symtab.
---
 gdb/ada-lang.c    |  1 +
 gdb/buildsym.c    | 25 +++++++++++++++++--------
 gdb/buildsym.h    |  4 +++-
 gdb/c-lang.c      |  4 ++++
 gdb/coffread.c    |  4 +++-
 gdb/d-lang.c      |  1 +
 gdb/dbxread.c     | 12 ++++++++----
 gdb/dictionary.c  | 36 +++++++++++++++++++++++-------------
 gdb/dictionary.h  | 11 +++++++----
 gdb/dwarf2read.c  |  2 +-
 gdb/f-lang.c      |  1 +
 gdb/go-lang.c     |  1 +
 gdb/jit.c         |  6 ++++--
 gdb/language.c    |  2 ++
 gdb/language.h    | 12 ++++++++++++
 gdb/m2-lang.c     |  1 +
 gdb/mdebugread.c  | 23 +++++++++++++----------
 gdb/objc-lang.c   |  1 +
 gdb/opencl-lang.c |  1 +
 gdb/p-lang.c      |  1 +
 gdb/rust-lang.c   |  1 +
 gdb/stabsread.h   |  3 ++-
 gdb/symtab.c      |  8 ++++++++
 gdb/symtab.h      |  3 +++
 gdb/xcoffread.c   | 11 +++++++----
 25 files changed, 126 insertions(+), 49 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index f582c56..1d4cf36 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14034,6 +14034,7 @@ extern const struct language_defn ada_language_defn = {
   c_watch_location_expression,
   ada_get_symbol_name_cmp,	/* la_get_symbol_name_cmp */
   ada_iterate_over_symbols,
+  default_search_name_hash,
   &ada_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index cbad027..15f65a8 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -128,6 +128,9 @@ struct buildsym_compunit
 
   /* The compunit we are building.  */
   struct compunit_symtab *compunit_symtab;
+
+  /* Language of this compunit_symtab.  */
+  enum language language;
 };
 
 /* The work-in-progress of the compunit we are building.
@@ -351,20 +354,23 @@ finish_block_internal (struct symbol *symbol,
 
   if (symbol)
     {
-      BLOCK_DICT (block) = dict_create_linear (&objfile->objfile_obstack,
-					       *listhead);
+      BLOCK_DICT (block)
+	= dict_create_linear (&objfile->objfile_obstack,
+			      buildsym_compunit->language, *listhead);
     }
   else
     {
       if (expandable)
 	{
-	  BLOCK_DICT (block) = dict_create_hashed_expandable ();
+	  BLOCK_DICT (block)
+	    = dict_create_hashed_expandable (buildsym_compunit->language);
 	  dict_add_pending (BLOCK_DICT (block), *listhead);
 	}
       else
 	{
 	  BLOCK_DICT (block) =
-	    dict_create_hashed (&objfile->objfile_obstack, *listhead);
+	    dict_create_hashed (&objfile->objfile_obstack,
+				buildsym_compunit->language, *listhead);
 	}
     }
 
@@ -768,7 +774,8 @@ start_subfile (const char *name)
    (or NULL if not known).  */
 
 static struct buildsym_compunit *
-start_buildsym_compunit (struct objfile *objfile, const char *comp_dir)
+start_buildsym_compunit (struct objfile *objfile, const char *comp_dir,
+			 enum language language)
 {
   struct buildsym_compunit *bscu;
 
@@ -777,6 +784,7 @@ start_buildsym_compunit (struct objfile *objfile, const char *comp_dir)
 
   bscu->objfile = objfile;
   bscu->comp_dir = (comp_dir == NULL) ? NULL : xstrdup (comp_dir);
+  bscu->language = language;
 
   /* Initialize the debug format string to NULL.  We may supply it
      later via a call to record_debugformat.  */
@@ -1047,11 +1055,11 @@ prepare_for_building (const char *name, CORE_ADDR start_addr)
 
 struct compunit_symtab *
 start_symtab (struct objfile *objfile, const char *name, const char *comp_dir,
-	      CORE_ADDR start_addr)
+	      CORE_ADDR start_addr, enum language language)
 {
   prepare_for_building (name, start_addr);
 
-  buildsym_compunit = start_buildsym_compunit (objfile, comp_dir);
+  buildsym_compunit = start_buildsym_compunit (objfile, comp_dir, language);
 
   /* Allocate the compunit symtab now.  The caller needs it to allocate
      non-primary symtabs.  It is also needed by get_macro_table.  */
@@ -1088,7 +1096,8 @@ restart_symtab (struct compunit_symtab *cust,
   prepare_for_building (name, start_addr);
 
   buildsym_compunit = start_buildsym_compunit (COMPUNIT_OBJFILE (cust),
-					       COMPUNIT_DIRNAME (cust));
+					       COMPUNIT_DIRNAME (cust),
+					       compunit_language (cust));
   buildsym_compunit->compunit_symtab = cust;
 }
 
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index 60109a0..80ca7da 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -23,6 +23,7 @@ struct objfile;
 struct symbol;
 struct addrmap;
 struct compunit_symtab;
+enum language;
 
 /* This module provides definitions used for creating and adding to
    the symbol table.  These routines are called from various symbol-
@@ -254,7 +255,8 @@ extern record_line_ftype record_line;
 extern struct compunit_symtab *start_symtab (struct objfile *objfile,
 					     const char *name,
 					     const char *comp_dir,
-					     CORE_ADDR start_addr);
+					     CORE_ADDR start_addr,
+					     enum language language);
 
 extern void restart_symtab (struct compunit_symtab *cust,
 			    const char *name, CORE_ADDR start_addr);
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index f86e26e..9749935 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -871,6 +871,7 @@ extern const struct language_defn c_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &c_varobj_ops,
   c_get_compile_context,
   c_compute_program,
@@ -1015,6 +1016,7 @@ extern const struct language_defn cplus_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &cplus_varobj_ops,
   NULL,
   NULL,
@@ -1068,6 +1070,7 @@ extern const struct language_defn asm_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
@@ -1121,6 +1124,7 @@ extern const struct language_defn minimal_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/coffread.c b/gdb/coffread.c
index 9db4792..db0b77a 100644
--- a/gdb/coffread.c
+++ b/gdb/coffread.c
@@ -394,7 +394,9 @@ coff_start_symtab (struct objfile *objfile, const char *name)
 		 NULL,
   /* The start address is irrelevant, since we set
      last_source_start_addr in coff_end_symtab.  */
-		 0);
+		 0,
+  /* Let buildsym.c deduce the language for this symtab.  */
+		 language_unknown);
   record_debugformat ("COFF");
 }
 
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 941d3ed..b89b636 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -247,6 +247,7 @@ extern const struct language_defn d_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/dbxread.c b/gdb/dbxread.c
index daa3ce9..ce0eeb0 100644
--- a/gdb/dbxread.c
+++ b/gdb/dbxread.c
@@ -54,7 +54,6 @@
 #include "cp-support.h"
 #include "psympriv.h"
 #include "block.h"
-
 #include "aout/aout64.h"
 #include "aout/stab_gnu.h"	/* We always use GNU stabs, not
 				   native, now.  */
@@ -92,6 +91,7 @@ struct symloc
     int symbol_offset;
     int string_offset;
     int file_string_offset;
+    enum language pst_language;
   };
 
 #define LDSYMOFF(p) (((struct symloc *)((p)->read_symtab_private))->ldsymoff)
@@ -101,6 +101,7 @@ struct symloc
 #define SYMBOL_OFFSET(p) (SYMLOC(p)->symbol_offset)
 #define STRING_OFFSET(p) (SYMLOC(p)->string_offset)
 #define FILE_STRING_OFFSET(p) (SYMLOC(p)->file_string_offset)
+#define PST_LANGUAGE(p) (SYMLOC(p)->pst_language)
 \f
 
 /* The objfile we are currently reading.  */
@@ -1422,6 +1423,7 @@ read_dbx_symtab (minimal_symbol_reader &reader, struct objfile *objfile)
 		    || psymtab_language != language_cplus))
 	      psymtab_language = tmp_language;
 
+
 	    /* In C++, one may expect the same filename to come round many
 	       times, when code is coming alternately from the main file
 	       and from inline functions in other files.  So I check to see
@@ -2017,6 +2019,7 @@ start_psymtab (struct objfile *objfile, const char *filename, CORE_ADDR textlow,
 
   /* Deduce the source language from the filename for this psymtab.  */
   psymtab_language = deduce_language_from_filename (filename);
+  PST_LANGUAGE (result) = psymtab_language;
 
   return result;
 }
@@ -2403,7 +2406,8 @@ read_ofile_symtab (struct objfile *objfile, struct partial_symtab *pst)
 		 positive offsets.  */
 	    nlist.n_value = (nlist.n_value ^ 0x80000000) - 0x80000000;
 	  process_one_symbol (type, nlist.n_desc, nlist.n_value,
-			      namestring, section_offsets, objfile);
+			      namestring, section_offsets, objfile,
+			      PST_LANGUAGE (pst));
 	}
       /* We skip checking for a new .o or -l file; that should never
          happen in this routine.  */
@@ -2503,7 +2507,7 @@ cp_set_block_scope (const struct symbol *symbol,
 void
 process_one_symbol (int type, int desc, CORE_ADDR valu, const char *name,
 		    const struct section_offsets *section_offsets,
-		    struct objfile *objfile)
+		    struct objfile *objfile, enum language language)
 {
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
   struct context_stack *newobj;
@@ -2717,7 +2721,7 @@ process_one_symbol (int type, int desc, CORE_ADDR valu, const char *name,
       function_start_offset = 0;
 
       start_stabs ();
-      start_symtab (objfile, name, NULL, valu);
+      start_symtab (objfile, name, NULL, valu, language);
       record_debugformat ("stabs");
       break;
 
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index b2cfca2..1be7e48 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -165,6 +165,7 @@ struct dictionary_linear_expandable
 
 struct dictionary
 {
+  const struct language_defn *language;
   const struct dict_vector *vector;
   union
   {
@@ -179,6 +180,7 @@ struct dictionary
 /* Accessor macros.  */
 
 #define DICT_VECTOR(d)			(d)->vector
+#define DICT_LANGUAGE(d)                (d)->language
 
 /* These can be used for DICT_HASHED_EXPANDABLE, too.  */
 
@@ -245,8 +247,6 @@ static struct symbol *iter_match_next_hashed (const char *name,
 					      symbol_compare_ftype *compare,
 					      struct dict_iterator *iterator);
 
-static unsigned int dict_hash (const char *string);
-
 /* Functions only for DICT_HASHED.  */
 
 static int size_hashed (const struct dictionary *dict);
@@ -354,6 +354,7 @@ static void expand_hashtable (struct dictionary *dict);
 
 struct dictionary *
 dict_create_hashed (struct obstack *obstack,
+		    enum language language,
 		    const struct pending *symbol_list)
 {
   struct dictionary *retval;
@@ -363,6 +364,7 @@ dict_create_hashed (struct obstack *obstack,
 
   retval = XOBNEW (obstack, struct dictionary);
   DICT_VECTOR (retval) = &dict_hashed_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
 
   /* Calculate the number of symbols, and allocate space for them.  */
   for (list_counter = symbol_list;
@@ -397,11 +399,12 @@ dict_create_hashed (struct obstack *obstack,
    it.  */
 
 extern struct dictionary *
-dict_create_hashed_expandable (void)
+dict_create_hashed_expandable (enum language language)
 {
   struct dictionary *retval = XNEW (struct dictionary);
 
   DICT_VECTOR (retval) = &dict_hashed_expandable_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
   DICT_HASHED_NBUCKETS (retval) = DICT_EXPANDABLE_INITIAL_CAPACITY;
   DICT_HASHED_BUCKETS (retval) = XCNEWVEC (struct symbol *,
 					   DICT_EXPANDABLE_INITIAL_CAPACITY);
@@ -417,6 +420,7 @@ dict_create_hashed_expandable (void)
 
 struct dictionary *
 dict_create_linear (struct obstack *obstack,
+		    enum language language,
 		    const struct pending *symbol_list)
 {
   struct dictionary *retval;
@@ -426,6 +430,7 @@ dict_create_linear (struct obstack *obstack,
 
   retval = XOBNEW (obstack, struct dictionary);
   DICT_VECTOR (retval) = &dict_linear_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
 
   /* Calculate the number of symbols, and allocate space for them.  */
   for (list_counter = symbol_list;
@@ -461,11 +466,12 @@ dict_create_linear (struct obstack *obstack,
    it.  */
 
 struct dictionary *
-dict_create_linear_expandable (void)
+dict_create_linear_expandable (enum language language)
 {
   struct dictionary *retval = XNEW (struct dictionary);
 
   DICT_VECTOR (retval) = &dict_linear_expandable_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
   DICT_LINEAR_NSYMS (retval) = 0;
   DICT_LINEAR_EXPANDABLE_CAPACITY (retval) = DICT_EXPANDABLE_INITIAL_CAPACITY;
   DICT_LINEAR_SYMS (retval)
@@ -638,7 +644,9 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
 			 symbol_compare_ftype *compare,
 			 struct dict_iterator *iterator)
 {
-  unsigned int hash_index = dict_hash (name) % DICT_HASHED_NBUCKETS (dict);
+  unsigned int hash_index
+    = (search_name_hash (DICT_LANGUAGE (dict)->la_language, name)
+       % DICT_HASHED_NBUCKETS (dict));
   struct symbol *sym;
 
   DICT_ITERATOR_DICT (iterator) = dict;
@@ -689,10 +697,15 @@ insert_symbol_hashed (struct dictionary *dict,
 		      struct symbol *sym)
 {
   unsigned int hash_index;
+  unsigned int hash;
   struct symbol **buckets = DICT_HASHED_BUCKETS (dict);
 
-  hash_index = 
-    dict_hash (SYMBOL_SEARCH_NAME (sym)) % DICT_HASHED_NBUCKETS (dict);
+  /* We don't want to insert a symbol into a dictionary of a different
+     language.  The two may not use the same hashing algorithm.  */
+  gdb_assert (SYMBOL_LANGUAGE (sym) == DICT_LANGUAGE (dict)->la_language);
+
+  hash = search_name_hash (SYMBOL_LANGUAGE (sym), SYMBOL_SEARCH_NAME (sym));
+  hash_index = hash % DICT_HASHED_NBUCKETS (dict);
   sym->hash_next = buckets[hash_index];
   buckets[hash_index] = sym;
 }
@@ -765,13 +778,10 @@ expand_hashtable (struct dictionary *dict)
   xfree (old_buckets);
 }
 
-/* Produce an unsigned hash value from STRING0 that is consistent
-   with strcmp_iw, strcmp, and, at least on Ada symbols, wild_match.
-   That is, two identifiers equivalent according to any of those three
-   comparison operators hash to the same value.  */
+/* See dictionary.h.  */
 
-static unsigned int
-dict_hash (const char *string0)
+unsigned int
+default_search_name_hash (const char *string0)
 {
   /* The Ada-encoded version of a name P1.P2...Pn has either the form
      P1__P2__...Pn<suffix> or _ada_P1__P2__...Pn<suffix> (where the Pi
diff --git a/gdb/dictionary.h b/gdb/dictionary.h
index 4f4f160..ef5fbed 100644
--- a/gdb/dictionary.h
+++ b/gdb/dictionary.h
@@ -35,7 +35,7 @@ struct dictionary;
 struct symbol;
 struct obstack;
 struct pending;
-
+struct language_defn;
 
 /* The creation functions for various implementations of
    dictionaries.  */
@@ -45,6 +45,7 @@ struct pending;
    initialized from SYMBOL_LIST.  */
 
 extern struct dictionary *dict_create_hashed (struct obstack *obstack,
+					      enum language language,
 					      const struct pending
 					      *symbol_list);
 
@@ -53,7 +54,8 @@ extern struct dictionary *dict_create_hashed (struct obstack *obstack,
    it, call dict_add_symbol().  Call dict_free() when you're done with
    it.  */
 
-extern struct dictionary *dict_create_hashed_expandable (void);
+extern struct dictionary *
+  dict_create_hashed_expandable (enum language language);
 
 /* Create a dictionary implemented via a fixed-size array.  All memory
    it uses is allocated on OBSTACK; the environment is initialized
@@ -61,6 +63,7 @@ extern struct dictionary *dict_create_hashed_expandable (void);
    that they're found in SYMBOL_LIST.  */
 
 extern struct dictionary *dict_create_linear (struct obstack *obstack,
+					      enum language language,
 					      const struct pending
 					      *symbol_list);
 
@@ -69,8 +72,8 @@ extern struct dictionary *dict_create_linear (struct obstack *obstack,
    it, call dict_add_symbol().  Call dict_free() when you're done with
    it.  */
 
-extern struct dictionary *dict_create_linear_expandable (void);
-
+extern struct dictionary *
+  dict_create_linear_expandable (enum language language);
 
 /* The functions providing the interface to dictionaries.  Note that
    the most common parts of the interface, namely symbol lookup, are
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 7fade00..c44a76e 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -18810,7 +18810,7 @@ dwarf2_start_symtab (struct dwarf2_cu *cu,
 		     const char *name, const char *comp_dir, CORE_ADDR low_pc)
 {
   struct compunit_symtab *cust
-    = start_symtab (cu->objfile, name, comp_dir, low_pc);
+    = start_symtab (cu->objfile, name, comp_dir, low_pc, cu->language);
 
   record_debugformat ("DWARF 2");
   record_producer (cu->producer);
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 903cfd1..cfef64f 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -293,6 +293,7 @@ extern const struct language_defn f_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 60bb3c5..befd937 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -608,6 +608,7 @@ extern const struct language_defn go_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/jit.c b/gdb/jit.c
index ddf1005..c30a06a 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -661,12 +661,14 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
   size_t blockvector_size;
   CORE_ADDR begin, end;
   struct blockvector *bv;
+  enum language language;
 
   actual_nblocks = FIRST_LOCAL_BLOCK + stab->nblocks;
 
   cust = allocate_compunit_symtab (objfile, stab->file_name);
   allocate_symtab (cust, stab->file_name);
   add_compunit_symtab_to_objfile (cust);
+  language = compunit_language (cust);
 
   /* JIT compilers compile in memory.  */
   COMPUNIT_DIRNAME (cust) = NULL;
@@ -711,7 +713,7 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
 					   "void");
 
       BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack,
-                                                   NULL);
+						   language, NULL);
       /* The address range.  */
       BLOCK_START (new_block) = (CORE_ADDR) gdb_block_iter->begin;
       BLOCK_END (new_block) = (CORE_ADDR) gdb_block_iter->end;
@@ -749,7 +751,7 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
 		   ? allocate_global_block (&objfile->objfile_obstack)
 		   : allocate_block (&objfile->objfile_obstack));
       BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack,
-                                                   NULL);
+						   language, NULL);
       BLOCK_SUPERBLOCK (new_block) = block_iter;
       block_iter = new_block;
 
diff --git a/gdb/language.c b/gdb/language.c
index df4f3cd..2e95c9e 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -849,6 +849,7 @@ const struct language_defn unknown_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
@@ -899,6 +900,7 @@ const struct language_defn auto_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/language.h b/gdb/language.h
index f4852c1..9b5062e 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -392,6 +392,10 @@ struct language_defn
       (const struct block *block, const char *name, domain_enum domain,
        gdb::function_view<symbol_found_callback_ftype> callback);
 
+    /* Hash the given STRING.  Use default_search_name_hash if no
+       special treatment is required.  */
+    unsigned int (*la_search_name_hash) (const char *name);
+
     /* Various operations on varobj.  */
     const struct lang_varobj_ops *la_varobj_ops;
 
@@ -611,6 +615,14 @@ void default_print_typedef (struct type *type, struct symbol *new_symbol,
 void default_get_string (struct value *value, gdb_byte **buffer, int *length,
 			 struct type **char_type, const char **charset);
 
+/* Default name hashing function.  */
+
+/* Produce an unsigned hash value from SEARCH_NAME that is consistent
+   with strcmp_iw, strcmp, and, at least on Ada symbols, wild_match.
+   That is, two identifiers equivalent according to any of those three
+   comparison operators hash to the same value.  */
+extern unsigned int default_search_name_hash (const char *search_name);
+
 void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 		   struct type **char_type, const char **charset);
 
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index b9ab2b3..66ac34c 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -396,6 +396,7 @@ extern const struct language_defn m2_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index 8e27194..5df0f48 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -236,7 +236,7 @@ static struct type *new_type (char *);
 
 enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
 
-static struct block *new_block (enum block_type);
+static struct block *new_block (enum block_type, enum language);
 
 static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
 
@@ -811,7 +811,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	TYPE_PROTOTYPED (SYMBOL_TYPE (s)) = 1;
 
       /* Create and enter a new lexical context.  */
-      b = new_block (FUNCTION_BLOCK);
+      b = new_block (FUNCTION_BLOCK, SYMBOL_LANGUAGE (s));
       SYMBOL_BLOCK_VALUE (s) = b;
       BLOCK_FUNCTION (b) = s;
       BLOCK_START (b) = BLOCK_END (b) = sh->value;
@@ -1144,7 +1144,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	}
 
       top_stack->blocktype = stBlock;
-      b = new_block (NON_FUNCTION_BLOCK);
+      b = new_block (NON_FUNCTION_BLOCK, psymtab_language);
       BLOCK_START (b) = sh->value + top_stack->procadr;
       BLOCK_SUPERBLOCK (b) = top_stack->cur_block;
       top_stack->cur_block = b;
@@ -4026,6 +4026,7 @@ psymtab_to_symtab_1 (struct objfile *objfile,
 	  if (ECOFF_IS_STAB (&sh) || (name[0] == '#'))
 	    {
 	      int type_code = ECOFF_UNMARK_STAB (sh.index);
+	      enum language language = PST_PRIVATE (pst)->pst_language;
 
 	      /* We should never get non N_STAB symbols here, but they
 	         should be harmless, so keep process_one_symbol from
@@ -4053,14 +4054,14 @@ psymtab_to_symtab_1 (struct objfile *objfile,
 		    {
 		      last_symtab_ended = 0;
 		      process_one_symbol (type_code, 0, valu, name,
-					  section_offsets, objfile);
+					  section_offsets, objfile, language);
 		    }
 		}
 	      /* Similarly a hack.  */
 	      else if (name[0] == '#')
 		{
 		  process_one_symbol (N_SLINE, 0, valu, name,
-				      section_offsets, objfile);
+				      section_offsets, objfile, language);
 		}
 	      if (type_code == N_FUN)
 		{
@@ -4721,16 +4722,18 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
   struct compunit_symtab *cust = allocate_compunit_symtab (objfile, name);
   struct symtab *symtab;
   struct blockvector *bv;
+  enum language lang;
 
   add_compunit_symtab_to_objfile (cust);
   symtab = allocate_symtab (cust, name);
 
   SYMTAB_LINETABLE (symtab) = new_linetable (maxlines);
+  lang = compunit_language (cust);
 
   /* All symtabs must have at least two blocks.  */
   bv = new_bvect (2);
-  BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK) = new_block (NON_FUNCTION_BLOCK);
-  BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK) = new_block (NON_FUNCTION_BLOCK);
+  BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK) = new_block (NON_FUNCTION_BLOCK, lang);
+  BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK) = new_block (NON_FUNCTION_BLOCK, lang);
   BLOCK_SUPERBLOCK (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) =
     BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
   COMPUNIT_BLOCKVECTOR (cust) = bv;
@@ -4818,7 +4821,7 @@ new_bvect (int nblocks)
    hashed.  */
 
 static struct block *
-new_block (enum block_type type)
+new_block (enum block_type type, enum language language)
 {
   /* FIXME: carlton/2003-09-11: This should use allocate_block to
      allocate the block.  Which, in turn, suggests that the block
@@ -4826,9 +4829,9 @@ new_block (enum block_type type)
   struct block *retval = XCNEW (struct block);
 
   if (type == FUNCTION_BLOCK)
-    BLOCK_DICT (retval) = dict_create_linear_expandable ();
+    BLOCK_DICT (retval) = dict_create_linear_expandable (language);
   else
-    BLOCK_DICT (retval) = dict_create_hashed_expandable ();
+    BLOCK_DICT (retval) = dict_create_hashed_expandable (language);
 
   return retval;
 }
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 6ffe85e..4dbc7f2 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -405,6 +405,7 @@ extern const struct language_defn objc_language_defn = {
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index 9b0f015..e5aff0d 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1085,6 +1085,7 @@ extern const struct language_defn opencl_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 439a377..2dca923 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -456,6 +456,7 @@ extern const struct language_defn pascal_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 817976a..f0d9968 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2190,6 +2190,7 @@ extern const struct language_defn rust_language_defn =
   rust_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/stabsread.h b/gdb/stabsread.h
index b37be1a..b803cf9 100644
--- a/gdb/stabsread.h
+++ b/gdb/stabsread.h
@@ -17,6 +17,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 struct objfile;
+enum language;
 
 /* Definitions, prototypes, etc for stabs debugging format support
    functions.
@@ -169,7 +170,7 @@ extern struct partial_symtab *dbx_end_psymtab
 
 extern void process_one_symbol (int, int, CORE_ADDR, const char *,
 				const struct section_offsets *,
-				struct objfile *);
+				struct objfile *, enum language);
 
 extern void elfstab_build_psymtabs (struct objfile *objfile,
 				    asection *stabsect,
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 7c7ff7f..a875c4d 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -1802,6 +1802,14 @@ demangle_for_lookup (const char *name, enum language lang,
   return name;
 }
 
+/* See symtab.h.  */
+
+unsigned int
+search_name_hash (enum language language, const char *search_name)
+{
+  return language_def (language)->la_search_name_hash (search_name);
+}
+
 /* See symtab.h.
 
    This function (or rather its subordinates) have a bunch of loops and
diff --git a/gdb/symtab.h b/gdb/symtab.h
index fffe0f87..20904e4 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -277,6 +277,9 @@ extern const char *symbol_search_name (const struct general_symbol_info *);
 #define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
   (strcmp_iw (SYMBOL_SEARCH_NAME (symbol), (name)) == 0)
 
+extern unsigned int search_name_hash (enum language language,
+				      const char *search_name);
+
 /* Classification types for a minimal symbol.  These should be taken as
    "advisory only", since if gdb can't easily figure out a
    classification it simply selects mst_unknown.  It may also have to
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index 138f941..0d6f290 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -1044,7 +1044,8 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
   last_csect_name = 0;
 
   start_stabs ();
-  start_symtab (objfile, filestring, (char *) NULL, file_start_addr);
+  start_symtab (objfile, filestring, (char *) NULL, file_start_addr,
+		language_unknown);
   record_debugformat (debugfmt);
   symnum = ((struct symloc *) pst->read_symtab_private)->first_symnum;
   max_symnum =
@@ -1137,7 +1138,8 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
 	    }
 
 	  start_stabs ();
-	  start_symtab (objfile, "_globals_", (char *) NULL, (CORE_ADDR) 0);
+	  start_symtab (objfile, "_globals_", (char *) NULL, (CORE_ADDR) 0,
+			language_unknown);
 	  record_debugformat (debugfmt);
 	  cur_src_end_addr = first_object_file_end;
 	  /* Done with all files, everything from here on is globals.  */
@@ -1227,7 +1229,7 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
 			  /* Give all csects for this source file the same
 			     name.  */
 			  start_symtab (objfile, filestring, NULL,
-					(CORE_ADDR) 0);
+					(CORE_ADDR) 0, language_unknown);
 			  record_debugformat (debugfmt);
 			}
 
@@ -1347,7 +1349,8 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
 	    filestring = cs->c_name;
 
 	  start_stabs ();
-	  start_symtab (objfile, filestring, (char *) NULL, (CORE_ADDR) 0);
+	  start_symtab (objfile, filestring, (char *) NULL, (CORE_ADDR) 0,
+			language_unknown);
 	  record_debugformat (debugfmt);
 	  last_csect_name = 0;
 
-- 
2.5.5

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

* [PATCH 21/40] Use SYMBOL_MATCHES_SEARCH_NAME some more
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (26 preceding siblings ...)
  2017-06-02 12:29 ` [PATCH 07/40] objfile_per_bfd_storage non-POD Pedro Alves
@ 2017-06-02 12:29 ` Pedro Alves
  2017-07-17 21:39   ` Keith Seitz
  2017-06-02 12:29 ` [PATCH 16/40] Explicit locations -label completer Pedro Alves
                   ` (12 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:29 UTC (permalink / raw)
  To: gdb-patches

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* dwarf2read.c (dw2_lookup_symbol): Use
	SYMBOL_MATCHES_SEARCH_NAME.
	* psymtab.c (psym_lookup_symbol): Use SYMBOL_MATCHES_SEARCH_NAME.
---
 gdb/dwarf2read.c | 4 ++--
 gdb/psymtab.c    | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 439257f..7fade00 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -3850,10 +3850,10 @@ dw2_lookup_symbol (struct objfile *objfile, int block_index,
 	     information (but NAME might contain it).  */
 
 	  if (sym != NULL
-	      && strcmp_iw (SYMBOL_SEARCH_NAME (sym), name) == 0)
+	      && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
 	    return stab;
 	  if (with_opaque != NULL
-	      && strcmp_iw (SYMBOL_SEARCH_NAME (with_opaque), name) == 0)
+	      && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
 	    stab_best = stab;
 
 	  /* Keep looking through other CUs.  */
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index bb482ee..4077fb3 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -529,10 +529,10 @@ psym_lookup_symbol (struct objfile *objfile,
 	   information (but NAME might contain it).  */
 
 	if (sym != NULL
-	    && strcmp_iw (SYMBOL_SEARCH_NAME (sym), name) == 0)
+	    && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
 	  return stab;
 	if (with_opaque != NULL
-	    && strcmp_iw (SYMBOL_SEARCH_NAME (with_opaque), name) == 0)
+	    && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
 	  stab_best = stab;
 
 	/* Keep looking through other psymtabs.  */
-- 
2.5.5

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

* [PATCH 17/40] Linespec lexing and C++ operators
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (23 preceding siblings ...)
  2017-06-02 12:29 ` [PATCH 05/40] command.h: Include scoped_restore_command.h Pedro Alves
@ 2017-06-02 12:29 ` Pedro Alves
  2017-07-14 21:45   ` Keith Seitz
  2017-06-02 12:29 ` [PATCH 12/40] "complete" command and completion word break characters Pedro Alves
                   ` (15 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:29 UTC (permalink / raw)
  To: gdb-patches

There's some lexing code in linespec that isn't handling C++ operators
correctly.  It's the usual confusion with operator< / operator<<, in
code that wants to skip past template parameters.

The linespec_lexer_lex_string change is necessary otherwise we get
this (with current master):

 (gdb) break 'operator<'
 unmatched quote

The need for the find_toplevel_char change was exposed by the use of
that function in the explicit location completer.  Without the fix,
that completer is not able to "see" past operator< symbols, without
quoting, like:

 (gdb) b -function operator<(int, int) -labe[TAB]    # nothing happens

gdb incorrectly thinks "-labe" is part of the "unclosed" template
parameter list started with "<".

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* linespec.c (linespec_lexer_lex_string, find_toplevel_char):
	Handle 'operator<' / 'operator<<'.
---
 gdb/linespec.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 82 insertions(+), 6 deletions(-)

diff --git a/gdb/linespec.c b/gdb/linespec.c
index 0216bf1..f24cca2 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -674,14 +674,49 @@ linespec_lexer_lex_string (linespec_parser *parser)
 	  else if (*PARSER_STREAM (parser) == '<'
 		   || *PARSER_STREAM (parser) == '(')
 	    {
-	      const char *p;
+	      /* Don't interpret 'operator<' / 'operator<<' as a
+		 template parameter list though.  */
+	      if (*PARSER_STREAM (parser) == '<'
+		  && (PARSER_STATE (parser)->language->la_language
+		      == language_cplus)
+		  && (PARSER_STREAM (parser) - start) >= CP_OPERATOR_LEN)
+		{
+		  const char *p = PARSER_STREAM (parser);
+
+		  while (p > start && isspace (p[-1]))
+		    p--;
+		  if (p - start >= CP_OPERATOR_LEN)
+		    {
+		      p-= CP_OPERATOR_LEN;
+		      if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+			  && (p == start
+			      || !(isalnum (p[-1]) || p[-1] == '_')))
+			{
+			  /* This is an operator name.  Keep going.  */
+			  ++(PARSER_STREAM (parser));
+			  if (*PARSER_STREAM (parser) == '<')
+			    ++(PARSER_STREAM (parser));
+			  continue;
+			}
+		    }
+		}
 
-	      p = find_parameter_list_end (PARSER_STREAM (parser));
-	      if (p != NULL)
+	      const char *p = find_parameter_list_end (PARSER_STREAM (parser));
+	      PARSER_STREAM (parser) = p;
+
+	      /* Don't loop around to the normal \0 case above because
+		 we don't want to misinterpret a potential keyword at
+		 the end of the token when the string isn't
+		 "()<>"-balanced.  This handles "b
+		 function(thread<tab>" in completion mode.  */
+	      if (*p == '\0')
 		{
-		  PARSER_STREAM (parser) = p;
-		  continue;
+		  LS_TOKEN_STOKEN (token).ptr = start;
+		  LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;
+		  return token;
 		}
+	      else
+		continue;
 	    }
 	  /* Commas are terminators, but not if they are part of an
 	     operator name.  */
@@ -1112,7 +1147,7 @@ find_methods (struct type *t, const char *name,
 /* 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>.  */
+   within foo<int, int>, while considering C++ operator</operator<<.  */
 
 const char *
 find_toplevel_char (const char *s, char c)
@@ -1140,6 +1175,47 @@ find_toplevel_char (const char *s, char c)
 	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;
-- 
2.5.5

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

* [PATCH 12/40] "complete" command and completion word break characters
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (24 preceding siblings ...)
  2017-06-02 12:29 ` [PATCH 17/40] Linespec lexing and C++ operators Pedro Alves
@ 2017-06-02 12:29 ` Pedro Alves
  2017-07-14 17:50   ` Keith Seitz
  2017-06-02 12:29 ` [PATCH 07/40] objfile_per_bfd_storage non-POD Pedro Alves
                   ` (14 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:29 UTC (permalink / raw)
  To: gdb-patches

The linespec/locations/completer testcase added later in the series
tests every completion with both TAB completion and the "complete"
command.  This exposed problems in the "complete" command, around
determining the completion word point.

First, the complete command has a too-simple approximation of what
readline's TAB-completion code does to find the completion word point.
Unfortunately, readline doesn't expose the functionality it uses
internally, so to fix this this patch copies over the relevant code,
and adjusts it a bit to better fit the use cases we need it for.
(Specifically, our version avoids relying on the
rl_word_break_characters, etc. globals, and instead takes those as
arguments.)

A following patch will want to use this function for TAB-completion
too, but the "complete" command was a good excuse to split this to a
separate patch.

Then, notice how the complete_command does not call into the completer
for the command being completed to determine the right set of word
break characters.  It always uses the default set.  That is fixed by
having the "complete" command call into complete_line_internal for a
full handle_brkchars phase, just TAB-completion.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-cmds.c (complete_command): Use a completion tracker
	along with completion_find_completion_word for handle_brkchars
	phase.
	* completer.c (RL_QF_SINGLE_QUOTE, RL_QF_DOUBLE_QUOTE)
	(RL_QF_BACKSLASH, RL_QF_OTHER_QUOTE): New.
	(struct gdb_rl_completion_word_info): New.
	(gdb_rl_find_completion_word): New.
	(completion_find_completion_word): New.
	* completer.h (completion_find_completion_word): Declare.
---
 gdb/cli/cli-cmds.c |  34 +++++-----
 gdb/completer.c    | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/completer.h    |   4 ++
 3 files changed, 199 insertions(+), 19 deletions(-)

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 7756fcc..7717577 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -246,7 +246,6 @@ static void
 complete_command (char *arg_entry, int from_tty)
 {
   const char *arg = arg_entry;
-  int argpoint;
 
   dont_repeat ();
 
@@ -264,36 +263,31 @@ complete_command (char *arg_entry, int from_tty)
 
   if (arg == NULL)
     arg = "";
-  argpoint = strlen (arg);
-
-  /* complete_line assumes that its first argument is somewhere
-     within, and except for filenames at the beginning of, the word to
-     be completed.  The following crude imitation of readline's
-     word-breaking tries to accomodate this.  */
-  const char *point = arg + argpoint;
-  while (point > arg)
-    {
-      if (strchr (rl_completer_word_break_characters, point[-1]) != 0)
-        break;
-      point--;
-    }
 
+  completion_tracker tracker_handle_brkchars;
   completion_tracker tracker_handle_completions;
 
+  int quote_char = '\0';
+  const char *word;
+
   TRY
     {
-      complete_line (tracker_handle_completions, point, arg, strlen (arg));
+      word = completion_find_completion_word (tracker_handle_brkchars,
+					      arg, &quote_char);
+
+      /* Completers must be called twice.  */
+      complete_line (tracker_handle_completions, word, arg, strlen (arg));
     }
   CATCH (ex, RETURN_MASK_ALL)
     {
       return;
     }
 
-  std::string arg_prefix (arg, point - arg);
+  std::string arg_prefix (arg, word - arg);
 
   completion_result result
     = (tracker_handle_completions.build_completion_result
-       (point, point - arg, strlen (arg)));
+       (word, word - arg, strlen (arg)));
 
   if (result.number_matches != 0)
     {
@@ -308,16 +302,18 @@ complete_command (char *arg_entry, int from_tty)
 	      printf_unfiltered ("%s%s",
 				 arg_prefix.c_str (),
 				 result.match_list[i + 1]);
+	      if (quote_char)
+		printf_unfiltered ("%c", quote_char);
 	      printf_unfiltered ("\n");
 	    }
 	}
 
       if (result.number_matches == max_completions)
 	{
-	  /* ARG_PREFIX and POINT are included in the output so that emacs
+	  /* ARG_PREFIX and WORD are included in the output so that emacs
 	     will include the message in the output.  */
 	  printf_unfiltered (_("%s%s %s\n"),
-			     arg_prefix.c_str (), point,
+			     arg_prefix.c_str (), word,
 			     get_max_completions_reached_message ());
 	}
     }
diff --git a/gdb/completer.c b/gdb/completer.c
index c6e1e28..a1d3a43 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -212,6 +212,166 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
     (gdb_completer_file_name_break_characters);
 }
 
+/* Possible values for the found_quote flags word used by the completion
+   functions.  It says what kind of (shell-like) quoting we found anywhere
+   in the line. */
+#define RL_QF_SINGLE_QUOTE      0x01
+#define RL_QF_DOUBLE_QUOTE      0x02
+#define RL_QF_BACKSLASH         0x04
+#define RL_QF_OTHER_QUOTE       0x08
+
+/* Find the bounds of the current word for completion purposes, and leave
+   rl_point set to the end of the word.  This function skips quoted
+   substrings (characters between matched pairs of characters in
+   rl_completer_quote_characters).  First we try to find an unclosed
+   quoted substring on which to do matching.  If one is not found, we use
+   the word break characters to find the boundaries of the current word.
+   We call an application-specific function to decide whether or not a
+   particular word break character is quoted; if that function returns a
+   non-zero result, the character does not break a word.  This function
+   returns the opening quote character if we found an unclosed quoted
+   substring, '\0' otherwise.  FP, if non-null, is set to a value saying
+   which (shell-like) quote characters we found (single quote, double
+   quote, or backslash) anywhere in the string.  DP, if non-null, is set to
+   the value of the delimiter character that caused a word break. */
+
+struct gdb_rl_completion_word_info
+{
+  const char *word_break_characters;
+  const char *quote_characters;
+  const char *basic_quote_characters;
+};
+
+static const char *
+gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
+			     int *qc, int *dp,
+			     const char *line_buffer)
+{
+  int scan, end, found_quote, delimiter, pass_next, isbrk;
+  char quote_char;
+  const char *brkchars;
+  int point = strlen (line_buffer);
+
+  /* The algorithm below does '--point'.  Avoid buffer underflow with
+     the empty string.  */
+  if (point == 0)
+    {
+      if (qc != NULL)
+	*qc = '\0';
+      if (dp != NULL)
+	*dp = '\0';
+      return line_buffer;
+    }
+
+  end = point;
+  found_quote = delimiter = 0;
+  quote_char = '\0';
+
+  brkchars = info->word_break_characters;
+
+  if (info->quote_characters != NULL)
+    {
+      /* We have a list of characters which can be used in pairs to
+	 quote substrings for the completer.  Try to find the start of
+	 an unclosed quoted substring.  */
+      /* FOUND_QUOTE is set so we know what kind of quotes we
+	 found.  */
+      for (scan = pass_next = 0;
+	   scan < end;
+	   scan++)
+	{
+	  if (pass_next)
+	    {
+	      pass_next = 0;
+	      continue;
+	    }
+
+	  /* Shell-like semantics for single quotes -- don't allow
+	     backslash to quote anything in single quotes, especially
+	     not the closing quote.  If you don't like this, take out
+	     the check on the value of quote_char.  */
+	  if (quote_char != '\'' && line_buffer[scan] == '\\')
+	    {
+	      pass_next = 1;
+	      found_quote |= RL_QF_BACKSLASH;
+	      continue;
+	    }
+
+	  if (quote_char != '\0')
+	    {
+	      /* Ignore everything until the matching close quote
+		 char.  */
+	      if (line_buffer[scan] == quote_char)
+		{
+		  /* Found matching close.  Abandon this
+		     substring.  */
+		  quote_char = '\0';
+		  point = end;
+		}
+	    }
+	  else if (strchr (info->quote_characters, line_buffer[scan]))
+	    {
+	      /* Found start of a quoted substring.  */
+	      quote_char = line_buffer[scan];
+	      point = scan + 1;
+	      /* Shell-like quoting conventions.  */
+	      if (quote_char == '\'')
+		found_quote |= RL_QF_SINGLE_QUOTE;
+	      else if (quote_char == '"')
+		found_quote |= RL_QF_DOUBLE_QUOTE;
+	      else
+		found_quote |= RL_QF_OTHER_QUOTE;
+	    }
+	}
+    }
+
+  if (point == end && quote_char == '\0')
+    {
+      /* We didn't find an unclosed quoted substring upon which to do
+	 completion, so use the word break characters to find the
+	 substring on which to complete.  */
+      while (--point)
+	{
+	  scan = line_buffer[point];
+
+	  if (strchr (brkchars, scan) != 0)
+	    break;
+	}
+    }
+
+  /* If we are at an unquoted word break, then advance past it.  */
+  scan = line_buffer[point];
+
+  /* If there is an application-specific function to say whether or
+     not a character is quoted and we found a quote character, let
+     that function decide whether or not a character is a word break,
+     even if it is found in rl_completer_word_break_characters.  Don't
+     bother if we're at the end of the line, though.  */
+  if (scan)
+    {
+      isbrk = strchr (brkchars, scan) != 0;
+
+      if (isbrk)
+	{
+	  /* If the character that caused the word break was a quoting
+	     character, then remember it as the delimiter.  */
+	  if (info->basic_quote_characters
+	      && strchr (info->basic_quote_characters, scan)
+	      && (end - point) > 1)
+	    delimiter = scan;
+
+	  point++;
+	}
+    }
+
+  if (qc != NULL)
+    *qc = quote_char;
+  if (dp != NULL)
+    *dp = delimiter;
+
+  return line_buffer + point;
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -1305,6 +1465,26 @@ gdb_completion_word_break_characters ()
   return NULL;
 }
 
+/* Get the list of chars that are considered as word breaks
+   for the current command.  */
+
+const char *
+completion_find_completion_word (completion_tracker &tracker, const char *text,
+				 int *quote_char)
+{
+  size_t point = strlen (text);
+
+  complete_line_internal (tracker, NULL, text, point, handle_brkchars);
+
+  gdb_rl_completion_word_info info;
+
+  info.word_break_characters = rl_completer_word_break_characters;
+  info.quote_characters = gdb_completer_quote_characters;
+  info.basic_quote_characters = rl_basic_quote_characters;
+
+  return gdb_rl_find_completion_word (&info, quote_char, NULL, text);
+}
+
 /* See completer.h.  */
 
 void
diff --git a/gdb/completer.h b/gdb/completer.h
index e554bff..207781d 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -203,6 +203,10 @@ extern void complete_line (completion_tracker &tracker,
 			   const char *line_buffer,
 			   int point);
 
+extern const char *completion_find_completion_word (completion_tracker &tracker,
+						    const char *text,
+						    int *quote_char);
+
 extern char **gdb_rl_attempted_completion_function (const char *text,
 						    int start, int end);
 
-- 
2.5.5

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

* [PATCH 05/40] command.h: Include scoped_restore_command.h
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (22 preceding siblings ...)
  2017-06-02 12:28 ` [PATCH 24/40] Per-language symbol name hashing algorithm Pedro Alves
@ 2017-06-02 12:29 ` Pedro Alves
  2017-06-27 11:30   ` Yao Qi
  2017-06-02 12:29 ` [PATCH 17/40] Linespec lexing and C++ operators Pedro Alves
                   ` (16 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:29 UTC (permalink / raw)
  To: gdb-patches

This file depends on scoped_restore:

  extern scoped_restore_tmpl<int> prevent_dont_repeat (void);

But doesn't include the corresponding header.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* command.h: Include "scoped_restore_command.h".
---
 gdb/command.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gdb/command.h b/gdb/command.h
index aa179e9..4a56a51 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -19,6 +19,7 @@
 #define COMMAND_H 1
 
 #include "gdb_vecs.h"
+#include "common/scoped_restore.h"
 
 /* This file defines the public interface for any code wanting to
    create commands.  */
-- 
2.5.5

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

* [PATCH 33/40] Make the linespec/location completer ignore data symbols
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (28 preceding siblings ...)
  2017-06-02 12:29 ` [PATCH 16/40] Explicit locations -label completer Pedro Alves
@ 2017-06-02 12:29 ` Pedro Alves
  2017-08-09 15:42   ` Keith Seitz
  2017-06-02 12:30 ` [PATCH 30/40] Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints Pedro Alves
                   ` (10 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:29 UTC (permalink / raw)
  To: gdb-patches

Currently "b foo[TAB]" offers data symbols as completion candidates.
This doesn't make sense, since you can't set a breakpoint on data
symbols, only on code symbols.

 (gdb) b globa[TAB]
 (gdb) b global [ENTER]
 Function "global" not defined.
 Make breakpoint pending on future shared library load? (y or [n]) n
 (gdb) info symbol global
 global in section .rodata

So this patch makes linespec completion ignore data symbols.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_make_symbol_completion_list): Use
	completion_skip_symbol.
	* symtab.c (symbol_is_function_or_method(minimal_symbol*)): New.
	(symbol_is_function_or_method(symbol*)): New.
	(add_symtab_completions): Add complete_symbol_mode parameter.  Use
	completion_skip_symbol.
	(default_collect_symbol_completion_matches_break_on): Use
	completion_skip_symbol.  Pass down mode.
	(collect_file_symbol_completion_matches): Pass down mode.
	* symtab.h (symbol_is_function_or_method): New declarations.
	(completion_skip_symbol): New template function.
---
 gdb/ada-lang.c | 12 ++++++++++++
 gdb/symtab.c   | 45 ++++++++++++++++++++++++++++++++++++++++++---
 gdb/symtab.h   | 20 ++++++++++++++++++++
 3 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 868ee35..c53f84b 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6463,6 +6463,9 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
   {
     QUIT;
 
+    if (completion_skip_symbol (mode, msymbol))
+      continue;
+
     completion_list_add_name (tracker,
 			      MSYMBOL_LANGUAGE (msymbol),
 			      MSYMBOL_LINKAGE_NAME (msymbol),
@@ -6479,6 +6482,9 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
       ALL_BLOCK_SYMBOLS (b, iter, sym)
       {
+	if (completion_skip_symbol (mode, sym))
+	  continue;
+
 	completion_list_add_name (tracker,
 				  SYMBOL_LANGUAGE (sym),
 				  SYMBOL_LINKAGE_NAME (sym),
@@ -6495,6 +6501,9 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
     b = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (s), GLOBAL_BLOCK);
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
+      if (completion_skip_symbol (mode, sym))
+	continue;
+
       completion_list_add_name (tracker,
 				SYMBOL_LANGUAGE (sym),
 				SYMBOL_LINKAGE_NAME (sym),
@@ -6511,6 +6520,9 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
       continue;
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
+      if (completion_skip_symbol (mode, sym))
+	continue;
+
       completion_list_add_name (tracker,
 				SYMBOL_LANGUAGE (sym),
 				SYMBOL_LINKAGE_NAME (sym),
diff --git a/gdb/symtab.c b/gdb/symtab.c
index bbc317d..52cf70a 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -5034,11 +5034,44 @@ completion_list_add_fields (completion_tracker &tracker,
     }
 }
 
+/* See symtab.h.  */
+
+bool
+symbol_is_function_or_method (symbol *sym)
+{
+  switch (TYPE_CODE (SYMBOL_TYPE (sym)))
+    {
+    case TYPE_CODE_FUNC:
+    case TYPE_CODE_METHOD:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Return whether MSYMBOL is a function/method.  */
+
+bool
+symbol_is_function_or_method (minimal_symbol *msymbol)
+{
+  switch (MSYMBOL_TYPE (msymbol))
+    {
+    case mst_text:
+    case mst_text_gnu_ifunc:
+    case mst_solib_trampoline:
+    case mst_file_text:
+      return true;
+    default:
+      return false;
+    }
+}
+
 /* Add matching symbols from SYMTAB to the current completion list.  */
 
 static void
 add_symtab_completions (struct compunit_symtab *cust,
 			completion_tracker &tracker,
+			complete_symbol_mode mode,
 			const lookup_name_info &lookup_name,
 			const char *text, const char *word,
 			enum type_code code)
@@ -5057,6 +5090,9 @@ add_symtab_completions (struct compunit_symtab *cust,
       b = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cust), i);
       ALL_BLOCK_SYMBOLS (b, iter, sym)
 	{
+	  if (completion_skip_symbol (mode, sym))
+	    continue;
+
 	  if (code == TYPE_CODE_UNDEF
 	      || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
@@ -5156,6 +5192,9 @@ default_collect_symbol_completion_matches_break_on
 	{
 	  QUIT;
 
+	  if (completion_skip_symbol (mode, msymbol))
+	    continue;
+
 	  completion_list_add_msymbol (tracker, msymbol, lookup_name,
 				       sym_text, word);
 
@@ -5166,7 +5205,7 @@ default_collect_symbol_completion_matches_break_on
 
   /* Add completions for all currently loaded symbol tables.  */
   ALL_COMPUNITS (objfile, cust)
-    add_symtab_completions (cust, tracker, lookup_name,
+    add_symtab_completions (cust, tracker, mode, lookup_name,
 			    sym_text, word, code);
 
   /* Look through the partial symtabs for all symbols which begin by
@@ -5177,7 +5216,7 @@ default_collect_symbol_completion_matches_break_on
 			   [&] (compunit_symtab *symtab) /* expansion notify */
 			     {
 			       add_symtab_completions (symtab,
-						       tracker, lookup_name,
+						       tracker, mode, lookup_name,
 						       sym_text, word, code);
 			     },
 			   ALL_DOMAIN);
@@ -5384,7 +5423,7 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
   iterate_over_symtabs (srcfile, [&] (symtab *s)
     {
       add_symtab_completions (SYMTAB_COMPUNIT (s),
-			      tracker, lookup_name,
+			      tracker, mode, lookup_name,
 			      sym_text, word, TYPE_CODE_UNDEF);
       return false;
     });
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 736fea0..26444ad 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1809,6 +1809,26 @@ extern void collect_file_symbol_completion_matches
 extern completion_list
   make_source_files_completion_list (const char *, const char *);
 
+/* Return whether SYM is a function/method, as opposed to a data symbol.  */
+
+extern bool symbol_is_function_or_method (symbol *sym);
+
+/* Return whether MSYMBOL is a function/method, as opposed to a data
+   symbol */
+
+extern bool symbol_is_function_or_method (minimal_symbol *msymbol);
+
+/* Return whether SYM should be skipped in completion mode MODE.  In
+   linespec mode, we're only interested in functions/methods.  */
+
+template <typename Symbol>
+static bool
+completion_skip_symbol (complete_symbol_mode mode, Symbol *sym)
+{
+  return (mode == complete_symbol_mode::LINESPEC
+	  && !symbol_is_function_or_method (sym));
+}
+
 /* symtab.c */
 
 int matching_obj_sections (struct obj_section *, struct obj_section *);
-- 
2.5.5

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

* [PATCH 07/40] objfile_per_bfd_storage non-POD
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (25 preceding siblings ...)
  2017-06-02 12:29 ` [PATCH 12/40] "complete" command and completion word break characters Pedro Alves
@ 2017-06-02 12:29 ` Pedro Alves
  2017-06-27 12:00   ` Yao Qi
  2017-06-02 12:29 ` [PATCH 21/40] Use SYMBOL_MATCHES_SEARCH_NAME some more Pedro Alves
                   ` (13 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:29 UTC (permalink / raw)
  To: gdb-patches

A following patch will want to add a std::vector to
objfile_per_bfd_storage.  That makes it non-trivially
constructible/destructible.  Since objfile_per_bfd_storage objects are
allocated on an obstack, we need to call their ctors/dtors manually.
This is what this patch does.  And then since we can now rely on
ctors/dtors being run, make objfile_per_bfd_storage::storage_obstack
be an auto_obstack.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* objfiles.c (get_objfile_bfd_data): Call bfd_alloc instead of
	bfd_zalloc.  Call objfile_per_bfd_storage's ctor.
	(free_objfile_per_bfd_storage): Call objfile_per_bfd_storage's
	dtor.
	* objfiles.h (objfile_per_bfd_storage): Add ctor.  Make
	'storage_obstack' field an auto_obstack.  In-class initialize all
	non-bitfield fields.  Make minsyms_read bool.
	* symfile.c (read_symbols): Adjust.
---
 gdb/objfiles.c | 15 +++++++++++----
 gdb/objfiles.h | 32 ++++++++++++++++++--------------
 gdb/symfile.c  |  2 +-
 3 files changed, 30 insertions(+), 19 deletions(-)

diff --git a/gdb/objfiles.c b/gdb/objfiles.c
index 9500b1c..d261c87 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -142,12 +142,19 @@ get_objfile_bfd_data (struct objfile *objfile, struct bfd *abfd)
 	{
 	  storage
 	    = ((struct objfile_per_bfd_storage *)
-	       bfd_zalloc (abfd, sizeof (struct objfile_per_bfd_storage)));
+	       bfd_alloc (abfd, sizeof (struct objfile_per_bfd_storage)));
 	  set_bfd_data (abfd, objfiles_bfd_data, storage);
 	}
       else
-	storage = OBSTACK_ZALLOC (&objfile->objfile_obstack,
-				  struct objfile_per_bfd_storage);
+	{
+	  storage = (objfile_per_bfd_storage *)
+	    obstack_alloc (&objfile->objfile_obstack,
+			   sizeof (objfile_per_bfd_storage));
+	}
+
+      /* objfile_per_bfd_storage is not trivially constructible, must
+	 call the ctor manually.  */
+      storage = new (storage) objfile_per_bfd_storage ();
 
       /* Look up the gdbarch associated with the BFD.  */
       if (abfd != NULL)
@@ -171,7 +178,7 @@ free_objfile_per_bfd_storage (struct objfile_per_bfd_storage *storage)
   bcache_xfree (storage->macro_cache);
   if (storage->demangled_names_hash)
     htab_delete (storage->demangled_names_hash);
-  obstack_free (&storage->storage_obstack, 0);
+  storage->~objfile_per_bfd_storage ();
 }
 
 /* A wrapper for free_objfile_per_bfd_storage that can be passed as a
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 58db6c9..3260425 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -184,24 +184,28 @@ extern void print_symbol_bcache_statistics (void);
 
 struct objfile_per_bfd_storage
 {
+  objfile_per_bfd_storage ()
+    : minsyms_read (false)
+  {}
+
   /* The storage has an obstack of its own.  */
 
-  struct obstack storage_obstack;
+  auto_obstack storage_obstack;
 
   /* Byte cache for file names.  */
 
-  struct bcache *filename_cache;
+  bcache *filename_cache = NULL;
 
   /* Byte cache for macros.  */
 
-  struct bcache *macro_cache;
+  bcache *macro_cache = NULL;
 
   /* The gdbarch associated with the BFD.  Note that this gdbarch is
      determined solely from BFD information, without looking at target
      information.  The gdbarch determined from a running target may
      differ from this e.g. with respect to register types and names.  */
 
-  struct gdbarch *gdbarch;
+  struct gdbarch *gdbarch = NULL;
 
   /* Hash table for mapping symbol names to demangled names.  Each
      entry in the hash table is actually two consecutive strings,
@@ -209,19 +213,19 @@ struct objfile_per_bfd_storage
      name, and the second is the demangled name or just a zero byte
      if the name doesn't demangle.  */
 
-  struct htab *demangled_names_hash;
+  htab *demangled_names_hash = NULL;
 
   /* The per-objfile information about the entry point, the scope (file/func)
      containing the entry point, and the scope of the user's main() func.  */
 
-  struct entry_info ei;
+  entry_info ei {};
 
   /* The name and language of any "main" found in this objfile.  The
      name can be NULL, which means that the information was not
      recorded.  */
 
-  const char *name_of_main;
-  enum language language_of_main;
+  const char *name_of_main = NULL;
+  enum language language_of_main = language_unknown;
 
   /* Each file contains a pointer to an array of minimal symbols for all
      global symbols that are defined within the file.  The array is
@@ -233,15 +237,15 @@ struct objfile_per_bfd_storage
      as all the data that it points to, should be allocated on the
      objfile_obstack for this file.  */
 
-  struct minimal_symbol *msymbols;
-  int minimal_symbol_count;
+  minimal_symbol *msymbols = NULL;
+  int minimal_symbol_count = 0;
 
   /* The number of minimal symbols read, before any minimal symbol
      de-duplication is applied.  Note in particular that this has only
      a passing relationship with the actual size of the table above;
      use minimal_symbol_count if you need the true size.  */
 
-  int n_minsyms;
+  int n_minsyms = 0;
 
   /* This is true if minimal symbols have already been read.  Symbol
      readers can use this to bypass minimal symbol reading.  Also, the
@@ -251,16 +255,16 @@ struct objfile_per_bfd_storage
      for multiple readers to install minimal symbols into a given
      per-BFD.  */
 
-  unsigned int minsyms_read : 1;
+  bool minsyms_read : 1;
 
   /* This is a hash table used to index the minimal symbols by name.  */
 
-  struct minimal_symbol *msymbol_hash[MINIMAL_SYMBOL_HASH_SIZE];
+  minimal_symbol *msymbol_hash[MINIMAL_SYMBOL_HASH_SIZE] {};
 
   /* This hash table is used to index the minimal symbols by their
      demangled names.  */
 
-  struct minimal_symbol *msymbol_demangled_hash[MINIMAL_SYMBOL_HASH_SIZE];
+  minimal_symbol *msymbol_demangled_hash[MINIMAL_SYMBOL_HASH_SIZE] {};
 };
 
 /* Master structure for keeping track of each file from which
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 846aabe..7892d17 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -859,7 +859,7 @@ static void
 read_symbols (struct objfile *objfile, symfile_add_flags add_flags)
 {
   (*objfile->sf->sym_read) (objfile, add_flags);
-  objfile->per_bfd->minsyms_read = 1;
+  objfile->per_bfd->minsyms_read = true;
 
   /* find_separate_debug_file_in_section should be called only if there is
      single binary with no existing separate debug info file.  */
-- 
2.5.5

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

* [PATCH 16/40] Explicit locations -label completer
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (27 preceding siblings ...)
  2017-06-02 12:29 ` [PATCH 21/40] Use SYMBOL_MATCHES_SEARCH_NAME some more Pedro Alves
@ 2017-06-02 12:29 ` Pedro Alves
  2017-07-14 21:32   ` Keith Seitz
  2017-06-02 12:29 ` [PATCH 33/40] Make the linespec/location completer ignore data symbols Pedro Alves
                   ` (11 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:29 UTC (permalink / raw)
  To: gdb-patches

We're missing a completer for

  (gdb) break -function func -label [TAB]

This patch adds one.  Tests will be added later in the series.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.c (collect_explicit_location_matches): Handle
	MATCH_LABEL.
	(convert_explicit_location_to_linespec): New, factored out from
	...
	(convert_explicit_location_to_sals): ... this.
	(complete_label): New.
	(linespec_complete_label, find_label_symbols_in_block): New.
	(find_label_symbols): Add completion_mode parameter and adjust to
	call find_label_symbols_in_block.
	* linespec.h (linespec_complete_label): Declare.
---
 gdb/completer.c |   8 ++-
 gdb/linespec.c  | 199 ++++++++++++++++++++++++++++++++++++++++++++------------
 gdb/linespec.h  |  11 ++++
 3 files changed, 177 insertions(+), 41 deletions(-)

diff --git a/gdb/completer.c b/gdb/completer.c
index 0473d8c..c7986e6 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -633,7 +633,13 @@ collect_explicit_location_matches (completion_tracker &tracker,
       break;
 
     case MATCH_LABEL:
-      /* Not supported.  */
+      {
+	const char *label = string_or_empty (explicit_loc->label_name);
+	linespec_complete_label (tracker, language,
+				 explicit_loc->source_filename,
+				 explicit_loc->function_name,
+				 label);
+      }
       break;
 
     default:
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 20ac71d..0216bf1 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -299,7 +299,8 @@ static VEC (symtab_ptr) *symtabs_from_filename (const char *,
 static VEC (symbolp) *find_label_symbols (struct linespec_state *self,
 					  VEC (symbolp) *function_symbols,
 					  VEC (symbolp) **label_funcs_ret,
-					  const char *name);
+					  const char *name,
+					  bool completion_mode = false);
 
 static void find_linespec_symbols (struct linespec_state *self,
 				   VEC (symtab_ptr) *file_symtabs,
@@ -2053,31 +2054,33 @@ convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
   return sals;
 }
 
-/* Convert the explicit location EXPLICIT_LOC into SaLs.  */
+/* Build RESULT from the explicit location components SOURCE_FILENAME,
+   FUNCTION_NAME, LABEL_NAME and LINE_OFFSET.  */
 
-static struct symtabs_and_lines
-convert_explicit_location_to_sals (struct linespec_state *self,
-				   linespec_p result,
-				   const struct explicit_location *explicit_loc)
+static void
+convert_explicit_location_to_linespec (struct linespec_state *self,
+				       linespec_p result,
+				       const char *source_filename,
+				       const char *function_name,
+				       const char *label_name,
+				       struct line_offset line_offset)
 {
   VEC (symbolp) *symbols, *labels;
   VEC (bound_minimal_symbol_d) *minimal_symbols;
 
-  if (explicit_loc->source_filename != NULL)
+  if (source_filename != NULL)
     {
       TRY
 	{
 	  result->file_symtabs
-	    = symtabs_from_filename (explicit_loc->source_filename,
-				     self->search_pspace);
+	    = symtabs_from_filename (source_filename, self->search_pspace);
 	}
       CATCH (except, RETURN_MASK_ERROR)
 	{
-	  source_file_not_found_error (explicit_loc->source_filename);
+	  source_file_not_found_error (source_filename);
 	}
       END_CATCH
-      result->explicit_loc.source_filename
-	= xstrdup (explicit_loc->source_filename);
+      result->explicit_loc.source_filename = xstrdup (source_filename);
     }
   else
     {
@@ -2085,41 +2088,53 @@ convert_explicit_location_to_sals (struct linespec_state *self,
       VEC_safe_push (symtab_ptr, result->file_symtabs, NULL);
     }
 
-  if (explicit_loc->function_name != NULL)
+  if (function_name != NULL)
     {
       find_linespec_symbols (self, result->file_symtabs,
-			     explicit_loc->function_name, &symbols,
+			     function_name, &symbols,
 			     &minimal_symbols);
 
       if (symbols == NULL && minimal_symbols == NULL)
-	symbol_not_found_error (explicit_loc->function_name,
+	symbol_not_found_error (function_name,
 				result->explicit_loc.source_filename);
 
-      result->explicit_loc.function_name
-	= xstrdup (explicit_loc->function_name);
+      result->explicit_loc.function_name = xstrdup (function_name);
       result->function_symbols = symbols;
       result->minimal_symbols = minimal_symbols;
     }
 
-  if (explicit_loc->label_name != NULL)
+  if (label_name != NULL)
     {
       symbols = NULL;
       labels = find_label_symbols (self, result->function_symbols,
-				   &symbols, explicit_loc->label_name);
+				   &symbols, label_name);
 
       if (labels == NULL)
 	undefined_label_error (result->explicit_loc.function_name,
-			       explicit_loc->label_name);
+			       label_name);
 
-      result->explicit_loc.label_name = xstrdup (explicit_loc->label_name);
+      result->explicit_loc.label_name = xstrdup (label_name);
       result->labels.label_symbols = labels;
       result->labels.function_symbols = symbols;
     }
 
-  if (explicit_loc->line_offset.sign != LINE_OFFSET_UNKNOWN)
-    result->explicit_loc.line_offset = explicit_loc->line_offset;
+  if (line_offset.sign != LINE_OFFSET_UNKNOWN)
+    result->explicit_loc.line_offset = line_offset;
+}
 
-   return convert_linespec_to_sals (self, result);
+/* Convert the explicit location EXPLICIT_LOC into SaLs.  */
+
+static struct symtabs_and_lines
+convert_explicit_location_to_sals (struct linespec_state *self,
+				   linespec_p result,
+				   const struct explicit_location *explicit_loc)
+{
+  convert_explicit_location_to_linespec (self, result,
+					 explicit_loc->source_filename,
+					 explicit_loc->function_name,
+					 explicit_loc->label_name,
+					 explicit_loc->line_offset);
+  return convert_linespec_to_sals (self, result);
 }
 
 /* Parse a string that specifies a linespec.
@@ -2470,6 +2485,69 @@ linespec_complete_function (completion_tracker &tracker,
     collect_symbol_completion_matches (tracker, mode, function, function);
 }
 
+/* Helper for linespec_complete_label.  Find labels that match
+   LABEL_NAME in the function symbols listed in the PARSER, and add
+   them to the tracker.  */
+
+static void
+complete_label (completion_tracker &tracker,
+		linespec_parser *parser,
+		const char *label_name)
+{
+  VEC (symbolp) *label_function_symbols = NULL;
+  VEC (symbolp) *labels
+    = find_label_symbols (PARSER_STATE (parser),
+			  PARSER_RESULT (parser)->function_symbols,
+			  &label_function_symbols,
+			  label_name, true);
+
+  symbol *label;
+  for (int ix = 0;
+       VEC_iterate (symbolp, labels, ix, label); ++ix)
+    {
+      char *match = xstrdup (SYMBOL_SEARCH_NAME (label));
+      tracker.add_completion (gdb::unique_xmalloc_ptr<char> (match));
+    }
+  VEC_free (symbolp, labels);
+}
+
+/* See linespec.h.  */
+
+void
+linespec_complete_label (completion_tracker &tracker,
+			 const struct language_defn *language,
+			 const char *source_filename,
+			 const char *function_name,
+			 const char *label_name)
+{
+  linespec_parser parser;
+  struct cleanup *cleanup;
+
+  linespec_parser_new (&parser, 0, language, NULL, NULL, 0, NULL);
+  cleanup = make_cleanup (linespec_parser_delete, &parser);
+
+  line_offset unknown_offset = { 0, LINE_OFFSET_UNKNOWN };
+
+  TRY
+    {
+      convert_explicit_location_to_linespec (PARSER_STATE (&parser),
+					     PARSER_RESULT (&parser),
+					     source_filename,
+					     function_name,
+					     NULL, unknown_offset);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      do_cleanups (cleanup);
+      return;
+    }
+  END_CATCH
+
+  complete_label (tracker, &parser, label_name);
+
+  do_cleanups (cleanup);
+}
+
 /* A helper function for decode_line_full and decode_line_1 to
    turn LOCATION into symtabs_and_lines.  */
 
@@ -3385,13 +3463,63 @@ find_linespec_symbols (struct linespec_state *state,
     }
 }
 
-/* Return all labels named NAME in FUNCTION_SYMBOLS.  Return the
-   actual function symbol in which the label was found in LABEL_FUNC_RET.  */
+/* Helper for find_label_symbols.  Find all labels that match name
+   NAME in BLOCK.  Return all labels that match in FUNCTION_SYMBOLS.
+   Return the actual function symbol in which the label was found in
+   LABEL_FUNC_RET.  If COMPLETION_MODE is true, then NAME is
+   interpreted as a label name prefix.  Otherwise, only a label named
+   exactly NAME match.  */
+
+static void
+find_label_symbols_in_block (const struct block *block,
+			     const char *name, struct symbol *fn_sym,
+			     bool completion_mode,
+			     VEC (symbolp) **result,
+			     VEC (symbolp) **label_funcs_ret)
+{
+  if (completion_mode)
+    {
+      struct block_iterator iter;
+      struct symbol *sym;
+      size_t name_len = strlen (name);
+
+      int (*cmp) (const char *, const char *, size_t);
+      cmp = case_sensitivity == case_sensitive_on ? strncmp : strncasecmp;
+
+      ALL_BLOCK_SYMBOLS (block, iter, sym)
+	{
+	  if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
+				     SYMBOL_DOMAIN (sym), LABEL_DOMAIN)
+	      && cmp (SYMBOL_SEARCH_NAME (sym), name, name_len) == 0)
+	    {
+	      VEC_safe_push (symbolp, *result, sym);
+	      VEC_safe_push (symbolp, *label_funcs_ret, fn_sym);
+	    }
+	}
+    }
+  else
+    {
+      struct symbol *sym = lookup_symbol (name, block, LABEL_DOMAIN, 0).symbol;
+
+      if (sym != NULL)
+	{
+	  VEC_safe_push (symbolp, *result, sym);
+	  VEC_safe_push (symbolp, *label_funcs_ret, fn_sym);
+	}
+    }
+}
+
+/* Return all labels that match name NAME in FUNCTION_SYMBOLS.  Return
+   the actual function symbol in which the label was found in
+   LABEL_FUNC_RET.  If COMPLETION_MODE is true, then NAME is
+   interpreted as a label name prefix.  Otherwise, only labels named
+   exactly NAME match.  */
 
 static VEC (symbolp) *
 find_label_symbols (struct linespec_state *self,
 		    VEC (symbolp) *function_symbols,
-		    VEC (symbolp) **label_funcs_ret, const char *name)
+		    VEC (symbolp) **label_funcs_ret, const char *name,
+		    bool completion_mode)
 {
   int ix;
   const struct block *block;
@@ -3412,13 +3540,8 @@ find_label_symbols (struct linespec_state *self,
 	return NULL;
       fn_sym = BLOCK_FUNCTION (block);
 
-      sym = lookup_symbol (name, block, LABEL_DOMAIN, 0).symbol;
-
-      if (sym != NULL)
-	{
-	  VEC_safe_push (symbolp, result, sym);
-	  VEC_safe_push (symbolp, *label_funcs_ret, fn_sym);
-	}
+      find_label_symbols_in_block (block, name, fn_sym, completion_mode,
+				   &result, label_funcs_ret);
     }
   else
     {
@@ -3427,13 +3550,9 @@ find_label_symbols (struct linespec_state *self,
 	{
 	  set_current_program_space (SYMTAB_PSPACE (symbol_symtab (fn_sym)));
 	  block = SYMBOL_BLOCK_VALUE (fn_sym);
-	  sym = lookup_symbol (name, block, LABEL_DOMAIN, 0).symbol;
 
-	  if (sym != NULL)
-	    {
-	      VEC_safe_push (symbolp, result, sym);
-	      VEC_safe_push (symbolp, *label_funcs_ret, fn_sym);
-	    }
+	  find_label_symbols_in_block (block, name, fn_sym, completion_mode,
+				       &result, label_funcs_ret);
 	}
     }
 
diff --git a/gdb/linespec.h b/gdb/linespec.h
index d55ba12..f16bb81 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -194,6 +194,17 @@ extern void linespec_complete_function (completion_tracker &tracker,
 					const char *function,
 					const char *source_filename);
 
+/* Complete a label symbol, in linespec mode.  Only labels of
+   functions named FUNCTION_NAME are considered.  If SOURCE_FILENAME
+   is non-NULL, limits completion to labels of functions defined in
+   source files that match SOURCE_FILENAME.  */
+
+extern void linespec_complete_label (completion_tracker &tracker,
+				     const struct language_defn *language,
+				     const char *source_filename,
+				     const char *function_name,
+				     const char *label_name);
+
 /* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
    advancing EXP_PTR past any parsed text.  */
 
-- 
2.5.5

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

* [PATCH 30/40] Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (29 preceding siblings ...)
  2017-06-02 12:29 ` [PATCH 33/40] Make the linespec/location completer ignore data symbols Pedro Alves
@ 2017-06-02 12:30 ` Pedro Alves
  2017-08-08 21:07   ` Keith Seitz
  2017-06-02 12:30 ` [PATCH 20/40] Eliminate block_iter_name_* Pedro Alves
                   ` (9 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:30 UTC (permalink / raw)
  To: gdb-patches

While working on C++ support for wild matching, I noticed that
attaching to my system's Firefox (which uses .gdb_index), setting a
break at main and bailing, like:

  $ gdb --batch -q -p `pidof firefox` -ex "b main"

would get substancially slower.  It'd take around 20s when currently
it takes 3s.

The problem is that gdb would expand more symtabs than currently,
because Firefox has symbols named like "nsHtml5Atoms::main",
"nsGkAtoms::main", etc., which given wild matching, match.

However, these are not function symbols, [they're "(nsIAtom *)"], so
it seems silly that they'd cause expansion in the first place.

The .gdb_index code (dwarf2read.c:dw2_expand_marked_cus) filters out
symbols matches based on search_domain:

  case VARIABLES_DOMAIN:
    if (symbol_kind != GDB_INDEX_SYMBOL_KIND_VARIABLE)
      continue;
    break;
  case FUNCTIONS_DOMAIN:
    if (symbol_kind != GDB_INDEX_SYMBOL_KIND_FUNCTION)
      continue;
    break;
  case TYPES_DOMAIN:
    if (symbol_kind != GDB_INDEX_SYMBOL_KIND_TYPE)
      continue;
    break;
  default:
    break;

however, we're currently passing down search_domain::ALL_DOMAIN when
we know we're looking for functions, for no good reason.  This patch
fixes that.

It seems like search_domain is underutilized throughout, but I'll
leave using it more for another pass.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* linespec.c (iterate_over_all_matching_symtabs): Add
	search_domain parameter.  Pass it down to expand_symtabs_matching.
	(decode_objc): Request FUNCTIONS_DOMAIN symbols only.
	(lookup_prefix_sym): Adjust by passing ALL_DOMAIN as
	search_domain.
	(add_all_symbol_names_from_pspace): Add search_domain parameter.
	Pass it down.
	(find_method, find_function_symbols): Request FUNCTIONS_DOMAIN
	symbols.
	(add_matching_symbols_to_info): Add search_domain parameter.  Pass
	it down.
---
 gdb/linespec.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/gdb/linespec.c b/gdb/linespec.c
index fa07534..5d95308 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -363,12 +363,14 @@ static int symbol_to_sal (struct symtab_and_line *result,
 
 static void add_matching_symbols_to_info (const char *name,
 					  symbol_name_match_type name_match_type,
+					  enum search_domain search_domain,
 					  struct collect_info *info,
 					  struct program_space *pspace);
 
 static void add_all_symbol_names_from_pspace (struct collect_info *info,
 					      struct program_space *pspace,
-					      VEC (const_char_ptr) *names);
+					      VEC (const_char_ptr) *names,
+					      enum search_domain search_domain);
 
 static VEC (symtab_ptr) *
   collect_symtabs_from_filename (const char *file,
@@ -1107,6 +1109,7 @@ iterate_over_all_matching_symtabs
   (struct linespec_state *state,
    const lookup_name_info &lookup_name,
    const domain_enum name_domain,
+   enum search_domain search_domain,
    struct program_space *search_pspace, bool include_inline,
    gdb::function_view<symbol_found_callback_ftype> callback)
 {
@@ -1131,7 +1134,7 @@ iterate_over_all_matching_symtabs
 						  NULL,
 						  lookup_name,
 						  NULL, NULL,
-						  ALL_DOMAIN);
+						  search_domain);
 
       ALL_OBJFILE_COMPUNITS (objfile, cu)
 	{
@@ -3461,7 +3464,8 @@ decode_objc (struct linespec_state *self, linespec_p ls, const char *arg)
       return values;
     }
 
-  add_all_symbol_names_from_pspace (&info, NULL, symbol_names);
+  add_all_symbol_names_from_pspace (&info, NULL, symbol_names,
+				    FUNCTIONS_DOMAIN);
 
   if (!VEC_empty (symbolp, info.result.symbols)
       || !VEC_empty (bound_minimal_symbol_d, info.result.minimal_symbols))
@@ -3588,11 +3592,11 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
       if (elt == NULL)
 	{
 	  iterate_over_all_matching_symtabs (state, lookup_name,
-					     STRUCT_DOMAIN, NULL, false,
-					     collector);
+					     STRUCT_DOMAIN, ALL_DOMAIN,
+					     NULL, false, collector);
 	  iterate_over_all_matching_symtabs (state, lookup_name,
-					     VAR_DOMAIN, NULL, false,
-					     collector);
+					     VAR_DOMAIN, ALL_DOMAIN,
+					     NULL, false, collector);
 	}
       else
 	{
@@ -3676,7 +3680,8 @@ compare_msymbols (const void *a, const void *b)
 static void
 add_all_symbol_names_from_pspace (struct collect_info *info,
 				  struct program_space *pspace,
-				  VEC (const_char_ptr) *names)
+				  VEC (const_char_ptr) *names,
+				  enum search_domain search_domain)
 {
   int ix;
   const char *iter;
@@ -3684,7 +3689,7 @@ add_all_symbol_names_from_pspace (struct collect_info *info,
   for (ix = 0; VEC_iterate (const_char_ptr, names, ix, iter); ++ix)
     add_matching_symbols_to_info (iter,
 				  symbol_name_match_type::FULL,
-				  info, pspace);
+				  search_domain, info, pspace);
 }
 
 static void
@@ -3791,7 +3796,8 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs,
 	  /* We have a list of candidate symbol names, so now we
 	     iterate over the symbol tables looking for all
 	     matches in this pspace.  */
-	  add_all_symbol_names_from_pspace (&info, pspace, result_names);
+	  add_all_symbol_names_from_pspace (&info, pspace, result_names,
+					    FUNCTIONS_DOMAIN);
 
 	  VEC_truncate (typep, superclass_vec, 0);
 	  last_result_len = VEC_length (const_char_ptr, result_names);
@@ -3951,9 +3957,10 @@ find_function_symbols (struct linespec_state *state,
   find_imps (name, &symbol_names);
   if (!VEC_empty (const_char_ptr, symbol_names))
     add_all_symbol_names_from_pspace (&info, state->search_pspace,
-				      symbol_names);
+				      symbol_names, FUNCTIONS_DOMAIN);
   else
     add_matching_symbols_to_info (name, symbol_name_match_type::WILD,
+				  FUNCTIONS_DOMAIN,
 				  &info, state->search_pspace);
 
   do_cleanups (cleanup);
@@ -4550,6 +4557,7 @@ search_minsyms_for_name (struct collect_info *info,
 static void
 add_matching_symbols_to_info (const char *name,
 			      symbol_name_match_type name_match_type,
+			      enum search_domain search_domain,
 			      struct collect_info *info,
 			      struct program_space *pspace)
 {
@@ -4563,7 +4571,7 @@ add_matching_symbols_to_info (const char *name,
       if (elt == NULL)
 	{
 	  iterate_over_all_matching_symtabs (info->state, lookup_name,
-					     VAR_DOMAIN,
+					     VAR_DOMAIN, search_domain,
 					     pspace, true, [&] (symbol *sym)
 	    { return info->add_symbol (sym); });
 	  search_minsyms_for_name (info, lookup_name, pspace, NULL);
-- 
2.5.5

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

* [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (31 preceding siblings ...)
  2017-06-02 12:30 ` [PATCH 20/40] Eliminate block_iter_name_* Pedro Alves
@ 2017-06-02 12:30 ` Pedro Alves
  2017-08-08 23:48   ` Keith Seitz
  2017-06-02 12:31 ` [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache) Pedro Alves
                   ` (7 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:30 UTC (permalink / raw)
  To: gdb-patches

Finally, this patch teaches GDB about setting breakpoints ignoring
namespaces, etc. by default.

Here's a contrived example:

  (gdb) b func<tab>
  (anonymous namespace)::A::function()            Bn::(anonymous namespace)::B::function()        function(int, int)
  (anonymous namespace)::B::function()            Bn::(anonymous namespace)::function()           gdb::(anonymous namespace)::A::function()
  (anonymous namespace)::B::function() const      Bn::(anonymous namespace)::function(int, int)   gdb::(anonymous namespace)::function()
  (anonymous namespace)::function()               Bn::B::func()                                   gdb::(anonymous namespace)::function(int, int)
  (anonymous namespace)::function(int, int)       Bn::B::function()                               gdb::A::func()
  A::func()                                       Bn::func()                                      gdb::A::function()
  A::function()                                   Bn::function()                                  gdb::func()
  B::func()                                       Bn::function(int, int)                          gdb::function()
  B::function()                                   Bn::function(long)                              gdb::function(int, int)
  B::function() const                             func()                                          gdb::function(long)
  B::function_const() const                       function()
  (gdb) b function
  Breakpoint 1 at 0x4005ce: function. (26 locations)

  (gdb) b B::function<tab>
  (anonymous namespace)::B::function()        B::function() const                         Bn::B::function()
  (anonymous namespace)::B::function() const  B::function_const() const
  B::function()                               Bn::(anonymous namespace)::B::function()
  (gdb) b B::function
  Breakpoint 1 at 0x40072c: B::function. (6 locations)

Notice that the symbols that the completer finds matches the number of
symbols that settting a breakpoint finds.  The testsuite patch will
add extensive and comprehensive tests to make sure of that in many
cases.

To get back the original behavior of interpreting the function name as
a fully-qualified name, you use the new "-qualified" option.  For
example:

 (gdb) b B::function
 (anonymous namespace)::B::function()        B::function() const                         Bn::B::function()
 (anonymous namespace)::B::function() const  B::function_const() const
 B::function()                               Bn::(anonymous namespace)::B::function()

vs:

 (gdb) b -qualified B::function
 B::function()              B::function() const        B::function_const() const

I've chosen "qualified" because "f" is already taken, for "-function".

To better understand how this is the better default, consider what
happens when we get to the point when _all_ of GDB is wrapped under
"namespace gdb {}".  I have a patch series that does that, and when I
started debugging that GDB, I immediately became frustrated.  You now
have to write "b gdb::internal_error", "b gdb::foo", "b gdb::bar",
etc. etc., which gets annoying pretty quickly.  OTOH, consider how
this makes it very easy to set breakpoints in classes wrapped in
anonymous namespaces.  You just don't think of them, GDB finds the
symbols for you automatically.

Implementation-wise, what the patch does is:

  - make C++ symbol name hashing only consider the last component of a
    symbol name.

  - add a symbol name matcher for symbol_name_match_type::WILD.

  - add unit tests.

  - adjust a few tests to use "-qualified" when they mean it.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* breakpoint.c (LOCATION_HELP_STRING): Mention "-qualified".
	* c-lang.c (cplus_language_defn): Install cp_search_name_hash.
	* completer.c (explicit_location_match_type::MATCH_QUALIFIED): New
	enumerator.
	(explicit_options): Add "-qualified".
	(collect_explicit_location_matches): Handle MATCH_QUALIFIED.
	* cp-support.c (cp_search_name_hash, cp_symbol_name_matches_1)
	(cp_symbol_name_matches): New functions.
	(cp_get_symbol_name_matcher): Return different matchers depending
	on the lookup name's match type.
	(selftests::test_cp_symbol_name_cmp): New function.
	(_initialize_cp_support): Register it.
	* cp-support.h (cp_search_name_hash): New declaration.
	* dwarf2read.c
	(selftests::dw2_expand_symtabs_matching::test_symbols): Add
	symbols.
	(selftests::dw2_expand_symtabs_matching::run_test): Add wild
	matching tests.
	* linespec.c (linespec_parse_basic): Lookup function symbols using
	symbol_name_match_type::WILD.
	(convert_explicit_location_to_linespec): New
	symbol_name_match_type parameter.  Pass it down to
	find_linespec_symbols.
	(convert_explicit_location_to_sals): Pass the location's name
	match type to convert_explicit_location_to_linespec.
	(linespec_complete_function): New symbol_name_match_type
	parameter.  Use it.
	(complete_linespec_component): Complete symbols using
	symbol_name_match_type::WILD.
	(linespec_complete_label): New symbol_name_match_type parameter.
	Use it.
	(linespec_complete): Complete symbols using
	symbol_name_match_type::WILD.
	(find_function_symbols, find_linespec_symbols): New
	symbol_name_match_type parameter.  Pass it down.
	* linespec.h (linespec_complete_function)
	(linespec_complete_label): New symbol_name_match_type parameter.
	* location.c (explicit_to_string_internal)
	(string_to_explicit_location): Handle "-qualified".
	* location.h (explicit_location::func_name_match_type): New field.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/langs.exp: Use -qualified.
	* gdb.cp/meth-typedefs.exp: Use -qualified, and add tests without
	it.
	* gdb.cp/namespace.exp: Use -qualified.
---
 gdb/breakpoint.c                       |   5 +-
 gdb/c-lang.c                           |   2 +-
 gdb/completer.c                        |   7 +
 gdb/cp-support.c                       | 226 ++++++++++++++++++++++++++++++++-
 gdb/cp-support.h                       |   2 +
 gdb/dwarf2read.c                       |  48 +++++++
 gdb/linespec.c                         |  24 +++-
 gdb/linespec.h                         |   9 +-
 gdb/location.c                         |  18 ++-
 gdb/location.h                         |   3 +
 gdb/testsuite/gdb.base/langs.exp       |   2 +-
 gdb/testsuite/gdb.cp/meth-typedefs.exp |  34 ++++-
 gdb/testsuite/gdb.cp/namespace.exp     |   2 +-
 13 files changed, 359 insertions(+), 23 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 70c0e02..328b6db 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -15694,7 +15694,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
 syntax to specify location parameters.\n\
 Example: To specify the start of the label named \"the_top\" in the\n\
 function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
--function fact -label the_top\".\n"
+-function fact -label the_top\".\n\
+For C++, \"-function\" matches all functions with the given name ignoring\n\
+missing leading specifiers (namespaces and classes).  You can override\n\
+that by instead specifying a fully qualified name using \"-qualified\".\n"
 
 /* This help string is used for the break, hbreak, tbreak and thbreak
    commands.  It is defined as a macro to prevent duplication.
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 49077c7..8d96f94 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -1016,7 +1016,7 @@ extern const struct language_defn cplus_language_defn =
   c_watch_location_expression,
   cp_get_symbol_name_matcher,
   iterate_over_symbols,
-  default_search_name_hash,
+  cp_search_name_hash,
   &cplus_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/completer.c b/gdb/completer.c
index eabbce7..99e40a3 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -76,6 +76,9 @@ enum explicit_location_match_type
     /* The name of a function or method.  */
     MATCH_FUNCTION,
 
+    /* The fully-qualified name of a function or method.  */
+    MATCH_QUALIFIED,
+
     /* A line number.  */
     MATCH_LINE,
 
@@ -609,6 +612,7 @@ static const char *const explicit_options[] =
   {
     "-source",
     "-function",
+    "-qualified",
     "-line",
     "-label",
     NULL
@@ -660,9 +664,11 @@ collect_explicit_location_matches (completion_tracker &tracker,
       break;
 
     case MATCH_FUNCTION:
+    case MATCH_QUALIFIED:
       {
 	const char *function = string_or_empty (explicit_loc->function_name);
 	linespec_complete_function (tracker, function,
+				    explicit_loc->func_name_match_type,
 				    explicit_loc->source_filename);
       }
       break;
@@ -677,6 +683,7 @@ collect_explicit_location_matches (completion_tracker &tracker,
 	linespec_complete_label (tracker, language,
 				 explicit_loc->source_filename,
 				 explicit_loc->function_name,
+				 explicit_loc->func_name_match_type,
 				 label);
       }
       break;
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 397c738..84d8a6b 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1642,7 +1642,96 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
   return *demangled != NULL;
 }
 
-/* C++ symbol_name_matcher_ftype implementation.  */
+/* Produce an unsigned hash value from SEARCH_NAME that is compatible
+   with cp_symbol_name_matches.  Only the last component in
+   "foo::bar::function()" is considered for hashing purposes (i.e.,
+   the entire prefix is skipped), so that later on looking up for
+   "function" or "bar::function" in all namespaces is possible.  */
+
+unsigned int
+cp_search_name_hash (const char *search_name)
+{
+  /* cp_entire_prefix_len assumes a fully-qualified name with no
+     leading "::".  */
+  if (startswith (search_name, "::"))
+    search_name += 2;
+
+  unsigned int prefix_len = cp_entire_prefix_len (search_name);
+  if (prefix_len != 0)
+    search_name += prefix_len + 2;
+
+  return default_search_name_hash (search_name);
+}
+
+/* Helper for cp_symbol_name_matches (i.e., symbol_name_matcher_ftype
+   implementation for symbol_name_match_type::WILD matching).  Split
+   to a separate function for unit-testing convenience.
+
+   If SYMBOL_SEARCH_NAME has more scopes than LOOKUP_NAME, we try to
+   match ignoring the extra leading scopes of SYMBOL_SEARCH_NAME.
+   This allows conveniently setting breakpoints on functions/methods
+   inside any namespace/class without specifying the fully-qualified
+   name.
+
+   E.g., these match:
+
+    [symbol search name]   [lookup name]
+    foo::bar::func         foo::bar::func
+    foo::bar::func         bar::func
+    foo::bar::func         func
+
+   While these don't:
+
+    [symbol search name]   [lookup name]
+    foo::zbar::func        bar::func
+    foo::bar::func         foo::func
+
+   See more examples in the test_cp_symbol_name_matches selftest
+   function below.
+
+   See symbol_name_matcher_ftype for description of SYMBOL_SEARCH_NAME
+   and COMP_MATCH_RES.  .
+
+   LOOKUP_NAME/LOOKUP_NAME_LEN is the name we're looking up.
+
+   See strncmp_iw_with_mode for description of MODE.
+*/
+
+static bool
+cp_symbol_name_matches_1 (const char *symbol_search_name,
+			  const char *lookup_name,
+			  size_t lookup_name_len,
+			  strncmp_iw_mode mode,
+			  completion_match_result *comp_match_res)
+{
+  const char *sname = symbol_search_name;
+
+  while (true)
+    {
+      if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len,
+				mode) == 0)
+	{
+	  if (comp_match_res != NULL)
+	    {
+	      comp_match_res->match.set_match (symbol_search_name);
+	      comp_match_res->match_for_lcd.set_match (sname);
+	    }
+	  return true;
+	}
+
+      unsigned int len = cp_find_first_component (sname);
+
+      if (sname[len] == '\0')
+	return false;
+
+      gdb_assert (sname[len] == ':');
+      /* Skip the '::'.  */
+      sname += len + 2;
+    }
+}
+
+/* C++ symbol_name_matcher_ftype implementation for fully qualified
+   matches.  */
 
 static bool
 cp_fq_symbol_name_matches (const char *symbol_search_name,
@@ -1671,19 +1760,151 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
   return false;
 }
 
+/* C++ symbol_name_matcher_ftype implementation for wild matches.
+   Defers work to cp_symbol_name_ncmp.  */
+
+static bool
+cp_symbol_name_matches (const char *symbol_search_name,
+			const lookup_name_info &lookup_name,
+			completion_match_result *comp_match_res)
+{
+  const std::string &name = lookup_name.cplus ().lookup_name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  return cp_symbol_name_matches_1 (symbol_search_name,
+				   name.c_str (), name.size (),
+				   mode, comp_match_res);
+}
+
 /* Implement the "la_get_symbol_name_matcher" language_defn method for
    C++.  */
 
 symbol_name_matcher_ftype *
 cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
 {
-  return cp_fq_symbol_name_matches;
+  switch (lookup_name.match_type ())
+    {
+    case symbol_name_match_type::FULL:
+    case symbol_name_match_type::EXPRESSION:
+      return cp_fq_symbol_name_matches;
+    case symbol_name_match_type::WILD:
+      return cp_symbol_name_matches;
+    }
+
+  gdb_assert_not_reached ("");
 }
 
 #if GDB_SELF_TEST
 
 namespace selftests {
 
+void
+test_cp_symbol_name_cmp ()
+{
+#define CHECK_MATCH(SYMBOL, INPUT)					\
+  SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL,				\
+					INPUT, sizeof (INPUT) - 1,	\
+					strncmp_iw_mode::MATCH_PARAMS,	\
+					NULL))
+
+#define CHECK_NOT_MATCH(SYMBOL, INPUT)					\
+  SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL,			\
+					 INPUT, sizeof (INPUT) - 1,	\
+					 strncmp_iw_mode::MATCH_PARAMS,	\
+					 NULL))
+
+  /* Like CHECK_MATCH, and also check that INPUT (and all substrings
+     that start at index 0) completes to SYMBOL.  */
+#define CHECK_MATCH_C(SYMBOL, INPUT)					\
+  CHECK_MATCH (SYMBOL, INPUT);						\
+  for (size_t i = 0; i < sizeof (INPUT) - 1; i++)			\
+    SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL, INPUT, i,		\
+					  strncmp_iw_mode::NORMAL,	\
+					  NULL))
+
+  /* Like CHECK_NOT_MATCH, and also check that INPUT does NOT complete
+     to SYMBOL.  */
+#define CHECK_NOT_MATCH_C(SYMBOL, INPUT)				\
+  CHECK_NOT_MATCH (SYMBOL, INPUT);					\
+  SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL, INPUT,			\
+					 sizeof (INPUT) - 1,	\
+					 strncmp_iw_mode::NORMAL,	\
+					 NULL))
+
+  /* Lookup name without parens matches all overloads.  */
+  CHECK_MATCH_C ("function()", "function");
+  CHECK_MATCH_C ("function(int)", "function");
+
+  /* Check whitespace around parameters is ignored.  */
+  CHECK_MATCH_C ("function()", "function ()");
+  CHECK_MATCH_C ("function ( )", "function()");
+  CHECK_MATCH_C ("function ()", "function( )");
+  CHECK_MATCH_C ("func(int)", "func( int )");
+  CHECK_MATCH_C ("func(int)", "func ( int ) ");
+  CHECK_MATCH_C ("func ( int )", "func( int )");
+  CHECK_MATCH_C ("func ( int )", "func ( int ) ");
+
+  /* Check symbol name prefixes aren't incorrectly matched.  */
+  CHECK_NOT_MATCH ("func", "function");
+  CHECK_NOT_MATCH ("function", "func");
+  CHECK_NOT_MATCH ("function()", "func");
+
+  /* Check that if the lookup name includes parameters, only the right
+     overload matches.  */
+  CHECK_MATCH_C ("function(int)", "function(int)");
+  CHECK_NOT_MATCH_C ("function(int)", "function()");
+
+  /* Tests matching symbols in some scope.  */
+  CHECK_MATCH_C ("foo::function()", "function");
+  CHECK_MATCH_C ("foo::function(int)", "function");
+  CHECK_MATCH_C ("foo::bar::function()", "function");
+  CHECK_MATCH_C ("bar::function()", "bar::function");
+  CHECK_MATCH_C ("foo::bar::function()", "bar::function");
+  CHECK_MATCH_C ("foo::bar::function(int)", "bar::function");
+
+  /* Same, with parameters in the lookup name.  */
+  CHECK_MATCH_C ("foo::function()", "function()");
+  CHECK_MATCH_C ("foo::bar::function()", "function()");
+  CHECK_MATCH_C ("foo::function(int)", "function(int)");
+  CHECK_MATCH_C ("foo::function()", "foo::function()");
+  CHECK_MATCH_C ("foo::bar::function()", "bar::function()");
+  CHECK_MATCH_C ("foo::bar::function(int)", "bar::function(int)");
+  CHECK_NOT_MATCH_C ("foo::bar::function(int)", "bar::function()");
+
+  CHECK_MATCH_C ("(anonymous namespace)::bar::function(int)",
+		 "bar::function(int)");
+  CHECK_MATCH_C ("foo::(anonymous namespace)::bar::function(int)",
+		 "function(int)");
+
+  /* Lookup scope wider than symbol scope, should not match.  */
+  CHECK_NOT_MATCH_C ("function()", "bar::function");
+  CHECK_NOT_MATCH_C ("function()", "bar::function()");
+
+  /* An explicit global scope forces a fully qualified match.  */
+  CHECK_NOT_MATCH_C ("foo::function()", "::function");
+  CHECK_NOT_MATCH_C ("foo::function()", "::function()");
+  CHECK_NOT_MATCH_C ("foo::function(int)", "::function()");
+  CHECK_NOT_MATCH_C ("foo::function(int)", "::function(int)");
+
+  CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi()");
+  CHECK_MATCH_C ("abc::def::ghi ( )", "abc::def::ghi()");
+  CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi ( )");
+  CHECK_MATCH_C ("function()", "function()");
+  CHECK_MATCH_C ("foo::function()", "function()");
+  CHECK_MATCH_C ("foo::bar::function()", "function()");
+  CHECK_MATCH_C ("bar::function()", "bar::function()");
+  CHECK_MATCH_C ("foo::bar::function()", "bar::function");
+  CHECK_MATCH_C ("(anonymous namespace)::bar::function(int)",
+		 "function(int)");
+  CHECK_MATCH_C ("foo::(anonymous namespace)::bar::function(int)",
+		 "function(int)");
+  CHECK_NOT_MATCH_C ("function()", "bar::function");
+  CHECK_NOT_MATCH_C ("foo::function()", "::function");
+}
+
 /* If non-NULL, return STR wrapped in quotes.  Otherwise, return a
    "<null>" string (with no quotes).  */
 
@@ -1890,6 +2111,7 @@ display the offending symbol."),
 #endif
 
 #if GDB_SELF_TEST
+  register_self_test (selftests::test_cp_symbol_name_cmp);
   register_self_test (selftests::test_cp_remove_params);
 #endif
 }
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 1cef3f7..600720f 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -110,6 +110,8 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
 extern struct type *cp_lookup_rtti_type (const char *name,
 					 struct block *block);
 
+extern unsigned int cp_search_name_hash (const char *search_name);
+
 extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
   (const lookup_name_info &lookup_name);
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index a267377..60a909f 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -4548,6 +4548,8 @@ static const char *test_symbols[] = {
   "ns::foo<char*>",
   "ns::foo<int>",
   "ns::foo<long>",
+  "ns2::tmpl<int>::foo2",
+  "(anonymous namespace)::A::B::C",
 
   /* A name with all sorts of complications.  Starts with "z" to make
      it easier for the completion tests below.  */
@@ -4627,6 +4629,8 @@ run_test ()
   {
     CHECK_MATCH ("w", symbol_name_match_type::FULL, true,
 		 EXPECT ("w1::w2"));
+    CHECK_MATCH ("w", symbol_name_match_type::WILD, true,
+		 EXPECT ("w1::w2"));
   }
 
   /* Same, with a "complicated" symbol.  */
@@ -4654,6 +4658,10 @@ run_test ()
   {
     CHECK_MATCH ("std::zfunction(int)", symbol_name_match_type::FULL, true,
 		 EXPECT ("std::zfunction", "std::zfunction2"));
+    CHECK_MATCH ("zfunction(int)", symbol_name_match_type::WILD, true,
+		 EXPECT ("std::zfunction", "std::zfunction2"));
+    CHECK_MATCH ("zfunc", symbol_name_match_type::WILD, true,
+		 EXPECT ("std::zfunction", "std::zfunction2"));
   }
 
   /* Check that whitespace is ignored appropriately.  A symbol with a
@@ -4662,6 +4670,8 @@ run_test ()
     static const char expected[] = "ns::foo<int>";
     CHECK_MATCH ("ns :: foo < int > ", symbol_name_match_type::FULL, false,
 		 EXPECT (expected));
+    CHECK_MATCH ("foo < int > ", symbol_name_match_type::WILD, false,
+		 EXPECT (expected));
   }
 
   /* Check that whitespace is ignored appropriately.  A symbol with a
@@ -4674,9 +4684,13 @@ run_test ()
       {
 	CHECK_MATCH ("ns :: foo < char * >", symbol_name_match_type::FULL,
 		     completion_mode[i], EXPECT (expected));
+	CHECK_MATCH ("foo < char * >", symbol_name_match_type::WILD,
+		     completion_mode[i], EXPECT (expected));
 
 	CHECK_MATCH ("ns :: foo < char * > (int)", symbol_name_match_type::FULL,
 		     completion_mode[i], EXPECT (expected));
+	CHECK_MATCH ("foo < char * > (int)", symbol_name_match_type::WILD,
+		     completion_mode[i], EXPECT (expected));
       }
   }
 
@@ -4687,14 +4701,48 @@ run_test ()
 		 symbol_name_match_type::FULL, true, EXPECT (expected));
     CHECK_MATCH ("ns :: foo < char * >  ( int ) &&",
 		 symbol_name_match_type::FULL, true, EXPECT (expected));
+    CHECK_MATCH ("foo < char * >  ( int ) const",
+		 symbol_name_match_type::WILD, true, EXPECT (expected));
+    CHECK_MATCH ("foo < char * >  ( int ) &&",
+		 symbol_name_match_type::WILD, true, EXPECT (expected));
   }
 
   /* Test lookup names that don't match anything.  */
   {
+    CHECK_MATCH ("bar2", symbol_name_match_type::WILD, false,
+		 {});
+
     CHECK_MATCH ("doesntexist", symbol_name_match_type::FULL, false,
 		 {});
   }
 
+  /* Some wild matching tests, exercising "(anonymous namespace)",
+     which should not be confused with a parameter list.  */
+  {
+    static const char *syms[] = {
+      "A::B::C",
+      "B::C",
+      "C",
+      "A :: B :: C ( int )",
+      "B :: C ( int )",
+      "C ( int )",
+    };
+
+    for (const char *s : syms)
+      {
+	CHECK_MATCH (s, symbol_name_match_type::WILD, false,
+		     EXPECT ("(anonymous namespace)::A::B::C"));
+      }
+  }
+
+  {
+    static const char expected[] = "ns2::tmpl<int>::foo2";
+    CHECK_MATCH ("tmp", symbol_name_match_type::WILD, true,
+		 EXPECT (expected));
+    CHECK_MATCH ("tmpl<", symbol_name_match_type::WILD, true,
+		 EXPECT (expected));
+  }
+
   SELF_CHECK (!any_mismatch);
 
 #undef EXPECT
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 5d95308..d17dcfa 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -351,6 +351,7 @@ static VEC (symbolp) *find_label_symbols (struct linespec_state *self,
 static void find_linespec_symbols (struct linespec_state *self,
 				   VEC (symtab_ptr) *file_symtabs,
 				   const char *name,
+				   symbol_name_match_type name_match_type,
 				   VEC (symbolp) **symbols,
 				   VEC (bound_minimal_symbol_d) **minsyms);
 
@@ -1873,6 +1874,7 @@ linespec_parse_basic (linespec_parser *parser)
 
 	  linespec_complete_function (tmp_tracker,
 				      parser->completion_word,
+				      symbol_name_match_type::WILD,
 				      source_filename);
 
 	  if (tmp_tracker.have_completions ())
@@ -1897,6 +1899,7 @@ linespec_parse_basic (linespec_parser *parser)
   /* Try looking it up as a function/method.  */
   find_linespec_symbols (PARSER_STATE (parser),
 			 PARSER_RESULT (parser)->file_symtabs, name,
+			 symbol_name_match_type::WILD,
 			 &symbols, &minimal_symbols);
 
   if (symbols != NULL || minimal_symbols != NULL)
@@ -2405,6 +2408,7 @@ convert_explicit_location_to_linespec (struct linespec_state *self,
 				       linespec_p result,
 				       const char *source_filename,
 				       const char *function_name,
+				       symbol_name_match_type fname_match_type,
 				       const char *label_name,
 				       struct line_offset line_offset)
 {
@@ -2434,8 +2438,8 @@ convert_explicit_location_to_linespec (struct linespec_state *self,
   if (function_name != NULL)
     {
       find_linespec_symbols (self, result->file_symtabs,
-			     function_name, &symbols,
-			     &minimal_symbols);
+			     function_name, fname_match_type,
+			     &symbols, &minimal_symbols);
 
       if (symbols == NULL && minimal_symbols == NULL)
 	symbol_not_found_error (function_name,
@@ -2475,6 +2479,7 @@ convert_explicit_location_to_sals (struct linespec_state *self,
   convert_explicit_location_to_linespec (self, result,
 					 explicit_loc->source_filename,
 					 explicit_loc->function_name,
+					 explicit_loc->func_name_match_type,
 					 explicit_loc->label_name,
 					 explicit_loc->line_offset);
   return convert_linespec_to_sals (self, result);
@@ -2853,10 +2858,10 @@ linespec_lex_to_end (char **stringp)
 void
 linespec_complete_function (completion_tracker &tracker,
 			    const char *function,
+			    symbol_name_match_type func_match_type,
 			    const char *source_filename)
 {
   complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
-  symbol_name_match_type func_match_type = symbol_name_match_type::WILD;
 
   if (source_filename != NULL)
     {
@@ -2895,7 +2900,8 @@ complete_linespec_component (linespec_parser *parser,
     {
       completion_list fn_list;
 
-      linespec_complete_function (tracker, text, source_filename);
+      linespec_complete_function (tracker, text, symbol_name_match_type::WILD,
+				  source_filename);
       if (source_filename == NULL)
 	fn_list = complete_source_filenames (text);
 
@@ -2960,6 +2966,7 @@ linespec_complete_label (completion_tracker &tracker,
 			 const struct language_defn *language,
 			 const char *source_filename,
 			 const char *function_name,
+			 symbol_name_match_type func_name_match_type,
 			 const char *label_name)
 {
   linespec_parser parser;
@@ -2976,6 +2983,7 @@ linespec_complete_label (completion_tracker &tracker,
 					     PARSER_RESULT (&parser),
 					     source_filename,
 					     function_name,
+					     func_name_match_type,
 					     NULL, unknown_offset);
     }
   CATCH (ex, RETURN_MASK_ERROR)
@@ -3059,7 +3067,7 @@ linespec_complete (completion_tracker &tracker, const char *text)
       VEC (bound_minimal_symbol_d) *minimal_symbols;
       find_linespec_symbols (PARSER_STATE (&parser),
 			     PARSER_RESULT (&parser)->file_symtabs,
-			     func_name,
+			     func_name, symbol_name_match_type::WILD,
 			     &function_symbols, &minimal_symbols);
 
       PARSER_RESULT (&parser)->function_symbols = function_symbols;
@@ -3940,6 +3948,7 @@ symtabs_from_filename (const char *filename,
 static void
 find_function_symbols (struct linespec_state *state,
 		       VEC (symtab_ptr) *file_symtabs, const char *name,
+		       symbol_name_match_type name_match_type,
 		       VEC (symbolp) **symbols,
 		       VEC (bound_minimal_symbol_d) **minsyms)
 {
@@ -3959,8 +3968,7 @@ find_function_symbols (struct linespec_state *state,
     add_all_symbol_names_from_pspace (&info, state->search_pspace,
 				      symbol_names, FUNCTIONS_DOMAIN);
   else
-    add_matching_symbols_to_info (name, symbol_name_match_type::WILD,
-				  FUNCTIONS_DOMAIN,
+    add_matching_symbols_to_info (name, name_match_type, FUNCTIONS_DOMAIN,
 				  &info, state->search_pspace);
 
   do_cleanups (cleanup);
@@ -3989,6 +3997,7 @@ static void
 find_linespec_symbols (struct linespec_state *state,
 		       VEC (symtab_ptr) *file_symtabs,
 		       const char *lookup_name,
+		       symbol_name_match_type name_match_type,
 		       VEC (symbolp) **symbols,
 		       VEC (bound_minimal_symbol_d) **minsyms)
 {
@@ -4006,6 +4015,7 @@ find_linespec_symbols (struct linespec_state *state,
      2) break class::method where method is in class (and not a baseclass)  */
 
   find_function_symbols (state, file_symtabs, lookup_name,
+			 name_match_type,
 			 symbols, minsyms);
 
   /* If we were unable to locate a symbol of the same name, try dividing
diff --git a/gdb/linespec.h b/gdb/linespec.h
index 27d237a..8759173 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -191,12 +191,14 @@ extern const char * const linespec_keywords[];
 extern void linespec_complete (completion_tracker &tracker,
 			       const char *text);
 
-/* Complete a function symbol, in linespec mode.  If SOURCE_FILENAME
-   is non-NULL, limits completion to the list of functions defined in
-   source files that match SOURCE_FILENAME.  */
+/* Complete a function symbol, in linespec mode, according to
+   FUNC_MATCH_TYPE.  If SOURCE_FILENAME is non-NULL, limits completion
+   to the list of functions defined in source files that match
+   SOURCE_FILENAME.  */
 
 extern void linespec_complete_function (completion_tracker &tracker,
 					const char *function,
+					symbol_name_match_type func_match_type,
 					const char *source_filename);
 
 /* Complete a label symbol, in linespec mode.  Only labels of
@@ -208,6 +210,7 @@ extern void linespec_complete_label (completion_tracker &tracker,
 				     const struct language_defn *language,
 				     const char *source_filename,
 				     const char *function_name,
+				     symbol_name_match_type name_match_type,
 				     const char *label_name);
 
 /* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
diff --git a/gdb/location.c b/gdb/location.c
index 19d3232..0a9978a 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -245,7 +245,13 @@ explicit_to_string_internal (int as_linespec,
       if (need_space)
 	buf.putc (space);
       if (!as_linespec)
-	buf.puts ("-function ");
+	{
+	  if (explicit_loc->func_name_match_type
+	      == symbol_name_match_type::FULL)
+	    buf.puts ("-qualified ");
+	  else
+	    buf.puts ("-function ");
+	}
       buf.puts (explicit_loc->function_name);
       need_space = 1;
     }
@@ -775,6 +781,16 @@ string_to_explicit_location (const char **argp,
 	  set_oarg (explicit_location_lex_one_function (argp, language,
 							completion_info));
 	  EL_EXPLICIT (location)->function_name = oarg.release ();
+	  EL_EXPLICIT (location)->func_name_match_type
+	    = symbol_name_match_type::WILD;
+	}
+      else if (strncmp (opt.get (), "-qualified", len) == 0)
+	{
+	  set_oarg (explicit_location_lex_one_function (argp, language,
+							completion_info));
+	  EL_EXPLICIT (location)->function_name = oarg.release ();
+	  EL_EXPLICIT (location)->func_name_match_type
+	    = symbol_name_match_type::FULL;
 	}
       else if (strncmp (opt.get (), "-line", len) == 0)
 	{
diff --git a/gdb/location.h b/gdb/location.h
index 58536e0..0200d42 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -79,6 +79,9 @@ struct explicit_location
   /* The function name.  Malloc'd.  */
   char *function_name;
 
+  /* Whether the function name is fully-qualified or not.  */
+  symbol_name_match_type func_name_match_type;
+
   /* The name of a label.  Malloc'd.  */
   char *label_name;
 
diff --git a/gdb/testsuite/gdb.base/langs.exp b/gdb/testsuite/gdb.base/langs.exp
index 8dcd5ee..03c690c 100644
--- a/gdb/testsuite/gdb.base/langs.exp
+++ b/gdb/testsuite/gdb.base/langs.exp
@@ -38,7 +38,7 @@ if [get_compiler_info] {
     return -1
 }
 
-gdb_test_multiple "b langs0" "break on nonexistent function in langs.exp" {
+gdb_test_multiple "b -qualified langs0" "break on nonexistent function in langs.exp" {
 	-re "Function \"langs0\" not defined\..*Make breakpoint pending on future shared library load.*y or .n.. $" {
 
 		gdb_test "n" "" "break on nonexistent function in langs.exp"
diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp
index 08f1464..9234fe1 100644
--- a/gdb/testsuite/gdb.cp/meth-typedefs.exp
+++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp
@@ -145,15 +145,37 @@ foreach test $methods {
     set func [lindex $test 0]
     set result [lindex $test 1]
 
-    gdb_test "list $func" $result
-    gdb_test "list '$func'" $result
-    if {[gdb_breakpoint $func]} {
-      pass "break $func"
+    gdb_test "list -qualified $func" $result
+    gdb_test "list -qualified '$func'" $result
+    if {[gdb_breakpoint "-qualified $func"]} {
+      pass "break -qualified $func"
     }
-    if {[gdb_breakpoint '$func']} {
-      pass "break '$func'"
+    if {[gdb_breakpoint "-qualified '$func'"]} {
+      pass "break -qualified '$func'"
     }
 }
 
+# The tests above use -qualified to explicitly pick the one "test"
+# symbol each test cares about.  Now check that both "break test(..)"
+# and "list test(..)" without -qualified find "test(..)" in all the 3
+# scopes that have the this particular overload.
+set func "test(aenum, astruct const&, aunion const***)"
+set line1 [gdb_get_line_number " A::FOO::$func"]
+set line2 [gdb_get_line_number " B::$func"]
+set line3 [gdb_get_line_number " $func"]
+
+foreach f [list "$func" "'$func'"] {
+    gdb_test \
+	"list $f" \
+	[multi_line \
+	     "file: \".*$srcfile\", line number: $line1" \
+	     "file: \".*$srcfile\", line number: $line2" \
+	     "file: \".*$srcfile\", line number: $line3"] \
+	"list $f"
+
+    delete_breakpoints
+    gdb_test "break $f" "\\(3 locations\\)"
+}
+
 gdb_exit
 return 0
diff --git a/gdb/testsuite/gdb.cp/namespace.exp b/gdb/testsuite/gdb.cp/namespace.exp
index 640ee4f..4a6b863 100644
--- a/gdb/testsuite/gdb.cp/namespace.exp
+++ b/gdb/testsuite/gdb.cp/namespace.exp
@@ -120,7 +120,7 @@ gdb_test "break AAA::xyzq" \
 
 # Break on a function in the global namespace.
 
-gdb_test "break ::ensureOtherRefs" \
+gdb_test "break -qualified ::ensureOtherRefs" \
     "Breakpoint.*at $hex: file.*$srcfile2, line $decimal\\."
 
 # Call a function in a nested namespace
-- 
2.5.5

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

* [PATCH 20/40] Eliminate block_iter_name_*
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (30 preceding siblings ...)
  2017-06-02 12:30 ` [PATCH 30/40] Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints Pedro Alves
@ 2017-06-02 12:30 ` Pedro Alves
  2017-07-17 19:47   ` Keith Seitz
  2017-06-02 12:30 ` [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching] Pedro Alves
                   ` (8 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:30 UTC (permalink / raw)
  To: gdb-patches

This patch gets rid of block_iter_name_* as being unnecessary.  It's
the same as calling block_iter_match_*, and passing strcmp_iw as
comparison routine.

(A later patch will get rid of those new explicit strcmp_iw calls.)

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* block.c (block_iter_name_step, block_iter_name_first)
	(block_iter_name_next): Delete.
	(block_lookup_symbol_primary): Adjust to use
	dict_iter_match_first/dict_iter_match_next.
	* block.h (block_iter_name_first, block_iter_name_next): Delete
	declarations.
	(ALL_BLOCK_SYMBOLS_WITH_NAME): Adjust to use
	dict_iter_match_first/dict_iter_match_next.
---
 gdb/block.c      | 73 ++------------------------------------------------------
 gdb/block.h      | 24 +++----------------
 gdb/dictionary.c | 14 -----------
 gdb/dictionary.h | 19 ---------------
 4 files changed, 5 insertions(+), 125 deletions(-)

diff --git a/gdb/block.c b/gdb/block.c
index 2f44460..1c343aa 100644
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -589,75 +589,6 @@ block_iterator_next (struct block_iterator *iterator)
   return block_iterator_step (iterator, 0);
 }
 
-/* Perform a single step for a "name" block iterator, iterating across
-   symbol tables as needed.  Returns the next symbol, or NULL when
-   iteration is complete.  */
-
-static struct symbol *
-block_iter_name_step (struct block_iterator *iterator, const char *name,
-		      int first)
-{
-  struct symbol *sym;
-
-  gdb_assert (iterator->which != FIRST_LOCAL_BLOCK);
-
-  while (1)
-    {
-      if (first)
-	{
-	  struct compunit_symtab *cust
-	    = find_iterator_compunit_symtab (iterator);
-	  const struct block *block;
-
-	  /* Iteration is complete.  */
-	  if (cust == NULL)
-	    return  NULL;
-
-	  block = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cust),
-				     iterator->which);
-	  sym = dict_iter_name_first (BLOCK_DICT (block), name,
-				      &iterator->dict_iter);
-	}
-      else
-	sym = dict_iter_name_next (name, &iterator->dict_iter);
-
-      if (sym != NULL)
-	return sym;
-
-      /* We have finished iterating the appropriate block of one
-	 symtab.  Now advance to the next symtab and begin iteration
-	 there.  */
-      ++iterator->idx;
-      first = 1;
-    }
-}
-
-/* See block.h.  */
-
-struct symbol *
-block_iter_name_first (const struct block *block,
-		       const char *name,
-		       struct block_iterator *iterator)
-{
-  initialize_block_iterator (block, iterator);
-
-  if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_name_first (block->dict, name, &iterator->dict_iter);
-
-  return block_iter_name_step (iterator, name, 1);
-}
-
-/* See block.h.  */
-
-struct symbol *
-block_iter_name_next (const char *name, struct block_iterator *iterator)
-{
-  if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_name_next (name, &iterator->dict_iter);
-
-  return block_iter_name_step (iterator, name, 0);
-}
-
 /* Perform a single step for a "match" block iterator, iterating
    across symbol tables as needed.  Returns the next symbol, or NULL
    when iteration is complete.  */
@@ -812,9 +743,9 @@ block_lookup_symbol_primary (const struct block *block, const char *name,
 	      || BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL);
 
   other = NULL;
-  for (sym = dict_iter_name_first (block->dict, name, &dict_iter);
+  for (sym = dict_iter_match_first (block->dict, name, strcmp_iw, &dict_iter);
        sym != NULL;
-       sym = dict_iter_name_next (name, &dict_iter))
+       sym = dict_iter_match_next (name, strcmp_iw, &dict_iter))
     {
       if (SYMBOL_DOMAIN (sym) == domain)
 	return sym;
diff --git a/gdb/block.h b/gdb/block.h
index eeb5ed4..1741e52 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -237,25 +237,6 @@ extern struct symbol *block_iterator_first (const struct block *block,
 extern struct symbol *block_iterator_next (struct block_iterator *iterator);
 
 /* Initialize ITERATOR to point at the first symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME (as tested using strcmp_iw), and return
-   that first symbol, or NULL if there are no such symbols.  */
-
-extern struct symbol *block_iter_name_first (const struct block *block,
-					     const char *name,
-					     struct block_iterator *iterator);
-
-/* Advance ITERATOR to point at the next symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME (as tested using strcmp_iw), or NULL if
-   there are no more such symbols.  Don't call this if you've
-   previously received NULL from block_iterator_first or
-   block_iterator_next on this iteration.  And don't call it unless
-   ITERATOR was created by a previous call to block_iter_name_first
-   with the same NAME.  */
-
-extern struct symbol *block_iter_name_next (const char *name,
-					    struct block_iterator *iterator);
-
-/* Initialize ITERATOR to point at the first symbol in BLOCK whose
    SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (which must use
    the same conventions as strcmp_iw and be compatible with any
    block hashing function), and return that first symbol, or NULL
@@ -340,8 +321,9 @@ extern int block_find_non_opaque_type_preferred (struct symbol *sym,
    must be a struct block_iterator.  SYM points to the current symbol.  */
 
 #define ALL_BLOCK_SYMBOLS_WITH_NAME(block, name, iter, sym)		\
-  for ((sym) = block_iter_name_first ((block), (name), &(iter));	\
+  for ((sym) = block_iter_match_first ((block), (name),			\
+				       strcmp_iw, &(iter));		\
        (sym) != NULL;							\
-       (sym) = block_iter_name_next ((name), &(iter)))
+       (sym) = block_iter_match_next ((name), strcmp_iw, &(iter)))
 
 #endif /* BLOCK_H */
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index e78db2e..b2cfca2 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -530,20 +530,6 @@ dict_iterator_next (struct dict_iterator *iterator)
 }
 
 struct symbol *
-dict_iter_name_first (const struct dictionary *dict,
-		      const char *name,
-		      struct dict_iterator *iterator)
-{
-  return dict_iter_match_first (dict, name, strcmp_iw, iterator);
-}
-
-struct symbol *
-dict_iter_name_next (const char *name, struct dict_iterator *iterator)
-{
-  return dict_iter_match_next (name, strcmp_iw, iterator);
-}
-
-struct symbol *
 dict_iter_match_first (const struct dictionary *dict,
 		       const char *name, symbol_compare_ftype *compare,
 		       struct dict_iterator *iterator)
diff --git a/gdb/dictionary.h b/gdb/dictionary.h
index 124fc98..4f4f160 100644
--- a/gdb/dictionary.h
+++ b/gdb/dictionary.h
@@ -123,25 +123,6 @@ extern struct symbol *dict_iterator_first (const struct dictionary *dict,
 extern struct symbol *dict_iterator_next (struct dict_iterator *iterator);
 
 /* Initialize ITERATOR to point at the first symbol in DICT whose
-   SYMBOL_SEARCH_NAME is NAME (as tested using strcmp_iw), and return
-   that first symbol, or NULL if there are no such symbols.  */
-
-extern struct symbol *dict_iter_name_first (const struct dictionary *dict,
-					    const char *name,
-					    struct dict_iterator *iterator);
-
-/* Advance ITERATOR to point at the next symbol in DICT whose
-   SYMBOL_SEARCH_NAME is NAME (as tested using strcmp_iw), or NULL if
-   there are no more such symbols.  Don't call this if you've
-   previously received NULL from dict_iterator_first or
-   dict_iterator_next on this iteration.  And don't call it unless
-   ITERATOR was created by a previous call to dict_iter_name_first
-   with the same NAME.  */
-
-extern struct symbol *dict_iter_name_next (const char *name,
-					   struct dict_iterator *iterator);
-
-/* Initialize ITERATOR to point at the first symbol in DICT whose
    SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (which must use
    the same conventions as strcmp_iw and be compatible with any
    dictionary hashing function), and return that first symbol, or NULL
-- 
2.5.5

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

* [PATCH 22/40] get_int_var_value
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (33 preceding siblings ...)
  2017-06-02 12:31 ` [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache) Pedro Alves
@ 2017-06-02 12:31 ` Pedro Alves
  2017-07-17 22:11   ` Keith Seitz
  2017-06-02 12:31 ` [PATCH 29/40] Simplify completion_list_add_name | remove sym_text / sym_text_len Pedro Alves
                   ` (5 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:31 UTC (permalink / raw)
  To: gdb-patches

I noticed that get_int_var_value's parameters could use some
constification.  And then realized that client code would become
simpler by changing the interface to return the success/failure
indication as actual return value, as allows getting rid of the the
local "boolean" variable.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_to_fixed_type_1): Adjust.
	(get_var_value): Constify parameters.
	(get_int_var_value): Change prototype.
	(to_fixed_range_type): Adjust.
	* ada-lang.h (get_int_var_value): Change prototype.
---
 gdb/ada-lang.c      | 44 ++++++++++++++------------------------------
 gdb/ada-lang.h      |  2 +-
 gdb/ada-typeprint.c |  4 +---
 3 files changed, 16 insertions(+), 34 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index ab54c64..c5f7f8c 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -193,8 +193,6 @@ static void move_bits (gdb_byte *, int, const gdb_byte *, int, int, int);
 static struct value *coerce_unspec_val_to_type (struct value *,
                                                 struct type *);
 
-static struct value *get_var_value (char *, char *);
-
 static int lesseq_defined_than (struct symbol *, struct symbol *);
 
 static int equiv_types (struct type *, struct type *);
@@ -8989,12 +8987,11 @@ ada_to_fixed_type_1 (struct type *type, const gdb_byte *valaddr,
             const char *name = ada_type_name (fixed_record_type);
             char *xvz_name
 	      = (char *) alloca (strlen (name) + 7 /* "___XVZ\0" */);
-            int xvz_found = 0;
             LONGEST size;
 
             xsnprintf (xvz_name, strlen (name) + 7, "%s___XVZ", name);
-            size = get_int_var_value (xvz_name, &xvz_found);
-            if (xvz_found && TYPE_LENGTH (fixed_record_type) != size)
+            if (get_int_var_value (xvz_name, size)
+		&& TYPE_LENGTH (fixed_record_type) != size)
               {
                 fixed_record_type = copy_type (fixed_record_type);
                 TYPE_LENGTH (fixed_record_type) = size;
@@ -11614,7 +11611,7 @@ scan_discrim_bound (const char *str, int k, struct value *dval, LONGEST * px,
    otherwise causes an error with message ERR_MSG.  */
 
 static struct value *
-get_var_value (char *name, char *err_msg)
+get_var_value (const char *name, const char *err_msg)
 {
   struct block_symbol *syms;
   int nsyms;
@@ -11633,27 +11630,20 @@ get_var_value (char *name, char *err_msg)
   return value_of_variable (syms[0].symbol, syms[0].block);
 }
 
-/* Value of integer variable named NAME in the current environment.  If
-   no such variable found, returns 0, and sets *FLAG to 0.  If
-   successful, sets *FLAG to 1.  */
+/* Value of integer variable named NAME in the current environment.
+   If no such variable is found, returns false.  Otherwise, sets VALUE
+   to the variable's value and returns true.  */
 
-LONGEST
-get_int_var_value (char *name, int *flag)
+bool
+get_int_var_value (const char *name, LONGEST &value)
 {
   struct value *var_val = get_var_value (name, 0);
 
   if (var_val == 0)
-    {
-      if (flag != NULL)
-        *flag = 0;
-      return 0;
-    }
-  else
-    {
-      if (flag != NULL)
-        *flag = 1;
-      return value_as_long (var_val);
-    }
+    return false;
+
+  value = value_as_long (var_val);
+  return true;
 }
 
 
@@ -11725,11 +11715,8 @@ to_fixed_range_type (struct type *raw_type, struct value *dval)
         }
       else
         {
-          int ok;
-
           strcpy (name_buf + prefix_len, "___L");
-          L = get_int_var_value (name_buf, &ok);
-          if (!ok)
+          if (!get_int_var_value (name_buf, L))
             {
               lim_warning (_("Unknown lower bound, using 1."));
               L = 1;
@@ -11744,11 +11731,8 @@ to_fixed_range_type (struct type *raw_type, struct value *dval)
         }
       else
         {
-          int ok;
-
           strcpy (name_buf + prefix_len, "___U");
-          U = get_int_var_value (name_buf, &ok);
-          if (!ok)
+          if (!get_int_var_value (name_buf, U))
             {
               lim_warning (_("Unknown upper bound, using %ld."), (long) L);
               U = L;
diff --git a/gdb/ada-lang.h b/gdb/ada-lang.h
index 5f97a6c..f5b3bca 100644
--- a/gdb/ada-lang.h
+++ b/gdb/ada-lang.h
@@ -335,7 +335,7 @@ extern const char *ada_type_name (struct type *);
 extern struct type *ada_find_parallel_type (struct type *,
                                             const char *suffix);
 
-extern LONGEST get_int_var_value (char *, int *);
+extern bool get_int_var_value (const char *, LONGEST &value);
 
 extern struct symbol *ada_find_renaming_symbol (struct symbol *name_sym,
                                                 const struct block *block);
diff --git a/gdb/ada-typeprint.c b/gdb/ada-typeprint.c
index 3c33bdc..8392513 100644
--- a/gdb/ada-typeprint.c
+++ b/gdb/ada-typeprint.c
@@ -256,14 +256,12 @@ print_dynamic_range_bound (struct type *type, const char *name, int name_len,
   static char *name_buf = NULL;
   static size_t name_buf_len = 0;
   LONGEST B;
-  int OK;
 
   GROW_VECT (name_buf, name_buf_len, name_len + strlen (suffix) + 1);
   strncpy (name_buf, name, name_len);
   strcpy (name_buf + name_len, suffix);
 
-  B = get_int_var_value (name_buf, &OK);
-  if (OK)
+  if (get_int_var_value (name_buf, B))
     ada_print_scalar (type, B, stream);
   else
     fprintf_filtered (stream, "?");
-- 
2.5.5

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

* [PATCH 29/40] Simplify completion_list_add_name | remove sym_text / sym_text_len
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (34 preceding siblings ...)
  2017-06-02 12:31 ` [PATCH 22/40] get_int_var_value Pedro Alves
@ 2017-06-02 12:31 ` Pedro Alves
  2017-08-08 20:59   ` Keith Seitz
  2017-06-02 12:33 ` [PATCH 31/40] Handle custom completion match prefix / LCD Pedro Alves
                   ` (4 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:31 UTC (permalink / raw)
  To: gdb-patches

sym_text_len existed to strip parameters out of the lookup name.  Now
that that's handled by the lookup_name_info objects, the
sym_text/sym_text_len parameters are no longer necessary.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_make_symbol_completion_list): Remove text and
	text_len locals and don't pass them down.
	* symtab.c (completion_list_add_name): Remove
	sym_text/sym_text_len parameters and adjust.
	(completion_list_add_symbol, completion_list_add_msymbol)
	(completion_list_objc_symbol, completion_list_add_fields)
	(add_symtab_completions): Likewise.
	(default_collect_symbol_completion_matches_break_on)
	(collect_file_symbol_completion_matches): Remove sym_text_len
	local and don't pass it down.
	* symtab.h (completion_list_add_name): Remove
	sym_text/sym_text_len parameters.
---
 gdb/ada-lang.c | 18 ++++--------
 gdb/symtab.c   | 90 +++++++++++++++++++---------------------------------------
 gdb/symtab.h   |  1 -
 3 files changed, 34 insertions(+), 75 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 50f7581..11ff719 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6432,7 +6432,6 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 				       const char *text, const char *word,
 				       enum type_code code)
 {
-  int text_len;
   struct symbol *sym;
   struct compunit_symtab *s;
   struct minimal_symbol *msymbol;
@@ -6444,10 +6443,7 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
   gdb_assert (code == TYPE_CODE_UNDEF);
 
-  text_len = strlen (text);
-
-  lookup_name_info lookup_name (std::string (text, text_len),
-				name_match_type, true);
+  lookup_name_info lookup_name (text, name_match_type, true);
 
   /* First, look at the partial symtab symbols.  */
   expand_symtabs_matching (NULL,
@@ -6468,8 +6464,7 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
     completion_list_add_name (tracker,
 			      MSYMBOL_LANGUAGE (msymbol),
 			      MSYMBOL_LINKAGE_NAME (msymbol),
-			      lookup_name,
-			      text, text_len, text, word);
+			      lookup_name, text, word);
   }
 
   /* Search upwards from currently selected frame (so that we can
@@ -6485,8 +6480,7 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 	completion_list_add_name (tracker,
 				  SYMBOL_LANGUAGE (sym),
 				  SYMBOL_LINKAGE_NAME (sym),
-				  lookup_name,
-				  text, text_len, text, word);
+				  lookup_name, text, word);
       }
     }
 
@@ -6502,8 +6496,7 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
       completion_list_add_name (tracker,
 				SYMBOL_LANGUAGE (sym),
 				SYMBOL_LINKAGE_NAME (sym),
-				lookup_name,
-				text, text_len, text, word);
+				lookup_name, text, word);
     }
   }
 
@@ -6519,8 +6512,7 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
       completion_list_add_name (tracker,
 				SYMBOL_LANGUAGE (sym),
 				SYMBOL_LINKAGE_NAME (sym),
-				lookup_name,
-				text, text_len, text, word);
+				lookup_name, text, word);
     }
   }
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 68b8af5..c5cd9ec 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4819,7 +4819,6 @@ completion_list_add_name (completion_tracker &tracker,
 			  language symbol_language,
 			  const char *symname,
 			  const lookup_name_info &lookup_name,
-			  const char *sym_text, int sym_text_len,
 			  const char *text, const char *word)
 {
   completion_match_result &match_res
@@ -4841,23 +4840,23 @@ completion_list_add_name (completion_tracker &tracker,
   {
     char *newobj;
 
-    if (word == sym_text)
+    if (word == text)
       {
 	newobj = (char *) xmalloc (strlen (symname) + 5);
 	strcpy (newobj, symname);
       }
-    else if (word > sym_text)
+    else if (word > text)
       {
 	/* Return some portion of symname.  */
 	newobj = (char *) xmalloc (strlen (symname) + 5);
-	strcpy (newobj, symname + (word - sym_text));
+	strcpy (newobj, symname + (word - text));
       }
     else
       {
 	/* Return some of SYM_TEXT plus symname.  */
-	newobj = (char *) xmalloc (strlen (symname) + (sym_text - word) + 5);
-	strncpy (newobj, word, sym_text - word);
-	newobj[sym_text - word] = '\0';
+	newobj = (char *) xmalloc (strlen (symname) + (text - word) + 5);
+	strncpy (newobj, word, text - word);
+	newobj[text - word] = '\0';
 	strcat (newobj, symname);
       }
 
@@ -4873,12 +4872,11 @@ static void
 completion_list_add_symbol (completion_tracker &tracker,
 			    symbol *sym,
 			    const lookup_name_info &lookup_name,
-			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
   completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
 			    SYMBOL_NATURAL_NAME (sym),
-			    lookup_name, sym_text, sym_text_len, text, word);
+			    lookup_name, text, word);
 }
 
 /* completion_list_add_name wrapper for struct minimal_symbol.  */
@@ -4887,12 +4885,11 @@ static void
 completion_list_add_msymbol (completion_tracker &tracker,
 			     minimal_symbol *sym,
 			     const lookup_name_info &lookup_name,
-			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
   completion_list_add_name (tracker, MSYMBOL_LANGUAGE (sym),
 			    MSYMBOL_NATURAL_NAME (sym),
-			    lookup_name, sym_text, sym_text_len, text, word);
+			    lookup_name, text, word);
 }
 
 
@@ -4903,7 +4900,6 @@ static void
 completion_list_objc_symbol (completion_tracker &tracker,
 			     struct minimal_symbol *msymbol,
 			     const lookup_name_info &lookup_name,
-			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
   static char *tmp = NULL;
@@ -4918,12 +4914,12 @@ completion_list_objc_symbol (completion_tracker &tracker,
   if ((method[0] != '-') && (method[0] != '+'))
     return;
 
-  if (sym_text[0] == '[')
+  if (text[0] == '[')
     /* Complete on shortened method method.  */
     completion_list_add_name (tracker, language_objc,
 			      method + 1,
 			      lookup_name,
-			      sym_text, sym_text_len, text, word);
+			      text, word);
 
   while ((strlen (method) + 1) >= tmplen)
     {
@@ -4945,12 +4941,10 @@ completion_list_objc_symbol (completion_tracker &tracker,
       tmp[category - method] = ' ';
       memcpy (tmp + (category - method) + 1, selector, strlen (selector) + 1);
       completion_list_add_name (tracker, language_objc, tmp,
-				lookup_name,
-				sym_text, sym_text_len, text, word);
-      if (sym_text[0] == '[')
+				lookup_name, text, word);
+      if (text[0] == '[')
 	completion_list_add_name (tracker, language_objc, tmp + 1,
-				  lookup_name,
-				  sym_text, sym_text_len, text, word);
+				  lookup_name, text, word);
     }
 
   if (selector != NULL)
@@ -4962,8 +4956,7 @@ completion_list_objc_symbol (completion_tracker &tracker,
 	*tmp2 = '\0';
 
       completion_list_add_name (tracker, language_objc, tmp,
-				lookup_name,
-				sym_text, sym_text_len, text, word);
+				lookup_name, text, word);
     }
 }
 
@@ -5017,7 +5010,6 @@ static void
 completion_list_add_fields (completion_tracker &tracker,
 			    struct symbol *sym,
 			    const lookup_name_info &lookup_name,
-			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
   if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
@@ -5031,8 +5023,7 @@ completion_list_add_fields (completion_tracker &tracker,
 	  if (TYPE_FIELD_NAME (t, j))
 	    completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
 				      TYPE_FIELD_NAME (t, j),
-				      lookup_name,
-				      sym_text, sym_text_len, text, word);
+				      lookup_name, text, word);
     }
 }
 
@@ -5042,7 +5033,6 @@ static void
 add_symtab_completions (struct compunit_symtab *cust,
 			completion_tracker &tracker,
 			const lookup_name_info &lookup_name,
-			const char *sym_text, int sym_text_len,
 			const char *text, const char *word,
 			enum type_code code)
 {
@@ -5065,7 +5055,6 @@ add_symtab_completions (struct compunit_symtab *cust,
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
 	    completion_list_add_symbol (tracker, sym,
 					lookup_name,
-					sym_text, sym_text_len,
 					text, word);
 	}
     }
@@ -5091,8 +5080,6 @@ default_collect_symbol_completion_matches_break_on
   struct block_iterator iter;
   /* The symbol we are completing on.  Points in same buffer as text.  */
   const char *sym_text;
-  /* Length of sym_text.  */
-  int sym_text_len;
   struct cleanup *cleanups;
 
   /* Now look for the symbol we are supposed to complete on.  */
@@ -5149,10 +5136,7 @@ default_collect_symbol_completion_matches_break_on
       }
   }
 
-  sym_text_len = strlen (sym_text);
-
-  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
-				name_match_type, true);
+  lookup_name_info lookup_name (sym_text, name_match_type, true);
 
   /* At this point scan through the misc symbol vectors and add each
      symbol you find to the list.  Eventually we want to ignore
@@ -5166,19 +5150,17 @@ default_collect_symbol_completion_matches_break_on
 	  QUIT;
 
 	  completion_list_add_msymbol (tracker, msymbol, lookup_name,
-				       sym_text, sym_text_len,
-				       text, word);
+				       sym_text, word);
 
 	  completion_list_objc_symbol (tracker, msymbol, lookup_name,
-				       sym_text, sym_text_len, text,
-				       word);
+				       sym_text, word);
 	}
     }
 
   /* Add completions for all currently loaded symbol tables.  */
   ALL_COMPUNITS (objfile, cust)
     add_symtab_completions (cust, tracker, lookup_name,
-			    sym_text, sym_text_len, text, word, code);
+			    sym_text, word, code);
 
   /* Look through the partial symtabs for all symbols which begin by
      matching SYM_TEXT.  Expand all CUs that you find to the list.  */
@@ -5189,8 +5171,7 @@ default_collect_symbol_completion_matches_break_on
 			     {
 			       add_symtab_completions (symtab,
 						       tracker, lookup_name,
-						       sym_text, sym_text_len,
-						       text, word, code);
+						       sym_text, word, code);
 			     },
 			   ALL_DOMAIN);
 
@@ -5212,17 +5193,14 @@ default_collect_symbol_completion_matches_break_on
 	    if (code == TYPE_CODE_UNDEF)
 	      {
 		completion_list_add_symbol (tracker, sym, lookup_name,
-					    sym_text, sym_text_len, text,
-					    word);
+					    sym_text, word);
 		completion_list_add_fields (tracker, sym, lookup_name,
-					    sym_text, sym_text_len, text,
-					    word);
+					    sym_text, word);
 	      }
 	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
 	      completion_list_add_symbol (tracker, sym, lookup_name,
-					  sym_text, sym_text_len, text,
-					  word);
+					  sym_text, word);
 	  }
 
 	/* Stop when we encounter an enclosing function.  Do not stop for
@@ -5240,12 +5218,12 @@ default_collect_symbol_completion_matches_break_on
       if (surrounding_static_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
 	  completion_list_add_fields (tracker, sym, lookup_name,
-				      sym_text, sym_text_len, text, word);
+				      sym_text, word);
 
       if (surrounding_global_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
 	  completion_list_add_fields (tracker, sym, lookup_name,
-				      sym_text, sym_text_len, text, word);
+				      sym_text, word);
     }
 
   /* Skip macros if we are completing a struct tag -- arguable but
@@ -5261,12 +5239,8 @@ default_collect_symbol_completion_matches_break_on
 				 macro_source_file *,
 				 int)
 	{
-	  completion_list_add_name (tracker,
-				    language_c,
-				    macro_name,
-				    lookup_name,
-				    sym_text, sym_text_len,
-				    text, word);
+	  completion_list_add_name (tracker, language_c, macro_name,
+				    lookup_name, sym_text, word);
 	};
 
       /* Add any macros visible in the default scope.  Note that this
@@ -5349,8 +5323,6 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
 {
   /* The symbol we are completing on.  Points in same buffer as text.  */
   const char *sym_text;
-  /* Length of sym_text.  */
-  int sym_text_len;
 
   /* Now look for the symbol we are supposed to complete on.
      FIXME: This should be language-specific.  */
@@ -5398,10 +5370,7 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
       }
   }
 
-  sym_text_len = strlen (sym_text);
-
-  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
-				name_match_type, true);
+  lookup_name_info lookup_name (sym_text, name_match_type, true);
 
   /* Go through symtabs for SRCFILE and check the externs and statics
      for symbols which match.  */
@@ -5409,8 +5378,7 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
     {
       add_symtab_completions (SYMTAB_COMPUNIT (s),
 			      tracker, lookup_name,
-			      sym_text, sym_text_len,
-			      text, word, TYPE_CODE_UNDEF);
+			      sym_text, word, TYPE_CODE_UNDEF);
       return false;
     });
 }
diff --git a/gdb/symtab.h b/gdb/symtab.h
index a452804..d68eed8 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1966,7 +1966,6 @@ void completion_list_add_name (completion_tracker &tracker,
 			       language symbol_language,
 			       const char *symname,
 			       const lookup_name_info &lookup_name,
-			       const char *sym_text, int sym_text_len,
 			       const char *text, const char *word);
 
 #endif /* !defined(SYMTAB_H) */
-- 
2.5.5

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

* [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache)
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (32 preceding siblings ...)
  2017-06-02 12:30 ` [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching] Pedro Alves
@ 2017-06-02 12:31 ` Pedro Alves
  2017-07-13 20:41   ` Keith Seitz
  2017-06-02 12:31 ` [PATCH 22/40] get_int_var_value Pedro Alves
                   ` (6 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:31 UTC (permalink / raw)
  To: gdb-patches

Tab completion when debugging a program binary that uses GDB index is
surprisingly much slower than when GDB uses psymtabs instead.  Around
1.5x/3x slower.  That's surprising, because the whole point of GDB
index is to speed things up...

For example, with:

 set pagination off
 set $count = 0
 while $count < 400
   complete b string_prin         # matches gdb's string_printf
   printf "count = %d\n", $count
   set $count = $count + 1
 end

 $ time ./gdb --batch -q  ./gdb-with-index -ex "source script.cmd"
 real    0m11.042s
 user    0m10.920s
 sys     0m0.042s

 $ time ./gdb --batch -q  ./gdb-without-index -ex "source script.cmd"
 real    0m4.635s
 user    0m4.590s
 sys     0m0.037s

Same but with:
 -   complete b string_prin
 +   complete b zzzzzz
to exercise the no-matches worse case, master currently gets you
something like:

 with index           without index
 real    0m11.971s    0m8.413s
 user    0m11.912s    0m8.355s
 sys     0m0.035s     0m0.035s

Running gdb under perf shows 80% spent inside
maybe_add_partial_symtab_filename, and 20% spent in the lbasename
inside that.

The problem that tab completion walks over all compunit symtabs, and
for each, walks the contained file symtabs.  And there a huge number
of file symtabs (each included system header, etc.) that appear in
each compunit symtab's file symtab list.  As in, when debugging GDB, I
have 367381 symtabs iterated, when of those only 5371 filenames are
unique...

This was a regression from the earlier (nice) split of symtabs in
compunit symtabs + file symtabs.

The fix here is to add a cache of unique filenames per objfile so that
the walk / uniquing is only done once.  There's already a abstraction
for this in symtab.c; this patch moves that code out to a separate
file and C++ifies it bit.

This makes the worse-case scenario above consistently drop to ~2.5s
(1.5s for the "string_prin" hit case), making it over 3.3x times
faster than psymtabs in this use case (7x in the "string_prin" hit
case).

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* Makefile.in (COMMON_OBS): Add filename-seen-cache.o.
	* dwarf2read.c: Include "filename-seen-cache.h".
	* dwarf2read.c (dwarf2_per_objfile) <filenames_cache>: New field.
	(dw2_map_symbol_filenames): Build and use a filenames_seen_cache.
	* filename-seen-cache.c: New file.
	* filename-seen-cache.h: New file.
	* symtab.c: Include "filename-seen-cache.h".
	(struct filename_seen_cache, INITIAL_FILENAME_SEEN_CACHE_SIZE)
	(create_filename_seen_cache, clear_filename_seen_cache)
	(delete_filename_seen_cache, filename_seen): Delete, parts moved
	to filename-seen-cache.h/filename_seen_cache.c.
	(output_source_filename, sources_info)
	(maybe_add_partial_symtab_filename)
	(make_source_files_completion_list): Adjust to use
	filename_seen_cache.
---
 gdb/Makefile.in           |   1 +
 gdb/dwarf2read.c          | 105 ++++++++++++++++++++++++++--------------------
 gdb/filename-seen-cache.c |  73 ++++++++++++++++++++++++++++++++
 gdb/filename-seen-cache.h |  64 ++++++++++++++++++++++++++++
 gdb/symtab.c              | 100 ++++++-------------------------------------
 5 files changed, 211 insertions(+), 132 deletions(-)
 create mode 100644 gdb/filename-seen-cache.c
 create mode 100644 gdb/filename-seen-cache.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 8be73ba..380df11 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1705,6 +1705,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	f-typeprint.o \
 	f-valprint.o \
 	fileio.o \
+	filename-seen-cache.o \
 	filestuff.o \
 	filesystem.o \
 	findcmd.o \
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index cb33fc9..439257f 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -73,10 +73,10 @@
 #include "common/function-view.h"
 #include "common/gdb_optional.h"
 #include "common/underlying.h"
-
 #include <fcntl.h>
 #include <sys/types.h>
 #include <algorithm>
+#include "filename-seen-cache.h"
 
 typedef struct symbol *symbolp;
 DEF_VEC_P (symbolp);
@@ -324,6 +324,9 @@ struct dwarf2_per_objfile
 
   /* Table containing line_header indexed by offset and offset_in_dwz.  */
   htab_t line_header_hash;
+
+  /* Table containing all filenames.  */
+  filename_seen_cache *filenames_cache;
 };
 
 static struct dwarf2_per_objfile *dwarf2_per_objfile;
@@ -4256,64 +4259,76 @@ static void
 dw2_map_symbol_filenames (struct objfile *objfile, symbol_filename_ftype *fun,
 			  void *data, int need_fullname)
 {
-  int i;
-  htab_up visited (htab_create_alloc (10, htab_hash_pointer, htab_eq_pointer,
-				      NULL, xcalloc, xfree));
-
   dw2_setup (objfile);
 
-  /* The rule is CUs specify all the files, including those used by
-     any TU, so there's no need to scan TUs here.
-     We can ignore file names coming from already-expanded CUs.  */
-
-  for (i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
+  if (dwarf2_per_objfile->filenames_cache == NULL)
     {
-      struct dwarf2_per_cu_data *per_cu = dw2_get_cutu (i);
+      dwarf2_per_objfile->filenames_cache = new filename_seen_cache ();
 
-      if (per_cu->v.quick->compunit_symtab)
-	{
-	  void **slot = htab_find_slot (visited.get (),
-					per_cu->v.quick->file_names,
-					INSERT);
+      htab_up visited (htab_create_alloc (10,
+					  htab_hash_pointer, htab_eq_pointer,
+					  NULL, xcalloc, xfree));
 
-	  *slot = per_cu->v.quick->file_names;
-	}
-    }
-
-  for (i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
-    {
-      int j;
-      struct dwarf2_per_cu_data *per_cu = dw2_get_cu (i);
-      struct quick_file_names *file_data;
-      void **slot;
+      /* The rule is CUs specify all the files, including those used
+	 by any TU, so there's no need to scan TUs here.  We can
+	 ignore file names coming from already-expanded CUs.  */
 
-      /* We only need to look at symtabs not already expanded.  */
-      if (per_cu->v.quick->compunit_symtab)
-	continue;
+      for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
+	{
+	  struct dwarf2_per_cu_data *per_cu = dw2_get_cutu (i);
 
-      file_data = dw2_get_file_names (per_cu);
-      if (file_data == NULL)
-	continue;
+	  if (per_cu->v.quick->compunit_symtab)
+	    {
+	      void **slot = htab_find_slot (visited.get (),
+					    per_cu->v.quick->file_names,
+					    INSERT);
 
-      slot = htab_find_slot (visited.get (), file_data, INSERT);
-      if (*slot)
-	{
-	  /* Already visited.  */
-	  continue;
+	      *slot = per_cu->v.quick->file_names;
+	    }
 	}
-      *slot = file_data;
 
-      for (j = 0; j < file_data->num_file_names; ++j)
+      for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
 	{
-	  const char *this_real_name;
+	  int j;
+	  struct dwarf2_per_cu_data *per_cu = dw2_get_cu (i);
+	  struct quick_file_names *file_data;
+	  void **slot;
 
-	  if (need_fullname)
-	    this_real_name = dw2_get_real_path (objfile, file_data, j);
-	  else
-	    this_real_name = NULL;
-	  (*fun) (file_data->file_names[j], this_real_name, data);
+	  /* We only need to look at symtabs not already expanded.  */
+	  if (per_cu->v.quick->compunit_symtab)
+	    continue;
+
+	  file_data = dw2_get_file_names (per_cu);
+	  if (file_data == NULL)
+	    continue;
+
+	  slot = htab_find_slot (visited.get (), file_data, INSERT);
+	  if (*slot)
+	    {
+	      /* Already visited.  */
+	      continue;
+	    }
+	  *slot = file_data;
+
+	  for (int j = 0; j < file_data->num_file_names; ++j)
+	    {
+	      const char *filename = file_data->file_names[j];
+	      dwarf2_per_objfile->filenames_cache->filename_seen (filename,
+								  true);
+	    }
 	}
     }
+
+  dwarf2_per_objfile->filenames_cache->traverse ([&] (const char *filename)
+    {
+      const char *this_real_name;
+
+      if (need_fullname)
+	this_real_name = gdb_realpath (filename);
+      else
+	this_real_name = NULL;
+      (*fun) (filename, this_real_name, data);
+    });
 }
 
 static int
diff --git a/gdb/filename-seen-cache.c b/gdb/filename-seen-cache.c
new file mode 100644
index 0000000..e04c7de
--- /dev/null
+++ b/gdb/filename-seen-cache.c
@@ -0,0 +1,73 @@
+/* Filename-seen cache for the GNU debugger, GDB.
+
+   Copyright (C) 1986-2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "filename-seen-cache.h"
+#include "filenames.h"
+
+  /* Initial size of the table.  It automagically grows from here.  */
+#define INITIAL_FILENAME_SEEN_CACHE_SIZE 100
+
+/* filename_seen_cache constructor.  */
+
+filename_seen_cache::filename_seen_cache ()
+{
+  m_tab = htab_create_alloc (INITIAL_FILENAME_SEEN_CACHE_SIZE,
+			     filename_hash, filename_eq,
+			     NULL, xcalloc, xfree);
+}
+
+/* Empty the cache, but do not delete it.  */
+
+void
+filename_seen_cache::clear ()
+{
+  htab_empty (m_tab);
+}
+
+/* filename_seen_cache destructor.  */
+
+filename_seen_cache::~filename_seen_cache ()
+{
+  htab_delete (m_tab);
+}
+
+/* If FILE is not already in the table of files in CACHE, return zero;
+   otherwise return non-zero.  Optionally add FILE to the table if ADD
+   is non-zero.
+
+   NOTE: We don't manage space for FILE, we assume FILE lives as long
+   as the caller needs.  */
+
+bool
+filename_seen_cache::filename_seen (const char *file, bool add)
+{
+  void **slot;
+
+  /* Is FILE in tab?  */
+  slot = htab_find_slot (m_tab, file, add ? INSERT : NO_INSERT);
+  if (*slot != NULL)
+    return true;
+
+  /* No; maybe add it to tab.  */
+  if (add)
+    *slot = (char *) file;
+
+  return false;
+}
diff --git a/gdb/filename-seen-cache.h b/gdb/filename-seen-cache.h
new file mode 100644
index 0000000..c041d3e
--- /dev/null
+++ b/gdb/filename-seen-cache.h
@@ -0,0 +1,64 @@
+/* Filename-seen cache for the GNU debugger, GDB.
+
+   Copyright (C) 1986-2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "common/function-view.h"
+
+/* Cache to watch for file names already seen.  */
+
+class filename_seen_cache
+{
+public:
+  filename_seen_cache ();
+  ~filename_seen_cache ();
+
+  /* Disable copy.  */
+  filename_seen_cache (const filename_seen_cache &) = delete;
+  void operator= (const filename_seen_cache &) = delete;
+
+  /* Empty the cache, but do not delete it.  */
+  void clear ();
+
+  /* If FILE is not already in the table of files in CACHE, return
+     false; otherwise return true.  Optionally add FILE to the table
+     if ADD is true.
+
+     NOTE: We don't manage space for FILE, we assume FILE lives as
+     long as the caller needs.  */
+  bool filename_seen (const char *file, bool add);
+
+  /* Traverse all cache entries, calling CALLBACK on each.  The
+     filename is passed as argument to CALLBACK.  */
+  void traverse (gdb::function_view<void (const char *)> callback)
+  {
+    htab_traverse_noresize (m_tab,
+			    [] (void **slot, void *info) -> int
+			    {
+			      auto filename = (const char *) *slot;
+			      auto cb = (gdb::function_view<void (const char *)> *) info;
+			      (*cb) (filename);
+			      return 1;
+			    },
+			    &callback);
+  }
+
+private:
+  /* Table of files seen so far.  */
+  htab_t m_tab;
+};
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 69f3bc2..4414d71 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -62,6 +62,7 @@
 #include "parser-defs.h"
 #include "completer.h"
 #include "progspace-and-thread.h"
+#include "filename-seen-cache.h"
 
 /* Forward declarations for local functions.  */
 
@@ -3966,74 +3967,6 @@ operator_chars (const char *p, const char **end)
 }
 \f
 
-/* Cache to watch for file names already seen by filename_seen.  */
-
-struct filename_seen_cache
-{
-  /* Table of files seen so far.  */
-  htab_t tab;
-  /* Initial size of the table.  It automagically grows from here.  */
-#define INITIAL_FILENAME_SEEN_CACHE_SIZE 100
-};
-
-/* filename_seen_cache constructor.  */
-
-static struct filename_seen_cache *
-create_filename_seen_cache (void)
-{
-  struct filename_seen_cache *cache = XNEW (struct filename_seen_cache);
-
-  cache->tab = htab_create_alloc (INITIAL_FILENAME_SEEN_CACHE_SIZE,
-				  filename_hash, filename_eq,
-				  NULL, xcalloc, xfree);
-
-  return cache;
-}
-
-/* Empty the cache, but do not delete it.  */
-
-static void
-clear_filename_seen_cache (struct filename_seen_cache *cache)
-{
-  htab_empty (cache->tab);
-}
-
-/* filename_seen_cache destructor.
-   This takes a void * argument as it is generally used as a cleanup.  */
-
-static void
-delete_filename_seen_cache (void *ptr)
-{
-  struct filename_seen_cache *cache = (struct filename_seen_cache *) ptr;
-
-  htab_delete (cache->tab);
-  xfree (cache);
-}
-
-/* If FILE is not already in the table of files in CACHE, return zero;
-   otherwise return non-zero.  Optionally add FILE to the table if ADD
-   is non-zero.
-
-   NOTE: We don't manage space for FILE, we assume FILE lives as long
-   as the caller needs.  */
-
-static int
-filename_seen (struct filename_seen_cache *cache, const char *file, int add)
-{
-  void **slot;
-
-  /* Is FILE in tab?  */
-  slot = htab_find_slot (cache->tab, file, add ? INSERT : NO_INSERT);
-  if (*slot != NULL)
-    return 1;
-
-  /* No; maybe add it to tab.  */
-  if (add)
-    *slot = (char *) file;
-
-  return 0;
-}
-
 /* Data structure to maintain printing state for output_source_filename.  */
 
 struct output_source_filename_data
@@ -4063,7 +3996,7 @@ output_source_filename (const char *name,
      symtabs; it doesn't hurt to check.  */
 
   /* Was NAME already seen?  */
-  if (filename_seen (data->filename_seen_cache, name, 1))
+  if (data->filename_seen_cache->filename_seen (name, true))
     {
       /* Yes; don't print it again.  */
       return;
@@ -4095,16 +4028,15 @@ sources_info (char *ignore, int from_tty)
   struct symtab *s;
   struct objfile *objfile;
   struct output_source_filename_data data;
-  struct cleanup *cleanups;
 
   if (!have_full_symbols () && !have_partial_symbols ())
     {
       error (_("No symbol table is loaded.  Use the \"file\" command."));
     }
 
-  data.filename_seen_cache = create_filename_seen_cache ();
-  cleanups = make_cleanup (delete_filename_seen_cache,
-			   data.filename_seen_cache);
+  filename_seen_cache filenames_seen;
+
+  data.filename_seen_cache = &filenames_seen;
 
   printf_filtered ("Source files for which symbols have been read in:\n\n");
 
@@ -4120,13 +4052,11 @@ sources_info (char *ignore, int from_tty)
   printf_filtered ("Source files for which symbols "
 		   "will be read in on demand:\n\n");
 
-  clear_filename_seen_cache (data.filename_seen_cache);
+  filenames_seen.clear ();
   data.first = 1;
   map_symbol_filenames (output_partial_symbol_filename, &data,
 			1 /*need_fullname*/);
   printf_filtered ("\n");
-
-  do_cleanups (cleanups);
 }
 
 /* Compare FILE against all the NFILES entries of FILES.  If BASENAMES is
@@ -5551,7 +5481,7 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
 
   if (not_interesting_fname (filename))
     return;
-  if (!filename_seen (data->filename_seen_cache, filename, 1)
+  if (!data->filename_seen_cache->filename_seen (filename, true)
       && filename_ncmp (filename, data->text, data->text_len) == 0)
     {
       /* This file matches for a completion; add it to the
@@ -5563,7 +5493,7 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
       const char *base_name = lbasename (filename);
 
       if (base_name != filename
-	  && !filename_seen (data->filename_seen_cache, base_name, 1)
+	  && !data->filename_seen_cache->filename_seen (base_name, true)
 	  && filename_ncmp (base_name, data->text, data->text_len) == 0)
 	add_filename_to_list (base_name, data->text, data->word, data->list);
     }
@@ -5584,23 +5514,20 @@ make_source_files_completion_list (const char *text, const char *word)
   VEC (char_ptr) *list = NULL;
   const char *base_name;
   struct add_partial_filename_data datum;
-  struct filename_seen_cache *filename_seen_cache;
-  struct cleanup *back_to, *cache_cleanup;
+  struct cleanup *back_to;
 
   if (!have_full_symbols () && !have_partial_symbols ())
     return list;
 
   back_to = make_cleanup (do_free_completion_list, &list);
 
-  filename_seen_cache = create_filename_seen_cache ();
-  cache_cleanup = make_cleanup (delete_filename_seen_cache,
-				filename_seen_cache);
+  filename_seen_cache filenames_seen;
 
   ALL_FILETABS (objfile, cu, s)
     {
       if (not_interesting_fname (s->filename))
 	continue;
-      if (!filename_seen (filename_seen_cache, s->filename, 1)
+      if (!filenames_seen.filename_seen (s->filename, true)
 	  && filename_ncmp (s->filename, text, text_len) == 0)
 	{
 	  /* This file matches for a completion; add it to the current
@@ -5615,13 +5542,13 @@ make_source_files_completion_list (const char *text, const char *word)
 	     command do when they parse file names.  */
 	  base_name = lbasename (s->filename);
 	  if (base_name != s->filename
-	      && !filename_seen (filename_seen_cache, base_name, 1)
+	      && !filenames_seen.filename_seen (base_name, true)
 	      && filename_ncmp (base_name, text, text_len) == 0)
 	    add_filename_to_list (base_name, text, word, &list);
 	}
     }
 
-  datum.filename_seen_cache = filename_seen_cache;
+  datum.filename_seen_cache = &filenames_seen;
   datum.text = text;
   datum.word = word;
   datum.text_len = text_len;
@@ -5629,7 +5556,6 @@ make_source_files_completion_list (const char *text, const char *word)
   map_symbol_filenames (maybe_add_partial_symtab_filename, &datum,
 			0 /*need_fullname*/);
 
-  do_cleanups (cache_cleanup);
   discard_cleanups (back_to);
 
   return list;
-- 
2.5.5

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

* [PATCH 31/40] Handle custom completion match prefix / LCD
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (35 preceding siblings ...)
  2017-06-02 12:31 ` [PATCH 29/40] Simplify completion_list_add_name | remove sym_text / sym_text_len Pedro Alves
@ 2017-06-02 12:33 ` Pedro Alves
  2017-08-08 21:28   ` Keith Seitz
  2017-06-02 12:39 ` [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching Pedro Alves
                   ` (3 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:33 UTC (permalink / raw)
  To: gdb-patches

A following patch will add support for wild matching for C++ symbols,
making completing on "b push_ba" on a C++ program complete to
std::vector<...>::push_back, std::string::push_back etc., like:

 (gdb) b push_ba[TAB]
 std::vector<...>::push_back(....)
 std::string<...>::push_back(....)

Currently, we compute the "lowest common denominator" between all
completion candidates (what the input line is adjusted to) as the
common prefix of all matches.  That's problematic with wild matching
as above, as then we'd end up with TAB changing the input line to
"b std::", losing the original input, like:

 (gdb) b push_ba[TAB]
 std::vector<...>::push_back(....)
 std::string<...>::push_back(....)
 (gdb) b std::

while obviously we'd want it to adjust itself to "b push_back(" instead:

 (gdb) b push_ba[TAB]
 std::vector<...>::push_back(....)
 std::string<...>::push_back(....)
 (gdb) b push_back(

This patch adds the core code necessary to support this, though
nothing really makes use of it yet in this patch.

yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_lookup_name_info::matches): Change type of
	parameter from completion_match to completion_match_result.
	Adjust.
	(do_wild_match, do_full_match, ada_symbol_name_matches): Likewise.
	* completer.c (completion_tracker::maybe_add_completion): Add
	match_for_lcd parameter and use it.
	(completion_tracker::add_completion): Likewise.
	* completer.h (class completion_match_for_lcd): New class.
	(completion_match_result::match_for_lcd): New field.
	(completion_tracker): Add comments.
	(completion_tracker::add_completion): Add match_for_lcd parameter.
	(completion_tracker::reset_completion_match_result): Reset
	match_for_lcd too.
	(completion_tracker::maybe_add_completion): Add match_for_lcd
	parameter.
	* cp-support.c (cp_fq_symbol_name_matches): Change type of
	parameter from completion_match to completion_match_result.
	Adjust.
	* language.c (default_symbol_name_matcher): Change type of
	parameter from completion_match to completion_match_result.
	Adjust.
	* language.h (completion_match_for_lcd): Forward declare.
	(default_symbol_name_matcher): Change type of parameter from
	completion_match to completion_match_result.
	* symtab.c (compare_symbol_name): Adjust.
	(completion_list_add_name): Pass the match_for_lcd to the tracker.
	* symtab.h (ada_lookup_name_info::matches): Change type of
	parameter from completion_match to completion_match_result.
	(symbol_name_matcher_ftype): Likewise, and update comments.
---
 gdb/ada-lang.c   | 20 +++++++++-------
 gdb/completer.c  | 17 +++++++++----
 gdb/completer.h  | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 gdb/cp-support.c |  9 ++++---
 gdb/language.c   |  9 ++++---
 gdb/language.h   |  3 ++-
 gdb/symtab.c     | 11 +++++++--
 gdb/symtab.h     | 20 ++++++++++------
 8 files changed, 128 insertions(+), 34 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 11ff719..868ee35 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6341,7 +6341,7 @@ bool
 ada_lookup_name_info::matches
   (const char *sym_name,
    symbol_name_match_type match_type,
-   completion_match *comp_match) const
+   completion_match_result *comp_match_res) const
 {
   bool match = false;
   const char *text = m_encoded_name.c_str ();
@@ -6399,14 +6399,15 @@ ada_lookup_name_info::matches
   if (!match)
     return false;
 
-  if (comp_match != NULL)
+  if (comp_match_res != NULL)
     {
-      std::string &match_str = comp_match->storage ();
+      std::string &match_str = comp_match_res->match.storage ();
 
       if (!m_encoded_p)
 	{
 	  match_str = ada_decode (sym_name);
-	  comp_match->set_match (match_str.c_str ());
+	  comp_match_res->match.set_match (match_str.c_str ());
+	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
 	}
       else
 	{
@@ -6415,7 +6416,8 @@ ada_lookup_name_info::matches
 	  else
 	    match_str = sym_name;
 
-	  comp_match->set_match (match_str.c_str ());
+	  comp_match_res->match.set_match (match_str.c_str ());
+	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
 	}
     }
 
@@ -13863,7 +13865,7 @@ static const struct exp_descriptor ada_exp_descriptor = {
 static bool
 do_wild_match (const char *symbol_search_name,
 	       const lookup_name_info &lookup_name,
-	       completion_match *match)
+	       completion_match_result *comp_match_res)
 {
   return wild_match (symbol_search_name, ada_lookup_name (lookup_name));
 }
@@ -13873,7 +13875,7 @@ do_wild_match (const char *symbol_search_name,
 static bool
 do_full_match (const char *symbol_search_name,
 	       const lookup_name_info &lookup_name,
-	       completion_match *match)
+	       completion_match_result *comp_match_res)
 {
   return full_match (symbol_search_name, ada_lookup_name (lookup_name));
 }
@@ -13943,11 +13945,11 @@ ada_lookup_name_info::ada_lookup_name_info (const lookup_name_info &lookup_name)
 static bool
 ada_symbol_name_matches (const char *symbol_search_name,
 			 const lookup_name_info &lookup_name,
-			 completion_match *match)
+			 completion_match_result *comp_match_res)
 {
   return lookup_name.ada ().matches (symbol_search_name,
 				     lookup_name.match_type (),
-				     match);
+				     comp_match_res);
 }
 
 /* Implement the "la_get_symbol_name_matcher" language_defn method for
diff --git a/gdb/completer.c b/gdb/completer.c
index 0835563..eabbce7 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1503,7 +1503,9 @@ completion_tracker::~completion_tracker ()
 /* See completer.h.  */
 
 bool
-completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
+completion_tracker::maybe_add_completion
+  (gdb::unique_xmalloc_ptr<char> name,
+   completion_match_for_lcd *match_for_lcd)
 {
   void **slot;
 
@@ -1516,7 +1518,13 @@ completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
   slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
   if (*slot == HTAB_EMPTY_ENTRY)
     {
-      const char *match_for_lcd_str = name.get ();
+      const char *match_for_lcd_str = NULL;
+
+      if (match_for_lcd != NULL)
+	match_for_lcd_str = match_for_lcd->finish ();
+
+      if (match_for_lcd_str == NULL)
+	match_for_lcd_str = name.get ();
 
       recompute_lowest_common_denominator (match_for_lcd_str);
 
@@ -1528,9 +1536,10 @@ completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
 }
 
 void
-completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
+completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name,
+				    completion_match_for_lcd *match_for_lcd)
 {
-  if (!maybe_add_completion (std::move (name)))
+  if (!maybe_add_completion (std::move (name), match_for_lcd))
     throw_max_completions_reached_error ();
 }
 
diff --git a/gdb/completer.h b/gdb/completer.h
index df90822..db781ab 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -116,12 +116,44 @@ private:
   std::string m_storage;
 };
 
+/* The result of a successful completion match, but for least common
+   denominator (LCD) computation.  Some completers provide matches
+   that don't start with the completion "word".  E.g., completing on
+   "b push_ba" on a C++ program usually completes to
+   std::vector<...>::push_back, std::string::push_back etc.  In such
+   case, the symbol comparison routine will set the LCD match to point
+   into the "push_back" substring within the symbol's name string.  */
+class completion_match_for_lcd
+{
+public:
+  /* Set the match for LCD.  See m_match's description.  */
+  void set_match (const char *match)
+  { m_match = match; }
+
+  /* Get the resulting LCD, after a successful match.  */
+  const char *finish ()
+  { return m_match; }
+
+  /* Prepare for another completion matching sequence.  */
+  void clear ()
+  { m_match = NULL; }
+
+private:
+  /* The completion match result for LCD.  This is usually either a
+     pointer into to a substring within a symbol's name, or to the
+     storage of the pairing completion_match object.  */
+  const char *m_match;
+};
+
 /* Convenience aggregate holding info returned by the symbol name
    matching routines (see symbol_name_matcher_ftype).  */
 struct completion_match_result
 {
   /* The completion match candidate.  */
   completion_match match;
+
+  /* The completion match, for LCD computation purposes.  */
+  completion_match_for_lcd match_for_lcd;
 };
 
 /* The final result of a completion that is handed over to either
@@ -190,6 +222,21 @@ public:
      that necessitates the time consuming expansion of many symbol
      tables.
 
+   - The completer's idea of least common denominator (aka the common
+     prefix) between all completion matches to hand over to readline.
+     Some completers provide matches that don't start with the
+     completion "word".  E.g., completing on "b push_ba" on a C++
+     program usually completes to std::vector<...>::push_back,
+     std::string::push_back etc.  If all matches happen to start with
+     "std::", then readline would figure out that the lowest common
+     denominator is "std::", and thus would do a partial completion
+     with that.  I.e., it would replace "push_ba" in the input buffer
+     with "std::", losing the original "push_ba", which is obviously
+     undesirable.  To avoid that, such completers pass the substring
+     of the match that matters for common denominator computation as
+     MATCH_FOR_LCD argument to add_completion.  The end result is
+     passed to readline in gdb_rl_attempted_completion_function.
+
    - The custom word point to hand over to readline, for completers
      that parse the input string in order to dynamically adjust
      themselves depending on exactly what they're completing.  E.g.,
@@ -209,7 +256,8 @@ public:
   /* Add the completion NAME to the list of generated completions if
      it is not there already.  If too many completions were already
      found, this throws an error.  */
-  void add_completion (gdb::unique_xmalloc_ptr<char> name);
+  void add_completion (gdb::unique_xmalloc_ptr<char> name,
+		       completion_match_for_lcd *match_for_lcd = NULL);
 
   /* Add all completions matches in LIST.  Elements are moved out of
      LIST.  */
@@ -272,6 +320,7 @@ public:
 
     /* Clear any previous match.  */
     res.match.clear ();
+    res.match_for_lcd.clear ();
     return m_completion_match_result;
   }
 
@@ -294,10 +343,19 @@ private:
   /* Add the completion NAME to the list of generated completions if
      it is not there already.  If false is returned, too many
      completions were found.  */
-  bool maybe_add_completion (gdb::unique_xmalloc_ptr<char> name);
+  bool maybe_add_completion (gdb::unique_xmalloc_ptr<char> name,
+			     completion_match_for_lcd *match_for_lcd);
 
   /* Given a new match, recompute the lowest common denominator (LCD)
-     to hand over to readline.  */
+     to hand over to readline.  Normally readline computes this itself
+     based on the whole set of completion matches.  However, some
+     completers want to override readline, in order to be able to
+     provide a LCD that is not really a prefix of the matches, but the
+     lowest common denominator of some relevant substring of each
+     match.  E.g., "b push_ba" completes to
+     "std::vector<..>::push_back", "std::string::push_back", etc., and
+     in this case we want the lowest common denominator to be
+     "push_back" instead of "std::".  */
   void recompute_lowest_common_denominator (const char *new_match);
 
   /* Completion match outputs returned by the symbol name matching
@@ -352,8 +410,13 @@ private:
      See intro.  */
   char *m_lowest_common_denominator = NULL;
 
-  /* If true, the LCD is unique.  I.e., all completion candidates had
-     the same string.  */
+  /* If true, the LCD is unique.  I.e., all completions had the same
+     MATCH_FOR_LCD substring, even if the completions were different.
+     For example, if "break function<tab>" found "a::function()" and
+     "b::function()", the LCD will be "function()" in both cases and
+     so we want to tell readline to complete the line with
+     "function()", instead of showing all the possible
+     completions.  */
   bool m_lowest_common_denominator_unique = false;
 };
 
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 064a45b..397c738 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1647,7 +1647,7 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
 static bool
 cp_fq_symbol_name_matches (const char *symbol_search_name,
 			   const lookup_name_info &lookup_name,
-			   completion_match *match)
+			   completion_match_result *comp_match_res)
 {
   /* Get the demangled name.  */
   const std::string &name = lookup_name.cplus ().lookup_name ();
@@ -1660,8 +1660,11 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
 			    name.c_str (), name.size (),
 			    mode) == 0)
     {
-      if (match != NULL)
-	match->set_match (symbol_search_name);
+      if (comp_match_res != NULL)
+	{
+	  comp_match_res->match.set_match (symbol_search_name);
+	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
+	}
       return true;
     }
 
diff --git a/gdb/language.c b/gdb/language.c
index 6705a3b..511a86f 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -714,7 +714,7 @@ default_get_string (struct value *value, gdb_byte **buffer, int *length,
 bool
 default_symbol_name_matcher (const char *symbol_search_name,
 			     const lookup_name_info &lookup_name,
-			     completion_match *match)
+			     completion_match_result *comp_match_res)
 {
   const std::string &name = lookup_name.name ();
 
@@ -725,8 +725,11 @@ default_symbol_name_matcher (const char *symbol_search_name,
   if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
 			    mode) == 0)
     {
-      if (match != NULL)
-	match->set_match (symbol_search_name);
+      if (comp_match_res != NULL)
+	{
+	  comp_match_res->match.set_match (symbol_search_name);
+	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
+	}
       return true;
     }
   else
diff --git a/gdb/language.h b/gdb/language.h
index 90b3bb4..1ac43b3 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -37,6 +37,7 @@ struct type_print_options;
 struct lang_varobj_ops;
 struct parser_state;
 struct compile_instance;
+struct completion_match_for_lcd;
 
 #define MAX_FORTRAN_DIMS  7	/* Maximum number of F77 array dims.  */
 
@@ -628,7 +629,7 @@ void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 extern bool default_symbol_name_matcher
   (const char *symbol_search_name,
    const lookup_name_info &lookup_name,
-   completion_match *match);
+   completion_match_result *comp_match_res);
 
 /* Get language LANG's symbol_name_matcher method for LOOKUP_NAME.
    Returns default_symbol_name_matcher if not set.  */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index c5cd9ec..bbc317d 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4807,7 +4807,7 @@ compare_symbol_name (const char *symbol_name, language symbol_language,
   symbol_name_matcher_ftype *name_match
     = language_get_symbol_name_matcher (lang, lookup_name);
 
-  return name_match (symbol_name, lookup_name, &match_res.match);
+  return name_match (symbol_name, lookup_name, &match_res);
 }
 
 /*  Test to see if the symbol specified by SYMNAME (which is already
@@ -4862,7 +4862,14 @@ completion_list_add_name (completion_tracker &tracker,
 
     gdb::unique_xmalloc_ptr<char> completion (newobj);
 
-    tracker.add_completion (std::move (completion));
+    /* Here we pass the match-for-lcd object to add_completion.  Some
+       languages match the user text against substrings of symbol
+       names in some cases.  E.g., in C++, "b push_ba" completes to
+       "std::vector::push_back", "std::string::push_back", etc., and
+       in this case we want the completion lowest common denominator
+       to be "push_back" instead of "std::".  */
+    tracker.add_completion (std::move (completion),
+			    &match_res.match_for_lcd);
   }
 }
 
diff --git a/gdb/symtab.h b/gdb/symtab.h
index d68eed8..736fea0 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -81,7 +81,7 @@ class ada_lookup_name_info final
      otherwise.  If non-NULL, store the matching results in MATCH.  */
   bool matches (const char *symbol_search_name,
 		symbol_name_match_type match_type,
-		completion_match *match) const;
+		completion_match_result *comp_match_res) const;
 
   /* The Ada-encoded lookup name.  */
   const std::string &lookup_name () const
@@ -292,15 +292,21 @@ private:
 
    SYMBOL_SEARCH_NAME should be a symbol's "search" name.
 
-   On success and if non-NULL, MATCH is set to point to the symbol
-   name as should be presented to the user as a completion match list
-   element.  In most languages, this is the same as the symbol's
-   search name, but in some, like Ada, the display name is dynamically
-   computed within the comparison routine.  */
+   On success and if non-NULL, COMP_MATCH_RES->match is set to point
+   to the symbol name as should be presented to the user as a
+   completion match list element.  In most languages, this is the same
+   as the symbol's search name, but in some, like Ada, the display
+   name is dynamically computed within the comparison routine.
+
+   Also, on success and if non-NULL, COMP_MATCH_RES->match_for_lcd
+   points the part of SYMBOL_SEARCH_NAME that was considered to match
+   LOOKUP_NAME.  E.g., in C++, in linespec/wild mode, if the symbol is
+   "foo::function()" and LOOKUP_NAME is "function(", MATCH_FOR_LCD
+   points to "function()" inside SYMBOL_SEARCH_NAME.  */
 typedef bool (symbol_name_matcher_ftype)
   (const char *symbol_search_name,
    const lookup_name_info &lookup_name,
-   completion_match *match);
+   completion_match_result *comp_match_res);
 
 /* Some of the structures in this file are space critical.
    The space-critical structures are:
-- 
2.5.5

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

* [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (36 preceding siblings ...)
  2017-06-02 12:33 ` [PATCH 31/40] Handle custom completion match prefix / LCD Pedro Alves
@ 2017-06-02 12:39 ` Pedro Alves
  2017-07-18 20:14   ` Keith Seitz
  2017-06-02 12:39 ` [PATCH 23/40] Make language_def O(1) Pedro Alves
                   ` (2 subsequent siblings)
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:39 UTC (permalink / raw)
  To: gdb-patches

Summary:
 - This is preparation for supporting wild name matching on C++ too.
 - This is also preparation for TAB-completion fixes.
 - Makes symbol name matching (think strcmp_iw) be based on a per-language method.
 - Merges completion and non-completion name comparison (think
   language_ops::la_get_symbol_name_cmp generalized).
 - Avoid re-hashing lookup name multiple times
 - Centralizes preparing a name for lookup (Ada name encoding / C++ Demangling),
   both completion and non-completion.
 - Fixes Ada latent bug with verbatim name matches in expressions
 - Makes ada-lang.c use common|symtab.c completion code a bit more.

Ada's wild matching basically means that

 "(gdb) break foo"

will find all methods named "foo" in all packages.  Translating to
C++, it's roughly the same as saying that "break klass::method" sets
breakpoints on all "klass::method" methods of all classes, no matter
the namespace.  A following patch will teach GDB about fullname vs
wild matching for C++ too.  This patch is preparatory work to get
there.

Another idea here is to do symbol name matching based on the symbol
language's algorithm.  I.e., avoid dependency on current language set.

This allows for example doing

  (gdb) b foo::bar< int > (<tab>

and having gdb name match the C++ symbols correctly even if the
current language is C or Assembly (or Rust, or Ada, or ...), which can
easily happen if you step into an Assembly/C runtime library frame.

By encapsulating all the information related to a lookup name in a
class, we can also cache hash computation for a given language in the
lookup name object, to avoid recomputing it over and over.

Similarly, because we don't really know upfront which languages the
lookup name will be matched against, for each language we store the
lookup name transformed into a search name.  E.g., for C++, that means
demangling the name.  But for Ada, it means encoding the name.  This
actually forces us to centralize all the different lookup name
encoding in a central place, resulting in clearer code, IMO.  See
e.g., the new ada_lookup_name_info class.

The lookup name -> symbol search name computation is also done only
once per language.

The old language->la_get_symbol_name_cmp / symbol_name_cmp_ftype are
generalized to work with both completion, and normal symbol look up.

At some point early on, I had separate completion vs non-completion
language vector entry points, but a single method ends up being better
IMO for simplifying things -- the more we merge the completion /
non-completion name lookup code paths, the less changes for bugs
causing completion vs normal lookup finding different symbols.

The ada-lex.l change is necessary because when doing

  (gdb) p <UpperCase>

then the name that is passed to write_ write_var_or_type ->
ada_lookup_symbol_list misses the "<>", i.e., it's just "UpperCase",
and we end up doing a wild match against "UpperCase" lowercased by
ada_lookup_name_info's constructor.  I.e., "uppercase" wouldn't ever
match "UpperCase", and the symbol lookup fails.

This wouldn't cause any regression in the testsuite, but I added a new
test that would pass before the patch and fail after, if it weren't
for that fix.

This is latent bug that happens to go unnoticed because that
particular path was inconsistent with the rest of Ada symbol lookup by
not lowercasing the lookup name.

Ada's symbol_completion_add is deleted, replaced by using common
code's completion_list_add_name.  To make the latter work for Ada, we
needed to add a new output parameter, because Ada wants to return back
a custom completion candidates that are not the symbol name.

With this patch, minimal symbol demangled name hashing is made
consistent with regular symbol hashing.  I.e., it now goes via the
language vector's search_name_hash method too, as I had suggested in a
previous patch.

dw2_expand_symtabs_matching / .gdb_index symbol names were a
challenge.  The problem is that we have no way to telling what is the
language of each symbol name found in the index, until we expand the
corresponding full symbol, which is off course what we're trying to
avoid.  Language information is simply not considered in the index
format...  Since the symbol name hashing and comparison routines are
per-language, we now have a problem.  The patch sorts this out by
matching each name against all languages.  This is inneficient, and
indeed slows down completion several times.  E.g., with:

 $ cat script.cmd
 set pagination off
 set $count = 0
 while $count < 400
   complete b string_prin
   printf "count = %d\n", $count
   set $count = $count + 1
 end

 $ time gdb --batch -q ./gdb-with-index -ex "source script-string_printf.cmd"

I get, before patch (-O2, x86-64):

 real    0m1.773s
 user    0m1.737s
 sys     0m0.040s

While after patch (-O2, x86-64):

 real    0m9.843s
 user    0m9.482s
 sys     0m0.034s

However, the following patch will optimize this, and will actually
make this use case faster compared to the "before patch" above:

 real    0m1.321s
 user    0m1.285s
 sys     0m0.039s

gdb/ChangeLog:
yyyy-mm-dd   Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_encode): Rename to ..
	(ada_encode_1): ... this.  Add throw_errors parameter and handle
	it.
	(ada_encode): Reimplement.
	(match_name): Delete, folded into full_name.
	(should_use_wild_match): Delete.
	(name_match_type_from_name): New.
	(ada_lookup_simple_minsym): Use lookup_name_info and the
	language's symbol_name_matcher_ftype.
	(add_symbols_from_enclosing_procs, ada_add_local_symbols)
	(ada_add_block_renamings): Adjust to use lookup_name_info.
	(ada_lookup_name): New.
	(add_nonlocal_symbols, ada_add_all_symbols)
	(ada_lookup_symbol_list_worker, ada_lookup_symbol_list)
	(ada_iterate_over_symbols): Adjust to use lookup_name_info.
	(ada_name_for_lookup): Delete.
	(wild_match): Reverse sense of return type.  Use bool.
	(full_match): Reverse sense of return type.  Inline bits of old
	match_name here.
	(ada_add_block_symbols, ada_add_block_symbols): Adjust to use
	lookup_name_info.
	(symbol_completion_match): Delete, folded into...
	(ada_lookup_name_info::matches): ... .this new method.
	(symbol_completion_add): Delete.
	(ada_collect_symbol_completion_matches): Add name_match_type
	parameter.  Adjust to use lookup_name_info and
	completion_list_add_name.
	(get_var_value, ada_add_global_exceptions): Adjust to use
	lookup_name_info.
	(do_wild_match, do_full_match): New functions.
	(ada_lookup_name_info::ada_lookup_name_info): New method.
	(ada_symbol_name_matches, ada_get_symbol_name_matcher): New
	functions.
	(ada_language_defn): Install ada_get_symbol_name_matcher.
	* ada-lex.l (processId): If name starts with '<', copy it
	verbatim.
	* block.c (block_iter_match_step, block_iter_match_first)
	(block_iter_match_next, block_lookup_symbol)
	(block_lookup_symbol_primary, block_find_symbol): Adjust to use
	lookup_name_info.
	* block.h (block_iter_match_first, block_iter_match_next)
	(ALL_BLOCK_SYMBOLS_WITH_NAME): Adjust to use lookup_name_info.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Adjust comments to
	refer to la_get_symbol_name_matcher.
	* completer.c (complete_files_symbols)
	(collect_explicit_location_matches, symbol_completer): Pass a
	symbol_name_match_type down.
	* completer.h (class completion_match, completion_match_result):
	New classes.
	(completion_tracker::reset_completion_match_result): New method.
	(completion_tracker::m_completion_match_result): New field.
	* cp-support.c (make_symbol_overload_list_block): Adjust to use
	lookup_name_info.
	(cp_fq_symbol_name_matches, cp_get_symbol_name_matcher): New
	functions.
	* cp-support.h (cp_get_symbol_name_matcher): New declaration.
	* d-lang.c: Adjust comments to refer to
	la_get_symbol_name_matcher.
	* dictionary.c (dict_vector) <iter_match_first, iter_match_next>:
	Adjust to use lookup_name_info.
	(dict_iter_match_first, dict_iter_match_next)
	(iter_match_first_hashed, iter_match_next_hashed)
	(iter_match_first_linear, iter_match_next_linear): Adjust to work
	with a lookup_name_info.
	* dictionary.h (dict_iter_match_first, dict_iter_match_next):
	Likewise.
	* dwarf2read.c (dw2_lookup_symbol): Adjust to use lookup_name_info.
	(dw2_map_matching_symbols): Adjust to use symbol_name_match_type.
	(gdb_index_symbol_name_matcher): New class.
	(dw2_expand_symtabs_matching) Adjust to use lookup_name_info and
	gdb_index_symbol_name_matcher.  Accept a NULL symbol_matcher.
	* f-lang.c (f_collect_symbol_completion_matches): Adjust to work
	with a symbol_name_match_type.
	(f_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* go-lang.c (go_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* language.c (default_symbol_name_matcher)
	(language_get_symbol_name_matcher): New functions.
	(unknown_language_defn, auto_language_defn): Adjust comments to
	refer to la_get_symbol_name_matcher.
	* language.h (symbol_name_cmp_ftype): Delete.
	(language_defn) <la_collect_symbol_completion_matches>: Add match
	type parameter.
	<la_get_symbol_name_cmp>: Delete field.
	<la_get_symbol_name_matcher>: New field.
	<la_iterate_over_symbols>: Adjust to use lookup_name_info.
	(default_symbol_name_matcher, language_get_symbol_name_matcher):
	Declare.
	* linespec.c (iterate_over_all_matching_symtabs)
	(iterate_over_file_blocks): Adjust to use lookup_name_info.
	(find_methods): Add language parameter, and use lookup_name_info
	and the language's symbol_name_matcher_ftype.
	(linespec_complete_function): Adjust.
	(linespec_complete_label, linespec_complete): Adjust.
	(lookup_prefix_sym): Use lookup_name_info.
	(add_all_symbol_names_from_pspace): Adjust.
	(find_superclass_methods): Add language parameter and pass it
	down.
	(find_method): Pass symbol language down.
	(find_linespec_symbols): Don't demangle or Ada encode here.
	(search_minsyms_for_name): Add lookup_name_info parameter.
	(add_matching_symbols_to_info): Add name_match_type parameter.
	Use lookup_name_info.
	* linespec.h (linespec_complete_function)
	(linespec_complete_label): Add name_match_type parameter.
	* m2-lang.c (m2_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* minsyms.c: Include <algorithm>.
	(add_minsym_to_demangled_hash_table): Remove table parameter and
	add objfile parameter.  Use search_name_hash, and add language to
	demangled languages vector.
	(struct found_minimal_symbols): New struct.
	(lookup_minimal_symbol_mangled, lookup_minimal_symbol_demangled):
	New functions.
	(lookup_minimal_symbol): Adjust to use them.  Don't canonicalize
	input names here.  Use lookup_name_info instead.  Lookup up
	demangled names once for each language in the demangled names
	vector.
	(iterate_over_minimal_symbols): Use lookup_name_info.  Lookup up
	demangled names once for each language in the demangled names
	vector.
	(build_minimal_symbol_hash_tables): Adjust.
	* minsyms.h (iterate_over_minimal_symbols): Adjust to pass down a
	lookup_name_info.
	* objc-lang.c (objc_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* objfiles.h: Include <vector>.
	(objfile_per_bfd_storage) <demangled_hash_languages>: New field.
	* opencl-lang.c (opencl_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* p-lang.c (pascal_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* psymtab.c (psym_lookup_symbol): Use lookup_name_info.
	(match_partial_symbol): Use symbol_name_match_type and
	lookup_name_info.
	(lookup_partial_symbol): Use lookup_name_info.
	(map_block): Use symbol_name_match_type and lookup_name_info.
	(psym_map_matching_symbols): Use symbol_name_match_type.
	(psymbol_name_matches): New.
	(recursively_search_psymtabs): Use lookup_name_info and
	psymbol_name_matches.
	(psym_expand_symtabs_matching): Use lookup_name_info.
	* rust-lang.c (rust_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* symfile-debug.c (debug_qf_map_matching_symbols)
	(debug_qf_map_matching_symbols): Use symbol_name_match_type.
	(debug_qf_expand_symtabs_matching): use lookup_name_info.
	* symfile.c (expand_symtabs_matching): Use lookup_name_info.
	* symfile.h (quick_symbol_functions) <map_matching_symbols>:
	Adjust to use symbol_name_match_type.
	<expand_symtabs_matching>: Adjust to use lookup_name_info.
	(expand_symtabs_matching): Adjust to use lookup_name_info.
	* symmisc.c (maintenance_expand_symtabs): Use
	lookup_name_info::match_any ().
	* symtab.c (symbol_matches_search_name): New.
	(eq_symbol_entry): Adjust to use lookup_name_info and the
	language's matcher.
	(demangle_for_lookup_info::demangle_for_lookup_info): New.
	(lookup_name_info::match_any): New.
	(iterate_over_symbols, search_symbols): Use lookup_name_info.
	(compare_symbol_name): Add language, lookup_name_info and
	completion_match_result parameters, and use them.
	(completion_list_add_name): Make extern.  Add language and
	lookup_name_info parameters.  Use them.
	(completion_list_add_symbol, completion_list_add_msymbol)
	(completion_list_objc_symbol): Add lookup_name_info parameters and
	adjust.  Pass down language.
	(completion_list_add_fields): Add lookup_name_info parameters and
	adjust.  Pass down language.
	(add_symtab_completions): Add lookup_name_info parameters and
	adjust.
	(default_collect_symbol_completion_matches_break_on): Add
	name_match_type parameter, and use it.  Use lookup_name_info.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches): Add name_match_type
	parameter, and pass it down.
	(collect_symbol_completion_matches_type): Adjust.
	(collect_file_symbol_completion_matches): Add name_match_type
	parameter, and use lookup_name_info.
	* symtab.h: Include <string> and "common/gdb_optional.h".
	(enum class symbol_name_match_type): New.
	(class ada_lookup_name_info): New.
	(struct demangle_for_lookup_info): New.
	(class lookup_name_info): New.
	(symbol_name_matcher_ftype): New.
	(SYMBOL_MATCHES_SEARCH_NAME): Use symbol_matches_search_name.
	(symbol_matches_search_name): Declare.
	(MSYMBOL_MATCHES_SEARCH_NAME): Delete.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches)
	(collect_file_symbol_completion_matches): Add name_match_type
	parameter.
	(iterate_over_symbols): Use lookup_name_info.
	(completion_list_add_name): Declare.

gdb/testsuite/ChangeLog:
yyyy-mm-dd   Pedro Alves  <palves@redhat.com>

	* gdb.ada/complete.exp (p <Exported_Capitalized>): New test.
	(p Exported_Capitalized): New test.
	(p exported_capitalized): New test.
	* utils.c (enum class strncmp_iw_mode): Moved to utils.h.
	(strncmp_iw_with_mode): Now extern.
	* utils.h (enum class strncmp_iw_mode): Moved from utils.c.
	(strncmp_iw_with_mode): Declare.
---
 gdb/ada-lang.c                     | 715 +++++++++++++++++++------------------
 gdb/ada-lex.l                      |  22 +-
 gdb/block.c                        |  38 +-
 gdb/block.h                        |  33 +-
 gdb/c-lang.c                       |   8 +-
 gdb/completer.c                    |   4 +
 gdb/completer.h                    |  75 ++++
 gdb/cp-support.c                   |  39 +-
 gdb/cp-support.h                   |   3 +
 gdb/d-lang.c                       |   2 +-
 gdb/dictionary.c                   |  66 ++--
 gdb/dictionary.h                   |   6 +-
 gdb/dwarf2read.c                   | 100 +++++-
 gdb/f-lang.c                       |   4 +-
 gdb/go-lang.c                      |   2 +-
 gdb/language.c                     |  39 +-
 gdb/language.h                     |  43 ++-
 gdb/linespec.c                     | 133 ++++---
 gdb/m2-lang.c                      |   2 +-
 gdb/minsyms.c                      | 370 ++++++++++++-------
 gdb/minsyms.h                      |   2 +-
 gdb/objc-lang.c                    |   2 +-
 gdb/objfiles.h                     |   7 +
 gdb/opencl-lang.c                  |   2 +-
 gdb/p-lang.c                       |   2 +-
 gdb/psymtab.c                      |  98 +++--
 gdb/rust-lang.c                    |   2 +-
 gdb/symfile-debug.c                |   6 +-
 gdb/symfile.c                      |   2 +
 gdb/symfile.h                      |   4 +-
 gdb/symmisc.c                      |   1 +
 gdb/symtab.c                       | 189 +++++++---
 gdb/symtab.h                       | 292 ++++++++++++++-
 gdb/testsuite/gdb.ada/complete.exp |  10 +
 gdb/utils.c                        |  16 +-
 gdb/utils.h                        |  27 ++
 36 files changed, 1571 insertions(+), 795 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 1d4cf36..50f7581 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -102,16 +102,16 @@ static int ada_type_match (struct type *, struct type *, int);
 
 static int ada_args_match (struct symbol *, struct value **, int);
 
-static int full_match (const char *, const char *);
-
 static struct value *make_array_descriptor (struct type *, struct value *);
 
 static void ada_add_block_symbols (struct obstack *,
-                                   const struct block *, const char *,
-                                   domain_enum, struct objfile *, int);
+				   const struct block *,
+				   const lookup_name_info &lookup_name,
+				   domain_enum, struct objfile *);
 
 static void ada_add_all_symbols (struct obstack *, const struct block *,
-				 const char *, domain_enum, int, int *);
+				 const lookup_name_info &lookup_name,
+				 domain_enum, int, int *);
 
 static int is_nonfunction (struct block_symbol *, int);
 
@@ -201,7 +201,7 @@ static int is_name_suffix (const char *);
 
 static int advance_wild_match (const char **, const char *, int);
 
-static int wild_match (const char *, const char *);
+static bool wild_match (const char *name, const char *patn);
 
 static struct value *ada_coerce_ref (struct value *);
 
@@ -974,25 +974,29 @@ const struct ada_opname_map ada_opname_table[] = {
   {NULL, NULL}
 };
 
-/* The "encoded" form of DECODED, according to GNAT conventions.
-   The result is valid until the next call to ada_encode.  */
+/* The "encoded" form of DECODED, according to GNAT conventions.  The
+   result is valid until the next call to ada_encode.  If
+   THROW_ERRORS, throw an error if invalid operator name is found.
+   Otherwise, return NULL in that case.  */
 
-char *
-ada_encode (const char *decoded)
+static char *
+ada_encode_1 (const char *decoded, bool throw_errors)
 {
   static char *encoding_buffer = NULL;
   static size_t encoding_buffer_size = 0;
-  const char *p;
-  int k;
 
   if (decoded == NULL)
     return NULL;
 
+  size_t decoded_len = strlen (decoded);
+
   GROW_VECT (encoding_buffer, encoding_buffer_size,
-             2 * strlen (decoded) + 10);
+             2 * decoded_len + 10);
 
-  k = 0;
-  for (p = decoded; *p != '\0'; p += 1)
+  int k = 0;
+  for (const char *p = decoded, *end = decoded + decoded_len;
+       p < end;
+       p += 1)
     {
       if (*p == '.')
         {
@@ -1004,11 +1008,17 @@ ada_encode (const char *decoded)
           const struct ada_opname_map *mapping;
 
           for (mapping = ada_opname_table;
-               mapping->encoded != NULL
-               && !startswith (p, mapping->decoded); mapping += 1)
+               (mapping->encoded != NULL
+		&& strncmp (p, mapping->decoded, end - p) != 0);
+	       mapping += 1)
             ;
           if (mapping->encoded == NULL)
-            error (_("invalid Ada operator name: %s"), p);
+	    {
+	      if (throw_errors)
+		error (_("invalid Ada operator name: %s"), p);
+	      else
+		return NULL;
+	    }
           strcpy (encoding_buffer + k, mapping->encoded);
           k += strlen (mapping->encoded);
           break;
@@ -1024,6 +1034,15 @@ ada_encode (const char *decoded)
   return encoding_buffer;
 }
 
+/* The "encoded" form of DECODED, according to GNAT conventions.
+   The result is valid until the next call to ada_encode.  */
+
+char *
+ada_encode (const char *decoded)
+{
+  return ada_encode_1 (decoded, true);
+}
+
 /* Return NAME folded to lower case, or, if surrounded by single
    quotes, unfolded, but with the quotes stripped away.  Result good
    to next call.  */
@@ -1488,31 +1507,6 @@ ada_sniff_from_mangled_name (const char *mangled, char **out)
   return 0;
 }
 
-/* Returns non-zero iff SYM_NAME matches NAME, ignoring any trailing
-   suffixes that encode debugging information or leading _ada_ on
-   SYM_NAME (see is_name_suffix commentary for the debugging
-   information that is ignored).  If WILD, then NAME need only match a
-   suffix of SYM_NAME minus the same suffixes.  Also returns 0 if
-   either argument is NULL.  */
-
-static int
-match_name (const char *sym_name, const char *name, int wild)
-{
-  if (sym_name == NULL || name == NULL)
-    return 0;
-  else if (wild)
-    return wild_match (sym_name, name) == 0;
-  else
-    {
-      int len_name = strlen (name);
-
-      return (strncmp (sym_name, name, len_name) == 0
-              && is_name_suffix (sym_name + len_name))
-        || (startswith (sym_name, "_ada_")
-            && strncmp (sym_name + 5, name, len_name) == 0
-            && is_name_suffix (sym_name + len_name + 5));
-    }
-}
 \f
 
                                 /* Arrays */
@@ -4756,16 +4750,18 @@ cache_symbol (const char *name, domain_enum domain, struct symbol *sym,
 \f
                                 /* Symbol Lookup */
 
-/* Return nonzero if wild matching should be used when searching for
-   all symbols matching LOOKUP_NAME.
+/* Return the symbol name match type that should be used used when
+   searching for all symbols matching LOOKUP_NAME.
 
    LOOKUP_NAME is expected to be a symbol name after transformation
    for Ada lookups (see ada_name_for_lookup).  */
 
-static int
-should_use_wild_match (const char *lookup_name)
+static symbol_name_match_type
+name_match_type_from_name (const char *lookup_name)
 {
-  return (strstr (lookup_name, "__") == NULL);
+  return (strstr (lookup_name, "__") == NULL
+	  ? symbol_name_match_type::WILD
+	  : symbol_name_match_type::FULL);
 }
 
 /* Return the result of a standard (literal, C-like) lookup of NAME in
@@ -4935,23 +4931,19 @@ ada_lookup_simple_minsym (const char *name)
   struct bound_minimal_symbol result;
   struct objfile *objfile;
   struct minimal_symbol *msymbol;
-  const int wild_match_p = should_use_wild_match (name);
 
   memset (&result, 0, sizeof (result));
 
-  /* Special case: If the user specifies a symbol name inside package
-     Standard, do a non-wild matching of the symbol name without
-     the "standard__" prefix.  This was primarily introduced in order
-     to allow the user to specifically access the standard exceptions
-     using, for instance, Standard.Constraint_Error when Constraint_Error
-     is ambiguous (due to the user defining its own Constraint_Error
-     entity inside its program).  */
-  if (startswith (name, "standard__"))
-    name += sizeof ("standard__") - 1;
+  symbol_name_match_type match_type = name_match_type_from_name (name);
+  lookup_name_info lookup_name (name, match_type);
+
+  symbol_name_matcher_ftype *match_name
+    = language_get_symbol_name_matcher (language_def (language_ada),
+					lookup_name);
 
   ALL_MSYMBOLS (objfile, msymbol)
   {
-    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), name, wild_match_p)
+    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), lookup_name, NULL)
         && MSYMBOL_TYPE (msymbol) != mst_solib_trampoline)
       {
 	result.minsym = msymbol;
@@ -4971,8 +4963,8 @@ ada_lookup_simple_minsym (const char *name)
 
 static void
 add_symbols_from_enclosing_procs (struct obstack *obstackp,
-                                  const char *name, domain_enum domain,
-                                  int wild_match_p)
+				  const lookup_name_info &lookup_name,
+				  domain_enum domain)
 {
 }
 
@@ -5424,17 +5416,16 @@ remove_irrelevant_renamings (struct block_symbol *syms,
    Note: This function assumes that OBSTACKP has 0 (zero) element in it.  */
 
 static void
-ada_add_local_symbols (struct obstack *obstackp, const char *name,
-                       const struct block *block, domain_enum domain,
-                       int wild_match_p)
+ada_add_local_symbols (struct obstack *obstackp,
+		       const lookup_name_info &lookup_name,
+		       const struct block *block, domain_enum domain)
 {
   int block_depth = 0;
 
   while (block != NULL)
     {
       block_depth += 1;
-      ada_add_block_symbols (obstackp, block, name, domain, NULL,
-			     wild_match_p);
+      ada_add_block_symbols (obstackp, block, lookup_name, domain, NULL);
 
       /* If we found a non-function match, assume that's the one.  */
       if (is_nonfunction (defns_collected (obstackp, 0),
@@ -5447,7 +5438,7 @@ ada_add_local_symbols (struct obstack *obstackp, const char *name,
   /* If no luck so far, try to find NAME as a local symbol in some lexically
      enclosing subprogram.  */
   if (num_defns_collected (obstackp) == 0 && block_depth > 2)
-    add_symbols_from_enclosing_procs (obstackp, name, domain, wild_match_p);
+    add_symbols_from_enclosing_procs (obstackp, lookup_name, domain);
 }
 
 /* An object of this type is used as the user_data argument when
@@ -5501,28 +5492,28 @@ aux_add_nonlocal_symbols (struct block *block, struct symbol *sym, void *data0)
   return 0;
 }
 
-/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are targetted
-   by renamings matching NAME in BLOCK.  Add these symbols to OBSTACKP.  If
-   WILD_MATCH_P is nonzero, perform the naming matching in "wild" mode (see
-   function "wild_match" for more information).  Return whether we found such
-   symbols.  */
+/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are
+   targetted by renamings matching LOOKUP_NAME in BLOCK.  Add these
+   symbols to OBSTACKP.  Return whether we found such symbols.  */
 
 static int
 ada_add_block_renamings (struct obstack *obstackp,
 			 const struct block *block,
-			 const char *name,
-			 domain_enum domain,
-			 int wild_match_p)
+			 const lookup_name_info &lookup_name,
+			 domain_enum domain)
 {
   struct using_direct *renaming;
   int defns_mark = num_defns_collected (obstackp);
 
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (language_def (language_ada),
+					lookup_name);
+
   for (renaming = block_using (block);
        renaming != NULL;
        renaming = renaming->next)
     {
       const char *r_name;
-      int name_match;
 
       /* Avoid infinite recursions: skip this renaming if we are actually
 	 already traversing it.
@@ -5547,11 +5538,13 @@ ada_add_block_renamings (struct obstack *obstackp,
       r_name = (renaming->alias != NULL
 		? renaming->alias
 		: renaming->declaration);
-      name_match
-	= wild_match_p ? wild_match (r_name, name) : strcmp (r_name, name);
-      if (name_match == 0)
-	ada_add_all_symbols (obstackp, block, renaming->declaration, domain,
-			     1, NULL);
+      if (name_match (r_name, lookup_name, NULL))
+	{
+	  lookup_name_info decl_lookup_name (renaming->declaration,
+					     lookup_name.match_type ());
+	  ada_add_all_symbols (obstackp, block, decl_lookup_name, domain,
+			       1, NULL);
+	}
       renaming->searched = 0;
     }
   return num_defns_collected (obstackp) != defns_mark;
@@ -5642,14 +5635,24 @@ compare_names (const char *string1, const char *string2)
   return result;
 }
 
+/* Convenience function to get at the Ada encoded lookup name for
+   LOOKUP_NAME, as a C string.  */
+
+static const char *
+ada_lookup_name (const lookup_name_info &lookup_name)
+{
+  return lookup_name.ada ().lookup_name ().c_str ();
+}
+
 /* Add to OBSTACKP all non-local symbols whose name and domain match
-   NAME and DOMAIN respectively.  The search is performed on GLOBAL_BLOCK
-   symbols if GLOBAL is non-zero, or on STATIC_BLOCK symbols otherwise.  */
+   LOOKUP_NAME and DOMAIN respectively.  The search is performed on
+   GLOBAL_BLOCK symbols if GLOBAL is non-zero, or on STATIC_BLOCK
+   symbols otherwise.  */
 
 static void
-add_nonlocal_symbols (struct obstack *obstackp, const char *name,
-		      domain_enum domain, int global,
-		      int is_wild_match)
+add_nonlocal_symbols (struct obstack *obstackp,
+		      const lookup_name_info &lookup_name,
+		      domain_enum domain, int global)
 {
   struct objfile *objfile;
   struct compunit_symtab *cu;
@@ -5658,6 +5661,9 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
   memset (&data, 0, sizeof data);
   data.obstackp = obstackp;
 
+  const char *name = ada_lookup_name (lookup_name);
+  bool is_wild_match = lookup_name.ada ().wild_match_p ();
+
   ALL_OBJFILES (objfile)
     {
       data.objfile = objfile;
@@ -5665,19 +5671,21 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
       if (is_wild_match)
 	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
 					       aux_add_nonlocal_symbols, &data,
-					       wild_match, NULL);
+					       symbol_name_match_type::WILD,
+					       NULL);
       else
 	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
 					       aux_add_nonlocal_symbols, &data,
-					       full_match, compare_names);
+					       symbol_name_match_type::FULL,
+					       compare_names);
 
       ALL_OBJFILE_COMPUNITS (objfile, cu)
 	{
 	  const struct block *global_block
 	    = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cu), GLOBAL_BLOCK);
 
-	  if (ada_add_block_renamings (obstackp, global_block , name, domain,
-				       is_wild_match))
+	  if (ada_add_block_renamings (obstackp, global_block, lookup_name,
+				       domain))
 	    data.found_sym = 1;
 	}
     }
@@ -5694,14 +5702,15 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
 						 global,
 						 aux_add_nonlocal_symbols,
 						 &data,
-						 full_match, compare_names);
+						 symbol_name_match_type::FULL,
+						 compare_names);
 	}
     }      	
 }
 
-/* Find symbols in DOMAIN matching NAME, in BLOCK and, if FULL_SEARCH is
-   non-zero, enclosing scope and in global scopes, returning the number of
-   matches.  Add these to OBSTACKP.
+/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if
+   FULL_SEARCH is non-zero, enclosing scope and in global scopes,
+   returning the number of matches.  Add these to OBSTACKP.
 
    When FULL_SEARCH is non-zero, any non-function/non-enumeral
    symbol match within the nest of blocks whose innermost member is BLOCK,
@@ -5709,8 +5718,9 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
    enclosing blocks is returned).  If there are any matches in or
    surrounding BLOCK, then these alone are returned.
 
-   Names prefixed with "standard__" are handled specially: "standard__"
-   is first stripped off, and only static and global symbols are searched.
+   Names prefixed with "standard__" are handled specially:
+   "standard__" is first stripped off (by the lookup_name
+   constructor), and only static and global symbols are searched.
 
    If MADE_GLOBAL_LOOKUP_P is non-null, set it before return to whether we had
    to lookup global symbols.  */
@@ -5718,13 +5728,12 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
 static void
 ada_add_all_symbols (struct obstack *obstackp,
 		     const struct block *block,
-		     const char *name,
+		     const lookup_name_info &lookup_name,
 		     domain_enum domain,
 		     int full_search,
 		     int *made_global_lookup_p)
 {
   struct symbol *sym;
-  const int wild_match_p = should_use_wild_match (name);
 
   if (made_global_lookup_p)
     *made_global_lookup_p = 0;
@@ -5736,25 +5745,21 @@ ada_add_all_symbols (struct obstack *obstackp,
      using, for instance, Standard.Constraint_Error when Constraint_Error
      is ambiguous (due to the user defining its own Constraint_Error
      entity inside its program).  */
-  if (startswith (name, "standard__"))
-    {
-      block = NULL;
-      name = name + sizeof ("standard__") - 1;
-    }
+  if (lookup_name.ada ().standard_p ())
+    block = NULL;
 
   /* Check the non-global symbols.  If we have ANY match, then we're done.  */
 
   if (block != NULL)
     {
       if (full_search)
-	ada_add_local_symbols (obstackp, name, block, domain, wild_match_p);
+	ada_add_local_symbols (obstackp, lookup_name, block, domain);
       else
 	{
 	  /* In the !full_search case we're are being called by
 	     ada_iterate_over_symbols, and we don't want to search
 	     superblocks.  */
-	  ada_add_block_symbols (obstackp, block, name, domain, NULL,
-				 wild_match_p);
+	  ada_add_block_symbols (obstackp, block, lookup_name, domain, NULL);
 	}
       if (num_defns_collected (obstackp) > 0 || !full_search)
 	return;
@@ -5764,10 +5769,11 @@ ada_add_all_symbols (struct obstack *obstackp,
      already performed this search before.  If we have, then return
      the same result.  */
 
-  if (lookup_cached_symbol (name, domain, &sym, &block))
+  if (lookup_cached_symbol (ada_lookup_name (lookup_name),
+			    domain, &sym, &block))
     {
       if (sym != NULL)
-        add_defn_to_vec (obstackp, sym, block);
+	add_defn_to_vec (obstackp, sym, block);
       return;
     }
 
@@ -5776,17 +5782,17 @@ ada_add_all_symbols (struct obstack *obstackp,
 
   /* Search symbols from all global blocks.  */
  
-  add_nonlocal_symbols (obstackp, name, domain, 1, wild_match_p);
+  add_nonlocal_symbols (obstackp, lookup_name, domain, 1);
 
   /* Now add symbols from all per-file blocks if we've gotten no hits
      (not strictly correct, but perhaps better than an error).  */
 
   if (num_defns_collected (obstackp) == 0)
-    add_nonlocal_symbols (obstackp, name, domain, 0, wild_match_p);
+    add_nonlocal_symbols (obstackp, lookup_name, domain, 0);
 }
 
-/* Find symbols in DOMAIN matching NAME, in BLOCK and, if full_search is
-   non-zero, enclosing scope and in global scopes, returning the number of
+/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if full_search
+   is non-zero, enclosing scope and in global scopes, returning the number of
    matches.
    Sets *RESULTS to point to a vector of (SYM,BLOCK) tuples,
    indicating the symbols found and the blocks and symbol tables (if
@@ -5803,19 +5809,19 @@ ada_add_all_symbols (struct obstack *obstackp,
    is first stripped off, and only static and global symbols are searched.  */
 
 static int
-ada_lookup_symbol_list_worker (const char *name, const struct block *block,
+ada_lookup_symbol_list_worker (const lookup_name_info &lookup_name,
+			       const struct block *block,
 			       domain_enum domain,
 			       struct block_symbol **results,
 			       int full_search)
 {
-  const int wild_match_p = should_use_wild_match (name);
   int syms_from_global_search;
   int ndefns;
 
   obstack_free (&symbol_list_obstack, NULL);
   obstack_init (&symbol_list_obstack);
-  ada_add_all_symbols (&symbol_list_obstack, block, name, domain,
-		       full_search, &syms_from_global_search);
+  ada_add_all_symbols (&symbol_list_obstack, block, lookup_name,
+		       domain, full_search, &syms_from_global_search);
 
   ndefns = num_defns_collected (&symbol_list_obstack);
   *results = defns_collected (&symbol_list_obstack, 1);
@@ -5823,32 +5829,37 @@ ada_lookup_symbol_list_worker (const char *name, const struct block *block,
   ndefns = remove_extra_symbols (*results, ndefns);
 
   if (ndefns == 0 && full_search && syms_from_global_search)
-    cache_symbol (name, domain, NULL, NULL);
+    cache_symbol (ada_lookup_name (lookup_name), domain, NULL, NULL);
 
   if (ndefns == 1 && full_search && syms_from_global_search)
-    cache_symbol (name, domain, (*results)[0].symbol, (*results)[0].block);
+    cache_symbol (ada_lookup_name (lookup_name), domain,
+		  (*results)[0].symbol, (*results)[0].block);
 
   ndefns = remove_irrelevant_renamings (*results, ndefns, block);
   return ndefns;
 }
 
-/* Find symbols in DOMAIN matching NAME0, in BLOCK0 and enclosing scope and
+/* Find symbols in DOMAIN matching NAME, in BLOCK and enclosing scope and
    in global scopes, returning the number of matches, and setting *RESULTS
    to a vector of (SYM,BLOCK) tuples.
    See ada_lookup_symbol_list_worker for further details.  */
 
 int
-ada_lookup_symbol_list (const char *name0, const struct block *block0,
+ada_lookup_symbol_list (const char *name, const struct block *block,
 			domain_enum domain, struct block_symbol **results)
 {
-  return ada_lookup_symbol_list_worker (name0, block0, domain, results, 1);
+  symbol_name_match_type name_match_type = name_match_type_from_name (name);
+  lookup_name_info lookup_name (name, name_match_type);
+
+  return ada_lookup_symbol_list_worker (lookup_name, block, domain, results, 1);
 }
 
 /* Implementation of the la_iterate_over_symbols method.  */
 
 static void
 ada_iterate_over_symbols
-  (const struct block *block, const char *name, domain_enum domain,
+  (const struct block *block, const lookup_name_info &name,
+   domain_enum domain,
    gdb::function_view<symbol_found_callback_ftype> callback)
 {
   int ndefs, i;
@@ -5862,24 +5873,6 @@ ada_iterate_over_symbols
     }
 }
 
-/* If NAME is the name of an entity, return a string that should
-   be used to look that entity up in Ada units.
-
-   NAME can have any form that the "break" or "print" commands might
-   recognize.  In other words, it does not have to be the "natural"
-   name, or the "encoded" name.  */
-
-std::string
-ada_name_for_lookup (const char *name)
-{
-  int nlen = strlen (name);
-
-  if (name[0] == '<' && name[nlen - 1] == '>')
-    return std::string (name + 1, nlen - 2);
-  else
-    return ada_encode (ada_fold_name (name));
-}
-
 /* The result is as for ada_lookup_symbol_list with FULL_SEARCH set
    to 1, but choosing the first symbol found if there are multiple
    choices.
@@ -6179,11 +6172,12 @@ advance_wild_match (const char **namep, const char *name0, int target0)
   return 1;
 }
 
-/* Return 0 iff NAME encodes a name of the form prefix.PATN.  Ignores any
-   informational suffixes of NAME (i.e., for which is_name_suffix is
-   true).  Assumes that PATN is a lower-cased Ada simple name.  */
+/* Return true iff NAME encodes a name of the form prefix.PATN.
+   Ignores any informational suffixes of NAME (i.e., for which
+   is_name_suffix is true).  Assumes that PATN is a lower-cased Ada
+   simple name.  */
 
-static int
+static bool
 wild_match (const char *name, const char *patn)
 {
   const char *p;
@@ -6199,39 +6193,49 @@ wild_match (const char *name, const char *patn)
 	    if (*p != *name)
 	      break;
 	  if (*p == '\0' && is_name_suffix (name))
-	    return match != name0 && !is_valid_name_for_wild_match (name0);
+	    return match == name0 || is_valid_name_for_wild_match (name0);
 
 	  if (name[-1] == '_')
 	    name -= 1;
 	}
       if (!advance_wild_match (&name, name0, *patn))
-	return 1;
+	return false;
     }
 }
 
-/* Returns 0 iff symbol name SYM_NAME matches SEARCH_NAME, apart from
-   informational suffix.  */
+/* Returns true iff symbol name SYM_NAME matches SEARCH_NAME, ignoring
+   any trailing suffixes that encode debugging information or leading
+   _ada_ on SYM_NAME (see is_name_suffix commentary for the debugging
+   information that is ignored).  */
 
-static int
+static bool
 full_match (const char *sym_name, const char *search_name)
 {
-  return !match_name (sym_name, search_name, 0);
-}
+  size_t len_name = strlen (search_name);
+
+  if (strncmp (sym_name, search_name, len_name) == 0
+      && is_name_suffix (sym_name + len_name))
+    return true;
 
+  if ((startswith (sym_name, "_ada_")
+       && strncmp (sym_name + 5, search_name, len_name) == 0
+       && is_name_suffix (sym_name + len_name + 5)))
+    return true;
 
-/* Add symbols from BLOCK matching identifier NAME in DOMAIN to
-   vector *defn_symbols, updating the list of symbols in OBSTACKP 
-   (if necessary).  If WILD, treat as NAME with a wildcard prefix.
-   OBJFILE is the section containing BLOCK.  */
+  return false;
+}
+
+/* Add symbols from BLOCK matching LOOKUP_NAME in DOMAIN to vector
+   *defn_symbols, updating the list of symbols in OBSTACKP (if
+   necessary).  OBJFILE is the section containing BLOCK.  */
 
 static void
 ada_add_block_symbols (struct obstack *obstackp,
-                       const struct block *block, const char *name,
-                       domain_enum domain, struct objfile *objfile,
-                       int wild)
+		       const struct block *block,
+		       const lookup_name_info &lookup_name,
+		       domain_enum domain, struct objfile *objfile)
 {
   struct block_iterator iter;
-  int name_len = strlen (name);
   /* A matching argument symbol, if any.  */
   struct symbol *arg_sym;
   /* Set true when we find a matching non-argument symbol.  */
@@ -6240,56 +6244,31 @@ ada_add_block_symbols (struct obstack *obstackp,
 
   arg_sym = NULL;
   found_sym = 0;
-  if (wild)
+  for (sym = block_iter_match_first (block, lookup_name, &iter);
+       sym != NULL;
+       sym = block_iter_match_next (lookup_name, &iter))
     {
-      for (sym = block_iter_match_first (block, name, wild_match, &iter);
-	   sym != NULL; sym = block_iter_match_next (name, wild_match, &iter))
-      {
-        if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                   SYMBOL_DOMAIN (sym), domain)
-            && wild_match (SYMBOL_LINKAGE_NAME (sym), name) == 0)
-          {
-	    if (SYMBOL_CLASS (sym) == LOC_UNRESOLVED)
-	      continue;
-	    else if (SYMBOL_IS_ARGUMENT (sym))
-	      arg_sym = sym;
-	    else
-	      {
-                found_sym = 1;
-                add_defn_to_vec (obstackp,
-                                 fixup_symbol_section (sym, objfile),
-                                 block);
-              }
-          }
-      }
-    }
-  else
-    {
-     for (sym = block_iter_match_first (block, name, full_match, &iter);
-	  sym != NULL; sym = block_iter_match_next (name, full_match, &iter))
-      {
-        if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                   SYMBOL_DOMAIN (sym), domain))
-          {
-	    if (SYMBOL_CLASS (sym) != LOC_UNRESOLVED)
-	      {
-		if (SYMBOL_IS_ARGUMENT (sym))
-		  arg_sym = sym;
-		else
-		  {
-		    found_sym = 1;
-		    add_defn_to_vec (obstackp,
-				     fixup_symbol_section (sym, objfile),
-				     block);
-		  }
-	      }
-          }
-      }
+      if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
+				 SYMBOL_DOMAIN (sym), domain))
+	{
+	  if (SYMBOL_CLASS (sym) != LOC_UNRESOLVED)
+	    {
+	      if (SYMBOL_IS_ARGUMENT (sym))
+		arg_sym = sym;
+	      else
+		{
+		  found_sym = 1;
+		  add_defn_to_vec (obstackp,
+				   fixup_symbol_section (sym, objfile),
+				   block);
+		}
+	    }
+	}
     }
 
   /* Handle renamings.  */
 
-  if (ada_add_block_renamings (obstackp, block, name, domain, wild))
+  if (ada_add_block_renamings (obstackp, block, lookup_name, domain))
     found_sym = 1;
 
   if (!found_sym && arg_sym != NULL)
@@ -6299,10 +6278,13 @@ ada_add_block_symbols (struct obstack *obstackp,
                        block);
     }
 
-  if (!wild)
+  if (!lookup_name.ada ().wild_match_p ())
     {
       arg_sym = NULL;
       found_sym = 0;
+      std::string ada_lookup_name = lookup_name.ada ().lookup_name ();
+      const char *name = ada_lookup_name.c_str ();
+      int name_len = ada_lookup_name.size ();
 
       ALL_BLOCK_SYMBOLS (block, iter, sym)
       {
@@ -6353,51 +6335,39 @@ ada_add_block_symbols (struct obstack *obstackp,
 
                                 /* Symbol Completion */
 
-/* If SYM_NAME is a completion candidate for TEXT, return this symbol
-   name in a form that's appropriate for the completion.  The result
-   does not need to be deallocated, but is only good until the next call.
-
-   TEXT_LEN is equal to the length of TEXT.
-   Perform a wild match if WILD_MATCH_P is set.
-   ENCODED_P should be set if TEXT represents the start of a symbol name
-   in its encoded form.  */
+/* See symtab.h.  */
 
-static const char *
-symbol_completion_match (const char *sym_name,
-                         const char *text, int text_len,
-                         int wild_match_p, int encoded_p)
+bool
+ada_lookup_name_info::matches
+  (const char *sym_name,
+   symbol_name_match_type match_type,
+   completion_match *comp_match) const
 {
-  const int verbatim_match = (text[0] == '<');
-  int match = 0;
-
-  if (verbatim_match)
-    {
-      /* Strip the leading angle bracket.  */
-      text = text + 1;
-      text_len--;
-    }
+  bool match = false;
+  const char *text = m_encoded_name.c_str ();
+  size_t text_len = m_encoded_name.size ();
 
   /* First, test against the fully qualified name of the symbol.  */
 
   if (strncmp (sym_name, text, text_len) == 0)
-    match = 1;
+    match = true;
 
-  if (match && !encoded_p)
+  if (match && !m_encoded_p)
     {
       /* One needed check before declaring a positive match is to verify
          that iff we are doing a verbatim match, the decoded version
          of the symbol name starts with '<'.  Otherwise, this symbol name
          is not a suitable completion.  */
       const char *sym_name_copy = sym_name;
-      int has_angle_bracket;
+      bool has_angle_bracket;
 
       sym_name = ada_decode (sym_name);
       has_angle_bracket = (sym_name[0] == '<');
-      match = (has_angle_bracket == verbatim_match);
+      match = (has_angle_bracket == m_verbatim_p);
       sym_name = sym_name_copy;
     }
 
-  if (match && !verbatim_match)
+  if (match && !m_verbatim_p)
     {
       /* When doing non-verbatim match, another check that needs to
          be done is to verify that the potentially matching symbol name
@@ -6408,12 +6378,12 @@ symbol_completion_match (const char *sym_name,
 
       for (tmp = sym_name; *tmp != '\0' && !isupper (*tmp); tmp++);
       if (*tmp != '\0')
-        match = 0;
+	match = false;
     }
 
   /* Second: Try wild matching...  */
 
-  if (!match && wild_match_p)
+  if (!match && m_wild_match_p)
     {
       /* Since we are doing wild matching, this means that TEXT
          may represent an unqualified symbol name.  We therefore must
@@ -6421,91 +6391,48 @@ symbol_completion_match (const char *sym_name,
       sym_name = ada_unqualified_name (ada_decode (sym_name));
 
       if (strncmp (sym_name, text, text_len) == 0)
-        match = 1;
+	match = true;
     }
 
-  /* Finally: If we found a mach, prepare the result to return.  */
+  /* Finally: If we found a match, prepare the result to return.  */
 
   if (!match)
-    return NULL;
-
-  if (verbatim_match)
-    sym_name = add_angle_brackets (sym_name);
-
-  if (!encoded_p)
-    sym_name = ada_decode (sym_name);
-
-  return sym_name;
-}
-
-/* A companion function to ada_collect_symbol_completion_matches().
-   Check if SYM_NAME represents a symbol which name would be suitable
-   to complete TEXT (TEXT_LEN is the length of TEXT), in which case it
-   is added as a completion match to TRACKER.
-
-   ORIG_TEXT is the string original string from the user command
-   that needs to be completed.  WORD is the entire command on which
-   completion should be performed.  These two parameters are used to
-   determine which part of the symbol name should be added to the
-   completion vector.
-   if WILD_MATCH_P is set, then wild matching is performed.
-   ENCODED_P should be set if TEXT represents a symbol name in its
-   encoded formed (in which case the completion should also be
-   encoded).  */
-
-static void
-symbol_completion_add (completion_tracker &tracker,
-		       const char *sym_name,
-                       const char *text, int text_len,
-                       const char *orig_text, const char *word,
-                       int wild_match_p, int encoded_p)
-{
-  const char *match = symbol_completion_match (sym_name, text, text_len,
-                                               wild_match_p, encoded_p);
-  char *completion;
+    return false;
 
-  if (match == NULL)
-    return;
+  if (comp_match != NULL)
+    {
+      std::string &match_str = comp_match->storage ();
 
-  /* We found a match, so add the appropriate completion to the given
-     string vector.  */
+      if (!m_encoded_p)
+	{
+	  match_str = ada_decode (sym_name);
+	  comp_match->set_match (match_str.c_str ());
+	}
+      else
+	{
+	  if (m_verbatim_p)
+	    match_str = add_angle_brackets (sym_name);
+	  else
+	    match_str = sym_name;
 
-  if (word == orig_text)
-    {
-      completion = (char *) xmalloc (strlen (match) + 5);
-      strcpy (completion, match);
-    }
-  else if (word > orig_text)
-    {
-      /* Return some portion of sym_name.  */
-      completion = (char *) xmalloc (strlen (match) + 5);
-      strcpy (completion, match + (word - orig_text));
-    }
-  else
-    {
-      /* Return some of ORIG_TEXT plus sym_name.  */
-      completion = (char *) xmalloc (strlen (match) + (orig_text - word) + 5);
-      strncpy (completion, word, orig_text - word);
-      completion[orig_text - word] = '\0';
-      strcat (completion, match);
+	  comp_match->set_match (match_str.c_str ());
+	}
     }
 
-  tracker.add_completion (gdb::unique_xmalloc_ptr<char> (completion));
+  return true;
 }
 
-/* Add the list of possible symbol names completing TEXT0 to TRACKER.
+/* Add the list of possible symbol names completing TEXT to TRACKER.
    WORD is the entire command on which completion is made.  */
 
 static void
 ada_collect_symbol_completion_matches (completion_tracker &tracker,
 				       complete_symbol_mode mode,
-				       const char *text0, const char *word,
+				       symbol_name_match_type name_match_type,
+				       const char *text, const char *word,
 				       enum type_code code)
 {
-  char *text;
   int text_len;
-  int wild_match_p;
-  int encoded_p;
   struct symbol *sym;
   struct compunit_symtab *s;
   struct minimal_symbol *msymbol;
@@ -6517,39 +6444,15 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
   gdb_assert (code == TYPE_CODE_UNDEF);
 
-  if (text0[0] == '<')
-    {
-      text = xstrdup (text0);
-      make_cleanup (xfree, text);
-      text_len = strlen (text);
-      wild_match_p = 0;
-      encoded_p = 1;
-    }
-  else
-    {
-      text = xstrdup (ada_encode (text0));
-      make_cleanup (xfree, text);
-      text_len = strlen (text);
-      for (i = 0; i < text_len; i++)
-        text[i] = tolower (text[i]);
+  text_len = strlen (text);
 
-      encoded_p = (strstr (text0, "__") != NULL);
-      /* If the name contains a ".", then the user is entering a fully
-         qualified entity name, and the match must not be done in wild
-         mode.  Similarly, if the user wants to complete what looks like
-         an encoded name, the match must not be done in wild mode.  */
-      wild_match_p = (strchr (text0, '.') == NULL && !encoded_p);
-    }
+  lookup_name_info lookup_name (std::string (text, text_len),
+				name_match_type, true);
 
   /* First, look at the partial symtab symbols.  */
   expand_symtabs_matching (NULL,
-			   [&] (const char *symname)
-			   {
-			     return symbol_completion_match (symname,
-							     text, text_len,
-							     wild_match_p,
-							     encoded_p);
-			   },
+			   lookup_name,
+			   NULL,
 			   NULL,
 			   ALL_DOMAIN);
 
@@ -6561,9 +6464,12 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
   ALL_MSYMBOLS (objfile, msymbol)
   {
     QUIT;
-    symbol_completion_add (tracker, MSYMBOL_LINKAGE_NAME (msymbol),
-			   text, text_len, text0, word, wild_match_p,
-			   encoded_p);
+
+    completion_list_add_name (tracker,
+			      MSYMBOL_LANGUAGE (msymbol),
+			      MSYMBOL_LINKAGE_NAME (msymbol),
+			      lookup_name,
+			      text, text_len, text, word);
   }
 
   /* Search upwards from currently selected frame (so that we can
@@ -6576,9 +6482,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
       ALL_BLOCK_SYMBOLS (b, iter, sym)
       {
-        symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                               text, text_len, text0, word,
-                               wild_match_p, encoded_p);
+	completion_list_add_name (tracker,
+				  SYMBOL_LANGUAGE (sym),
+				  SYMBOL_LINKAGE_NAME (sym),
+				  lookup_name,
+				  text, text_len, text, word);
       }
     }
 
@@ -6591,9 +6499,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
     b = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (s), GLOBAL_BLOCK);
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                             text, text_len, text0, word,
-                             wild_match_p, encoded_p);
+      completion_list_add_name (tracker,
+				SYMBOL_LANGUAGE (sym),
+				SYMBOL_LINKAGE_NAME (sym),
+				lookup_name,
+				text, text_len, text, word);
     }
   }
 
@@ -6606,9 +6516,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
       continue;
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                             text, text_len, text0, word,
-                             wild_match_p, encoded_p);
+      completion_list_add_name (tracker,
+				SYMBOL_LANGUAGE (sym),
+				SYMBOL_LINKAGE_NAME (sym),
+				lookup_name,
+				text, text_len, text, word);
     }
   }
 
@@ -11613,11 +11525,12 @@ scan_discrim_bound (const char *str, int k, struct value *dval, LONGEST * px,
 static struct value *
 get_var_value (const char *name, const char *err_msg)
 {
-  struct block_symbol *syms;
-  int nsyms;
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
-  nsyms = ada_lookup_symbol_list (name, get_selected_block (0), VAR_DOMAIN,
-                                  &syms);
+  struct block_symbol *syms;
+  int nsyms = ada_lookup_symbol_list_worker (lookup_name,
+					     get_selected_block (0),
+					     VAR_DOMAIN, &syms, 1);
 
   if (nsyms != 1)
     {
@@ -13336,6 +13249,7 @@ ada_add_global_exceptions (regex_t *preg, VEC(ada_exc_info) **exceptions)
      regular expression used to do the matching refers to the natural
      name.  So match against the decoded name.  */
   expand_symtabs_matching (NULL,
+			   lookup_name_info::match_any (),
 			   [&] (const char *search_name)
 			   {
 			     const char *decoded = ada_decode (search_name);
@@ -13952,16 +13866,113 @@ static const struct exp_descriptor ada_exp_descriptor = {
   ada_evaluate_subexp
 };
 
-/* Implement the "la_get_symbol_name_cmp" language_defn method
-   for Ada.  */
+/* symbol_name_matcher_ftype adapter for wild_match.  */
+
+static bool
+do_wild_match (const char *symbol_search_name,
+	       const lookup_name_info &lookup_name,
+	       completion_match *match)
+{
+  return wild_match (symbol_search_name, ada_lookup_name (lookup_name));
+}
+
+/* symbol_name_matcher_ftype adapter for full_match.  */
 
-static symbol_name_cmp_ftype
-ada_get_symbol_name_cmp (const char *lookup_name)
+static bool
+do_full_match (const char *symbol_search_name,
+	       const lookup_name_info &lookup_name,
+	       completion_match *match)
 {
-  if (should_use_wild_match (lookup_name))
-    return wild_match;
+  return full_match (symbol_search_name, ada_lookup_name (lookup_name));
+}
+
+/* Build the Ada lookup name for LOOKUP_NAME.  */
+
+ada_lookup_name_info::ada_lookup_name_info (const lookup_name_info &lookup_name)
+{
+  const std::string &user_name = lookup_name.name ();
+
+  if (user_name[0] == '<')
+    {
+      if (user_name.back () == '>')
+	m_encoded_name = user_name.substr (1, user_name.size () - 2);
+      else
+	m_encoded_name = user_name.substr (1, user_name.size () - 1);
+      m_encoded_p = true;
+      m_verbatim_p = true;
+      m_wild_match_p = false;
+      m_standard_p = false;
+    }
   else
-    return compare_names;
+    {
+      m_verbatim_p = false;
+
+      m_encoded_p = user_name.find ("__") != std::string::npos;
+
+      if (!m_encoded_p)
+	{
+	  const char *folded = ada_fold_name (user_name.c_str ());
+	  const char *encoded = ada_encode_1 (folded, false);
+	  if (encoded != NULL)
+	    m_encoded_name = encoded;
+	  else
+	    m_encoded_name = user_name;
+	}
+      else
+	m_encoded_name = user_name;
+
+      /* Handle the package Standard special case.  See description of
+	 m_standard_p.  */
+      if (startswith (m_encoded_name.c_str (), "standard__"))
+	{
+	  m_encoded_name = m_encoded_name.substr (sizeof ("standard__") - 1);
+	  m_standard_p = true;
+	}
+      else
+	m_standard_p = false;
+
+      /* If the name contains a ".", then the user is entering a fully
+	 qualified entity name, and the match must not be done in wild
+	 mode.  Similarly, if the user wants to complete what looks
+	 like an encoded name, the match must not be done in wild
+	 mode.  Also, in the standard__ special case always do
+	 non-wild matching.  */
+      m_wild_match_p
+	= (lookup_name.match_type () != symbol_name_match_type::FULL
+	   && !m_encoded_p
+	   && !m_standard_p
+	   && user_name.find ('.') == std::string::npos);
+    }
+}
+
+/* symbol_name_matcher_ftype method for Ada.  This only handles
+   completion mode.  */
+
+static bool
+ada_symbol_name_matches (const char *symbol_search_name,
+			 const lookup_name_info &lookup_name,
+			 completion_match *match)
+{
+  return lookup_name.ada ().matches (symbol_search_name,
+				     lookup_name.match_type (),
+				     match);
+}
+
+/* Implement the "la_get_symbol_name_matcher" language_defn method for
+   Ada.  */
+
+static symbol_name_matcher_ftype *
+ada_get_symbol_name_matcher (const lookup_name_info &lookup_name)
+{
+  if (lookup_name.completion_mode ())
+    return ada_symbol_name_matches;
+  else
+    {
+      if (lookup_name.ada ().wild_match_p ())
+	return do_wild_match;
+      else
+	return do_full_match;
+    }
 }
 
 /* Implement the "la_read_var_value" language_defn method for Ada.  */
@@ -14032,7 +14043,7 @@ extern const struct language_defn ada_language_defn = {
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  ada_get_symbol_name_cmp,	/* la_get_symbol_name_cmp */
+  ada_get_symbol_name_matcher,	/* la_get_symbol_name_matcher */
   ada_iterate_over_symbols,
   default_search_name_hash,
   &ada_varobj_ops,
diff --git a/gdb/ada-lex.l b/gdb/ada-lex.l
index 0825290..2c2d934d 100644
--- a/gdb/ada-lex.l
+++ b/gdb/ada-lex.l
@@ -412,13 +412,12 @@ processReal (struct parser_state *par_state, const char *num0)
 /* Store a canonicalized version of NAME0[0..LEN-1] in yylval.ssym.  The
    resulting string is valid until the next call to ada_parse.  If
    NAME0 contains the substring "___", it is assumed to be already
-   encoded and the resulting name is equal to it.  Otherwise, it differs
+   encoded and the resulting name is equal to it.  Similarly, if the name
+   starts with '<', it is copied verbatim.  Otherwise, it differs
    from NAME0 in that:
-    + Characters between '...' or <...> are transfered verbatim to 
-      yylval.ssym.
-    + <, >, and trailing "'" characters in quoted sequences are removed
-      (a leading quote is preserved to indicate that the name is not to be
-      GNAT-encoded).
+    + Characters between '...' are transfered verbatim to yylval.ssym.
+    + Trailing "'" characters in quoted sequences are removed (a leading quote is
+      preserved to indicate that the name is not to be GNAT-encoded).
     + Unquoted whitespace is removed.
     + Unquoted alphabetic characters are mapped to lower case.
    Result is returned as a struct stoken, but for convenience, the string
@@ -436,7 +435,7 @@ processId (const char *name0, int len)
   while (len > 0 && isspace (name0[len-1]))
     len -= 1;
 
-  if (strstr (name0, "___") != NULL)
+  if (name0[0] == '<' || strstr (name0, "___") != NULL)
     {
       strncpy (name, name0, len);
       name[len] = '\000';
@@ -470,15 +469,6 @@ processId (const char *name0, int len)
 	  while (i0 < len && name0[i0] != '\'');
 	  i0 += 1;
 	  break;
-	case '<':
-	  i0 += 1;
-	  while (i0 < len && name0[i0] != '>')
-	    {
-	      name[i] = name0[i0];
-	      i += 1; i0 += 1;
-	    }
-	  i0 += 1;
-	  break;
 	}
     }
   name[i] = '\000';
diff --git a/gdb/block.c b/gdb/block.c
index 1c343aa..a8075a1 100644
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -595,8 +595,7 @@ block_iterator_next (struct block_iterator *iterator)
 
 static struct symbol *
 block_iter_match_step (struct block_iterator *iterator,
-		       const char *name,
-		       symbol_compare_ftype *compare,
+		       const lookup_name_info &name,
 		       int first)
 {
   struct symbol *sym;
@@ -618,10 +617,10 @@ block_iter_match_step (struct block_iterator *iterator,
 	  block = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cust),
 				     iterator->which);
 	  sym = dict_iter_match_first (BLOCK_DICT (block), name,
-				       compare, &iterator->dict_iter);
+				       &iterator->dict_iter);
 	}
       else
-	sym = dict_iter_match_next (name, compare, &iterator->dict_iter);
+	sym = dict_iter_match_next (name, &iterator->dict_iter);
 
       if (sym != NULL)
 	return sym;
@@ -638,30 +637,27 @@ block_iter_match_step (struct block_iterator *iterator,
 
 struct symbol *
 block_iter_match_first (const struct block *block,
-			const char *name,
-			symbol_compare_ftype *compare,
+			const lookup_name_info &name,
 			struct block_iterator *iterator)
 {
   initialize_block_iterator (block, iterator);
 
   if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_match_first (block->dict, name, compare,
-				  &iterator->dict_iter);
+    return dict_iter_match_first (block->dict, name, &iterator->dict_iter);
 
-  return block_iter_match_step (iterator, name, compare, 1);
+  return block_iter_match_step (iterator, name, 1);
 }
 
 /* See block.h.  */
 
 struct symbol *
-block_iter_match_next (const char *name,
-		       symbol_compare_ftype *compare,
+block_iter_match_next (const lookup_name_info &name,
 		       struct block_iterator *iterator)
 {
   if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_match_next (name, compare, &iterator->dict_iter);
+    return dict_iter_match_next (name, &iterator->dict_iter);
 
-  return block_iter_match_step (iterator, name, compare, 0);
+  return block_iter_match_step (iterator, name, 0);
 }
 
 /* See block.h.
@@ -682,11 +678,13 @@ block_lookup_symbol (const struct block *block, const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   if (!BLOCK_FUNCTION (block))
     {
       struct symbol *other = NULL;
 
-      ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+      ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
 	{
 	  if (SYMBOL_DOMAIN (sym) == domain)
 	    return sym;
@@ -713,7 +711,7 @@ block_lookup_symbol (const struct block *block, const char *name,
 
       struct symbol *sym_found = NULL;
 
-      ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+      ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				     SYMBOL_DOMAIN (sym), domain))
@@ -738,14 +736,16 @@ block_lookup_symbol_primary (const struct block *block, const char *name,
   struct symbol *sym, *other;
   struct dict_iterator dict_iter;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   /* Verify BLOCK is STATIC_BLOCK or GLOBAL_BLOCK.  */
   gdb_assert (BLOCK_SUPERBLOCK (block) == NULL
 	      || BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL);
 
   other = NULL;
-  for (sym = dict_iter_match_first (block->dict, name, strcmp_iw, &dict_iter);
+  for (sym = dict_iter_match_first (block->dict, lookup_name, &dict_iter);
        sym != NULL;
-       sym = dict_iter_match_next (name, strcmp_iw, &dict_iter))
+       sym = dict_iter_match_next (lookup_name, &dict_iter))
     {
       if (SYMBOL_DOMAIN (sym) == domain)
 	return sym;
@@ -772,11 +772,13 @@ block_find_symbol (const struct block *block, const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   /* Verify BLOCK is STATIC_BLOCK or GLOBAL_BLOCK.  */
   gdb_assert (BLOCK_SUPERBLOCK (block) == NULL
 	      || BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL);
 
-  ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+  ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
     {
       /* MATCHER is deliberately called second here so that it never sees
 	 a non-domain-matching symbol.  */
diff --git a/gdb/block.h b/gdb/block.h
index 1741e52..0326c18 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -237,27 +237,22 @@ extern struct symbol *block_iterator_first (const struct block *block,
 extern struct symbol *block_iterator_next (struct block_iterator *iterator);
 
 /* Initialize ITERATOR to point at the first symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (which must use
-   the same conventions as strcmp_iw and be compatible with any
-   block hashing function), and return that first symbol, or NULL
-   if there are no such symbols.  */
+   SYMBOL_SEARCH_NAME matches NAME, and return that first symbol, or
+   NULL if there are no such symbols.  */
 
 extern struct symbol *block_iter_match_first (const struct block *block,
-					      const char *name,
-					      symbol_compare_ftype *compare,
+					      const lookup_name_info &name,
 					      struct block_iterator *iterator);
 
 /* Advance ITERATOR to point at the next symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (see
-   block_iter_match_first), or NULL if there are no more such symbols.
-   Don't call this if you've previously received NULL from 
+   SYMBOL_SEARCH_NAME matches NAME, or NULL if there are no more such
+   symbols.  Don't call this if you've previously received NULL from
    block_iterator_match_first or block_iterator_match_next on this
    iteration.  And don't call it unless ITERATOR was created by a
-   previous call to block_iter_match_first with the same NAME and COMPARE.  */
+   previous call to block_iter_match_first with the same NAME.  */
 
-extern struct symbol *block_iter_match_next (const char *name,
-					     symbol_compare_ftype *compare,
-					     struct block_iterator *iterator);
+extern struct symbol *block_iter_match_next
+  (const lookup_name_info &name, struct block_iterator *iterator);
 
 /* Search BLOCK for symbol NAME in DOMAIN.  */
 
@@ -316,14 +311,14 @@ extern int block_find_non_opaque_type_preferred (struct symbol *sym,
        (sym);						\
        (sym) = block_iterator_next (&(iter)))
 
-/* Macro to loop through all symbols with name NAME in BLOCK,
-   in no particular order.  ITER helps keep track of the iteration, and
-   must be a struct block_iterator.  SYM points to the current symbol.  */
+/* Macro to loop through all symbols in BLOCK with a name that matches
+   NAME, in no particular order.  ITER helps keep track of the
+   iteration, and must be a struct block_iterator.  SYM points to the
+   current symbol.  */
 
 #define ALL_BLOCK_SYMBOLS_WITH_NAME(block, name, iter, sym)		\
-  for ((sym) = block_iter_match_first ((block), (name),			\
-				       strcmp_iw, &(iter));		\
+  for ((sym) = block_iter_match_first ((block), (name), &(iter));	\
        (sym) != NULL;							\
-       (sym) = block_iter_match_next ((name), strcmp_iw, &(iter)))
+       (sym) = block_iter_match_next ((name), &(iter)))
 
 #endif /* BLOCK_H */
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 9749935..49077c7 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -869,7 +869,7 @@ extern const struct language_defn c_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &c_varobj_ops,
@@ -1014,7 +1014,7 @@ extern const struct language_defn cplus_language_defn =
   cp_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  cp_get_symbol_name_matcher,
   iterate_over_symbols,
   default_search_name_hash,
   &cplus_varobj_ops,
@@ -1068,7 +1068,7 @@ extern const struct language_defn asm_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
@@ -1122,7 +1122,7 @@ extern const struct language_defn minimal_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/completer.c b/gdb/completer.c
index 44a60ad..0835563 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -505,6 +505,7 @@ complete_files_symbols (completion_tracker &tracker,
     {
       collect_file_symbol_completion_matches (tracker,
 					      complete_symbol_mode::EXPRESSION,
+					      symbol_name_match_type::EXPRESSION,
 					      symbol_start, word,
 					      file_to_match);
       xfree (file_to_match);
@@ -515,6 +516,7 @@ complete_files_symbols (completion_tracker &tracker,
 
       collect_symbol_completion_matches (tracker,
 					 complete_symbol_mode::EXPRESSION,
+					 symbol_name_match_type::EXPRESSION,
 					 symbol_start, word);
       /* If text includes characters which cannot appear in a file
 	 name, they cannot be asking for completion on files.  */
@@ -557,6 +559,7 @@ complete_files_symbols (completion_tracker &tracker,
 	 on the entire text as a symbol.  */
       collect_symbol_completion_matches (tracker,
 					 complete_symbol_mode::EXPRESSION,
+					 symbol_name_match_type::EXPRESSION,
 					 orig_text, word);
     }
 }
@@ -1113,6 +1116,7 @@ symbol_completer (struct cmd_list_element *ignore,
 		  const char *text, const char *word)
 {
   collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
 				     text, word);
 }
 
diff --git a/gdb/completer.h b/gdb/completer.h
index fbfe4d5..df90822 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -68,6 +68,62 @@ struct match_list_displayer
    calls free on each element.  */
 typedef std::vector<gdb::unique_xmalloc_ptr<char>> completion_list;
 
+/* The result of a successful completion match.  When doing symbol
+   comparison, we use the symbol search name for the symbol name match
+   check, but the matched name that is shown to the user may be
+   different.  For example, Ada uses encoded names for lookup, but
+   then wants to decode the symbol name to show to the user, and also
+   in some cases wrap the matched name in "<sym>" (meaning we can't
+   always use the symbol's print name.  */
+
+class completion_match
+{
+public:
+  /* Get the completion match result.  See m_match/m_storage's
+     descriptions.  */
+  const char *match ()
+  { return m_match; }
+
+  /* Set the completion match result.  See m_match/m_storage's
+     descriptions.  */
+  void set_match (const char *match)
+  { m_match = match; }
+
+  /* Get temporary storage for generating a match result, dynamically.
+     The built string is only good until the next clear() call.  I.e.,
+     good until the next symbol comparison.  */
+  std::string &storage ()
+  { return m_storage; }
+
+  /* Prepare for another completion matching sequence.  */
+  void clear ()
+  {
+    m_match = NULL;
+    m_storage.clear ();
+  }
+
+private:
+  /* The completion match result.  This can either be a pointer into
+     M_STORAGE string, or it can be a pointer into the some other
+     string that outlives the completion matching sequence (usually, a
+     pointer to a symbol's name.  */
+  const char *m_match;
+
+  /* Storage a symbol comparison routine can use for generating a
+     match result, dynamically.  The built string is only good until
+     the next clear() call.  I.e., good until the next symbol
+     comparison.  */
+  std::string m_storage;
+};
+
+/* Convenience aggregate holding info returned by the symbol name
+   matching routines (see symbol_name_matcher_ftype).  */
+struct completion_match_result
+{
+  /* The completion match candidate.  */
+  completion_match match;
+};
+
 /* The final result of a completion that is handed over to either
    readline or the "completion" command (which pretends to be
    readline).  Mainly a wrapper for a readline-style match list array,
@@ -207,6 +263,18 @@ public:
      already have.  */
   bool completes_to_completion_word (const char *word);
 
+  /* Get a reference to the shared (between all the multiple symbol
+     name comparison calls) completion_match_result object, ready for
+     another symbol name match sequence.  */
+  completion_match_result &reset_completion_match_result ()
+  {
+    completion_match_result &res = m_completion_match_result;
+
+    /* Clear any previous match.  */
+    res.match.clear ();
+    return m_completion_match_result;
+  }
+
   /* True if we have any completion match recorded.  */
   bool have_completions () const
   { return !m_entries_vec.empty (); }
@@ -232,6 +300,13 @@ private:
      to hand over to readline.  */
   void recompute_lowest_common_denominator (const char *new_match);
 
+  /* Completion match outputs returned by the symbol name matching
+     routines (see symbol_name_matcher_ftype).  These results are only
+     valid for a single match call.  This is here in order to be able
+     to conveniently share the same storage among all the calls to the
+     symbol name matching routines.  */
+  completion_match_result m_completion_match_result;
+
   /* The completion matches found so far, in a vector.  */
   completion_list m_entries_vec;
 
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index df9a563..95e7cb8 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1189,7 +1189,9 @@ make_symbol_overload_list_block (const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
-  ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
+  ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
     overload_list_add_symbol (sym, name);
 }
 
@@ -1592,6 +1594,41 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
   return *demangled != NULL;
 }
 
+/* C++ symbol_name_matcher_ftype implementation.  */
+
+static bool
+cp_fq_symbol_name_matches (const char *symbol_search_name,
+			   const lookup_name_info &lookup_name,
+			   completion_match *match)
+{
+  /* Get the demangled name.  */
+  const std::string &name = lookup_name.cplus ().lookup_name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  if (strncmp_iw_with_mode (symbol_search_name,
+			    name.c_str (), name.size (),
+			    mode) == 0)
+    {
+      if (match != NULL)
+	match->set_match (symbol_search_name);
+      return true;
+    }
+
+  return false;
+}
+
+/* Implement the "la_get_symbol_name_matcher" language_defn method for
+   C++.  */
+
+symbol_name_matcher_ftype *
+cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
+{
+  return cp_fq_symbol_name_matches;
+}
+
 /* Don't allow just "maintenance cplus".  */
 
 static  void
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 37b281f..3a42cd6 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -107,6 +107,9 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
 extern struct type *cp_lookup_rtti_type (const char *name,
 					 struct block *block);
 
+extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
+  (const lookup_name_info &lookup_name);
+
 /* Functions/variables from cp-namespace.c.  */
 
 extern int cp_is_in_anonymous (const char *symbol_name);
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index b89b636..1834ccc 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -245,7 +245,7 @@ extern const struct language_defn d_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index 1be7e48..87e05d6 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -115,11 +115,9 @@ struct dict_vector
   struct symbol *(*iterator_next) (struct dict_iterator *iterator);
   /* Functions to iterate over symbols with a given name.  */
   struct symbol *(*iter_match_first) (const struct dictionary *dict,
-				      const char *name,
-				      symbol_compare_ftype *equiv,
+				      const lookup_name_info &name,
 				      struct dict_iterator *iterator);
-  struct symbol *(*iter_match_next) (const char *name,
-				     symbol_compare_ftype *equiv,
+  struct symbol *(*iter_match_next) (const lookup_name_info &name,
 				     struct dict_iterator *iterator);
   /* A size function, for maint print symtabs.  */
   int (*size) (const struct dictionary *dict);
@@ -239,12 +237,10 @@ static struct symbol *iterator_first_hashed (const struct dictionary *dict,
 static struct symbol *iterator_next_hashed (struct dict_iterator *iterator);
 
 static struct symbol *iter_match_first_hashed (const struct dictionary *dict,
-					       const char *name,
-					       symbol_compare_ftype *compare,
+					       const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
-static struct symbol *iter_match_next_hashed (const char *name,
-					      symbol_compare_ftype *compare,
+static struct symbol *iter_match_next_hashed (const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
 /* Functions only for DICT_HASHED.  */
@@ -269,12 +265,10 @@ static struct symbol *iterator_first_linear (const struct dictionary *dict,
 static struct symbol *iterator_next_linear (struct dict_iterator *iterator);
 
 static struct symbol *iter_match_first_linear (const struct dictionary *dict,
-					       const char *name,
-					       symbol_compare_ftype *compare,
+					       const lookup_name_info &name,
 					       struct dict_iterator *iterator);
 
-static struct symbol *iter_match_next_linear (const char *name,
-					      symbol_compare_ftype *compare,
+static struct symbol *iter_match_next_linear (const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
 static int size_linear (const struct dictionary *dict);
@@ -537,19 +531,18 @@ dict_iterator_next (struct dict_iterator *iterator)
 
 struct symbol *
 dict_iter_match_first (const struct dictionary *dict,
-		       const char *name, symbol_compare_ftype *compare,
+		       const lookup_name_info &name,
 		       struct dict_iterator *iterator)
 {
-  return (DICT_VECTOR (dict))->iter_match_first (dict, name,
-						 compare, iterator);
+  return (DICT_VECTOR (dict))->iter_match_first (dict, name, iterator);
 }
 
 struct symbol *
-dict_iter_match_next (const char *name, symbol_compare_ftype *compare,
+dict_iter_match_next (const lookup_name_info &name,
 		      struct dict_iterator *iterator)
 {
   return (DICT_VECTOR (DICT_ITERATOR_DICT (iterator)))
-    ->iter_match_next (name, compare, iterator);
+    ->iter_match_next (name, iterator);
 }
 
 int
@@ -640,13 +633,15 @@ iterator_hashed_advance (struct dict_iterator *iterator)
 }
 
 static struct symbol *
-iter_match_first_hashed (const struct dictionary *dict, const char *name,
-			 symbol_compare_ftype *compare,
+iter_match_first_hashed (const struct dictionary *dict,
+			 const lookup_name_info &name,
 			 struct dict_iterator *iterator)
 {
-  unsigned int hash_index
-    = (search_name_hash (DICT_LANGUAGE (dict)->la_language, name)
-       % DICT_HASHED_NBUCKETS (dict));
+  const language_defn *lang = DICT_LANGUAGE (dict);
+  unsigned int hash_index = (name.search_name_hash (lang->la_language)
+			     % DICT_HASHED_NBUCKETS (dict));
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
   struct symbol *sym;
 
   DICT_ITERATOR_DICT (iterator) = dict;
@@ -660,11 +655,8 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
        sym = sym->hash_next)
     {
       /* Warning: the order of arguments to compare matters!  */
-      if (compare (SYMBOL_SEARCH_NAME (sym), name) == 0)
-	{
-	  break;
-	}
-	
+      if (matches_name (SYMBOL_SEARCH_NAME (sym), name, NULL))
+	break;
     }
 
   DICT_ITERATOR_CURRENT (iterator) = sym;
@@ -672,16 +664,19 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
 }
 
 static struct symbol *
-iter_match_next_hashed (const char *name, symbol_compare_ftype *compare,
+iter_match_next_hashed (const lookup_name_info &name,
 			struct dict_iterator *iterator)
 {
+  const language_defn *lang = DICT_LANGUAGE (DICT_ITERATOR_DICT (iterator));
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
   struct symbol *next;
 
   for (next = DICT_ITERATOR_CURRENT (iterator)->hash_next;
        next != NULL;
        next = next->hash_next)
     {
-      if (compare (SYMBOL_SEARCH_NAME (next), name) == 0)
+      if (matches_name (SYMBOL_SEARCH_NAME (next), name, NULL))
 	break;
     }
 
@@ -874,27 +869,32 @@ iterator_next_linear (struct dict_iterator *iterator)
 
 static struct symbol *
 iter_match_first_linear (const struct dictionary *dict,
-			 const char *name, symbol_compare_ftype *compare,
+			 const lookup_name_info &name,
 			 struct dict_iterator *iterator)
 {
   DICT_ITERATOR_DICT (iterator) = dict;
   DICT_ITERATOR_INDEX (iterator) = -1;
 
-  return iter_match_next_linear (name, compare, iterator);
+  return iter_match_next_linear (name, iterator);
 }
 
 static struct symbol *
-iter_match_next_linear (const char *name, symbol_compare_ftype *compare,
+iter_match_next_linear (const lookup_name_info &name,
 			struct dict_iterator *iterator)
 {
   const struct dictionary *dict = DICT_ITERATOR_DICT (iterator);
+  const language_defn *lang = DICT_LANGUAGE (dict);
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
+
   int i, nsyms = DICT_LINEAR_NSYMS (dict);
   struct symbol *sym, *retval = NULL;
 
   for (i = DICT_ITERATOR_INDEX (iterator) + 1; i < nsyms; ++i)
     {
       sym = DICT_LINEAR_SYM (dict, i);
-      if (compare (SYMBOL_SEARCH_NAME (sym), name) == 0)
+
+      if (matches_name (SYMBOL_SEARCH_NAME (sym), name, NULL))
 	{
 	  retval = sym;
 	  break;
diff --git a/gdb/dictionary.h b/gdb/dictionary.h
index ef5fbed..17e1c58 100644
--- a/gdb/dictionary.h
+++ b/gdb/dictionary.h
@@ -132,8 +132,7 @@ extern struct symbol *dict_iterator_next (struct dict_iterator *iterator);
    if there are no such symbols.  */
 
 extern struct symbol *dict_iter_match_first (const struct dictionary *dict,
-					     const char *name,
-					     symbol_compare_ftype *compare,
+					     const lookup_name_info &name,
 					     struct dict_iterator *iterator);
 
 /* Advance ITERATOR to point at the next symbol in DICT whose
@@ -144,8 +143,7 @@ extern struct symbol *dict_iter_match_first (const struct dictionary *dict,
    iteration.  And don't call it unless ITERATOR was created by a
    previous call to dict_iter_match_first with the same NAME and COMPARE.  */
 
-extern struct symbol *dict_iter_match_next (const char *name,
-					    symbol_compare_ftype *compare,
+extern struct symbol *dict_iter_match_next (const lookup_name_info &name,
 					    struct dict_iterator *iterator);
 
 /* Return some notion of the size of the dictionary: the number of
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index c44a76e..f523326 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -3824,6 +3824,8 @@ dw2_lookup_symbol (struct objfile *objfile, int block_index,
 
   dw2_setup (objfile);
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   index = dwarf2_per_objfile->index_table;
 
   /* index is NULL if OBJF_READNOW.  */
@@ -3850,10 +3852,10 @@ dw2_lookup_symbol (struct objfile *objfile, int block_index,
 	     information (but NAME might contain it).  */
 
 	  if (sym != NULL
-	      && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	    return stab;
 	  if (with_opaque != NULL
-	      && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, lookup_name))
 	    stab_best = stab;
 
 	  /* Keep looking through other CUs.  */
@@ -3998,7 +4000,7 @@ dw2_map_matching_symbols (struct objfile *objfile,
 			  int global,
 			  int (*callback) (struct block *,
 					   struct symbol *, void *),
-			  void *data, symbol_compare_ftype *match,
+			  void *data, symbol_name_match_type match,
 			  symbol_compare_ftype *ordered_compare)
 {
   /* Currently unimplemented; used for Ada.  The function can be called if the
@@ -4006,10 +4008,97 @@ dw2_map_matching_symbols (struct objfile *objfile,
      does not look for non-Ada symbols this function should just return.  */
 }
 
+/* Symbol name matcher for .gdb_index names.
+
+   Symbol names in .gdb_index have a few particularities:
+
+   - There's not indication of which is the language of each symbol.
+
+     Since each language has its own symbol name matching algorithm,
+     and we don't know which language is the right one, we must match
+     each symbol against all languages.
+
+   - Symbol names in the index have no overload (parameter)
+     information. I.e., in C++, "foo(int)" and "foo(long)" both appear
+     as "foo" in the index, for example.
+
+     This means that the lookup names passed to the symbol name
+     matcher functions names must have no parameter information too
+     because (e.g.)  symbol search name "foo" does not match
+     lookup-name "foo(int)" [while swapping search name for lookup
+     name would match].
+*/
+class gdb_index_symbol_name_matcher
+{
+public:
+  /* Prepares the vector of comparison functions for LOOKUP_NAME.  */
+  gdb_index_symbol_name_matcher (const lookup_name_info &lookup_name);
+
+  /* Walk all the matcher routines and match SYMBOL_NAME against them.
+     Returns true if any matcher matches.  */
+  bool matches (const char *symbol_name);
+
+private:
+  /* A reference to the lookup name we're matching against.  */
+  const lookup_name_info &m_lookup_name;
+
+  /* A vector holding all the different symbol name matchers, for all
+     languages.  */
+  std::vector<symbol_name_matcher_ftype *> m_symbol_name_matcher_funcs;
+};
+
+gdb_index_symbol_name_matcher::gdb_index_symbol_name_matcher
+  (const lookup_name_info &lookup_name)
+    : m_lookup_name (lookup_name)
+{
+  /* Prepare the vector of comparison functions upfront, so avoid
+     doing the same work for each symbol.  Care is taken to avoid
+     matching with the same matcher twice if/when multiple languages
+     use the same matcher function.  */
+  auto &matchers = m_symbol_name_matcher_funcs;
+  matchers.reserve (nr_languages);
+
+  for (int i = 0; i < nr_languages; i++)
+    {
+      const language_defn *lang = language_def ((enum language) i);
+      if (lang->la_get_symbol_name_matcher != NULL)
+	{
+	  symbol_name_matcher_ftype *name_matcher
+	    = lang->la_get_symbol_name_matcher (m_lookup_name);
+
+	  /* Don't insert the same comparison routine twice.  Note
+	     that we do this linear walk instead of a cheaper sorted
+	     insert, or use a set or something like that, because
+	     relative order of function addresses is not stable.  This
+	     is not a problem in practice because the number of
+	     supported languages is low, and the cost here is tiny
+	     compared to the number of searches we'll do afterwards
+	     using this object.  */
+	  if (std::find (matchers.begin (), matchers.end (), name_matcher)
+	      == matchers.end ())
+	    matchers.push_back (name_matcher);
+	}
+    }
+  if (std::find (matchers.begin (), matchers.end (),
+		 default_symbol_name_matcher) == matchers.end ())
+    matchers.push_back (default_symbol_name_matcher);
+}
+
+bool
+gdb_index_symbol_name_matcher::matches (const char *symbol_name)
+{
+  for (auto matches_name : m_symbol_name_matcher_funcs)
+    if (matches_name (symbol_name, m_lookup_name, NULL))
+      return true;
+
+  return false;
+}
+
 static void
 dw2_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -4097,6 +4186,8 @@ dw2_expand_symtabs_matching
 	}
     }
 
+  gdb_index_symbol_name_matcher lookup_name_matcher (lookup_name);
+
   for (iter = 0; iter < index->symbol_table_slots; ++iter)
     {
       offset_type idx = 2 * iter;
@@ -4111,7 +4202,8 @@ dw2_expand_symtabs_matching
 
       name = index->constant_pool + MAYBE_SWAP (index->symbol_table[idx]);
 
-      if (!symbol_matcher (name))
+      if (!lookup_name_matcher.matches (name)
+	  || (symbol_matcher != NULL && !symbol_matcher (name)))
 	continue;
 
       /* The name was matched, now expand corresponding CUs that were
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index cfef64f..bbda645 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -231,10 +231,12 @@ f_word_break_characters (void)
 static void
 f_collect_symbol_completion_matches (completion_tracker &tracker,
 				     complete_symbol_mode mode,
+				     symbol_name_match_type compare_name,
 				     const char *text, const char *word,
 				     enum type_code code)
 {
   default_collect_symbol_completion_matches_break_on (tracker, mode,
+						      compare_name,
 						      text, word, ":", code);
 }
 
@@ -291,7 +293,7 @@ extern const struct language_defn f_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index befd937..9c6ae549 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -606,7 +606,7 @@ extern const struct language_defn go_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/language.c b/gdb/language.c
index 2e95c9e..6705a3b 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -709,6 +709,41 @@ default_get_string (struct value *value, gdb_byte **buffer, int *length,
   error (_("Getting a string is unsupported in this language."));
 }
 
+/* See language.h.  */
+
+bool
+default_symbol_name_matcher (const char *symbol_search_name,
+			     const lookup_name_info &lookup_name,
+			     completion_match *match)
+{
+  const std::string &name = lookup_name.name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
+			    mode) == 0)
+    {
+      if (match != NULL)
+	match->set_match (symbol_search_name);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* See language.h.  */
+
+symbol_name_matcher_ftype *
+language_get_symbol_name_matcher (const language_defn *lang,
+				  const lookup_name_info &lookup_name)
+{
+  if (lang->la_get_symbol_name_matcher != nullptr)
+    return lang->la_get_symbol_name_matcher (lookup_name);
+  return default_symbol_name_matcher;
+}
+
 /* Define the language that is no language.  */
 
 static int
@@ -847,7 +882,7 @@ const struct language_defn unknown_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
@@ -898,7 +933,7 @@ const struct language_defn auto_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/language.h b/gdb/language.h
index 9b5062e..90b3bb4 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -126,16 +126,6 @@ struct language_arch_info
   struct type *bool_type_default;
 };
 
-/* A pointer to a function expected to return nonzero if
-   SYMBOL_SEARCH_NAME matches the given LOOKUP_NAME.
-
-   SYMBOL_SEARCH_NAME should be a symbol's "search" name.
-   LOOKUP_NAME should be the name of an entity after it has been
-   transformed for lookup.  */
-
-typedef int (*symbol_name_cmp_ftype) (const char *symbol_search_name,
-				      const char *lookup_name);
-
 /* Structure tying together assorted information about a language.  */
 
 struct language_defn
@@ -331,6 +321,7 @@ struct language_defn
     void (*la_collect_symbol_completion_matches)
       (completion_tracker &tracker,
        complete_symbol_mode mode,
+       symbol_name_match_type match_type,
        const char *text,
        const char *word,
        enum type_code code);
@@ -367,13 +358,18 @@ struct language_defn
     gdb::unique_xmalloc_ptr<char> (*la_watch_location_expression)
          (struct type *type, CORE_ADDR addr);
 
-    /* Return a pointer to the function that should be used to match
-       a symbol name against LOOKUP_NAME. This is mostly for languages
-       such as Ada where the matching algorithm depends on LOOKUP_NAME.
+    /* Return a pointer to the function that should be used to match a
+       symbol name against LOOKUP_NAME, according to this language's
+       rules.  The matching algorithm depends on LOOKUP_NAME.  For
+       example, on Ada, the matching algorithm depends on the symbol
+       name (wild/full/verbatim matching), and on whether we're doing
+       a normal lookup or a completion match lookup.
 
-       This field may be NULL, in which case strcmp_iw will be used
-       to perform the matching.  */
-    symbol_name_cmp_ftype (*la_get_symbol_name_cmp) (const char *lookup_name);
+       This field may be NULL, in which case
+       default_symbol_name_matcher is used to perform the
+       matching.  */
+    symbol_name_matcher_ftype *(*la_get_symbol_name_matcher)
+      (const lookup_name_info &);
 
     /* Find all symbols in the current program space matching NAME in
        DOMAIN, according to this language's rules.
@@ -389,7 +385,8 @@ struct language_defn
        special processing here, 'iterate_over_symbols' should be
        used as the definition.  */
     void (*la_iterate_over_symbols)
-      (const struct block *block, const char *name, domain_enum domain,
+      (const struct block *block, const lookup_name_info &name,
+       domain_enum domain,
        gdb::function_view<symbol_found_callback_ftype> callback);
 
     /* Hash the given STRING.  Use default_search_name_hash if no
@@ -626,6 +623,18 @@ extern unsigned int default_search_name_hash (const char *search_name);
 void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 		   struct type **char_type, const char **charset);
 
+/* The default implementation of la_symbol_name_matcher.  Matches with
+   strncmp_iw.  */
+extern bool default_symbol_name_matcher
+  (const char *symbol_search_name,
+   const lookup_name_info &lookup_name,
+   completion_match *match);
+
+/* Get language LANG's symbol_name_matcher method for LOOKUP_NAME.
+   Returns default_symbol_name_matcher if not set.  */
+symbol_name_matcher_ftype *language_get_symbol_name_matcher
+  (const language_defn *lang, const lookup_name_info &lookup_name);
+
 /* The languages supported by GDB.  */
 
 extern const struct language_defn auto_language_defn;
diff --git a/gdb/linespec.c b/gdb/linespec.c
index c993c67..fa07534 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -326,7 +326,8 @@ typedef struct ls_parser linespec_parser;
 /* Prototypes for local functions.  */
 
 static void iterate_over_file_blocks
-  (struct symtab *symtab, const char *name, domain_enum domain,
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain,
    gdb::function_view<symbol_found_callback_ftype> callback);
 
 static void initialize_defaults (struct symtab **default_symtab,
@@ -361,6 +362,7 @@ static int symbol_to_sal (struct symtab_and_line *result,
 			  int funfirstline, struct symbol *sym);
 
 static void add_matching_symbols_to_info (const char *name,
+					  symbol_name_match_type name_match_type,
 					  struct collect_info *info,
 					  struct program_space *pspace);
 
@@ -1102,19 +1104,15 @@ maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
 
 static void
 iterate_over_all_matching_symtabs
-  (struct linespec_state *state, const char *name, const domain_enum domain,
+  (struct linespec_state *state,
+   const lookup_name_info &lookup_name,
+   const domain_enum name_domain,
    struct program_space *search_pspace, bool include_inline,
    gdb::function_view<symbol_found_callback_ftype> callback)
 {
   struct objfile *objfile;
   struct program_space *pspace;
 
-  /* The routine to be used for comparison.  */
-  symbol_name_cmp_ftype symbol_name_cmp
-    = (state->language->la_get_symbol_name_cmp != NULL
-       ? state->language->la_get_symbol_name_cmp (name)
-       : strcmp_iw);
-
   ALL_PSPACES (pspace)
   {
     if (search_pspace != NULL && search_pspace != pspace)
@@ -1129,21 +1127,17 @@ iterate_over_all_matching_symtabs
       struct compunit_symtab *cu;
 
       if (objfile->sf)
-	objfile->sf->qf->expand_symtabs_matching
-	  (objfile,
-	   NULL,
-	   [&] (const char *symbol_name)
-	   {
-	     return symbol_name_cmp (symbol_name, name) == 0;
-	   },
-	   NULL,
-	   ALL_DOMAIN);
+	objfile->sf->qf->expand_symtabs_matching (objfile,
+						  NULL,
+						  lookup_name,
+						  NULL, NULL,
+						  ALL_DOMAIN);
 
       ALL_OBJFILE_COMPUNITS (objfile, cu)
 	{
 	  struct symtab *symtab = COMPUNIT_FILETABS (cu);
 
-	  iterate_over_file_blocks (symtab, name, domain, callback);
+	  iterate_over_file_blocks (symtab, lookup_name, name_domain, callback);
 
 	  if (include_inline)
 	    {
@@ -1156,7 +1150,7 @@ iterate_over_all_matching_symtabs
 		{
 		  block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), i);
 		  state->language->la_iterate_over_symbols
-		    (block, name, domain, [&] (symbol *sym)
+		    (block, lookup_name, name_domain, [&] (symbol *sym)
 		     {
 		       /* Restrict calls to CALLBACK to symbols
 			  representing inline symbols only.  */
@@ -1193,8 +1187,8 @@ get_current_search_block (void)
 
 static void
 iterate_over_file_blocks
-  (struct symtab *symtab, const char *name, domain_enum domain,
-   gdb::function_view<symbol_found_callback_ftype> callback)
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain, gdb::function_view<symbol_found_callback_ftype> callback)
 {
   struct block *block;
 
@@ -1204,12 +1198,13 @@ iterate_over_file_blocks
     LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback);
 }
 
-/* A helper for find_method.  This finds all methods in type T which
-   match NAME.  It adds matching symbol names to RESULT_NAMES, and
-   adds T's direct superclasses to SUPERCLASSES.  */
+/* A helper for find_method.  This finds all methods in type T of
+   language T_LANG, which match NAME.  It adds matching symbol names
+   to RESULT_NAMES, and adds T's direct superclasses to
+   SUPERCLASSES.  */
 
 static void
-find_methods (struct type *t, const char *name,
+find_methods (struct type *t, enum language t_lang, const char *name,
 	      VEC (const_char_ptr) **result_names,
 	      VEC (typep) **superclasses)
 {
@@ -1222,6 +1217,9 @@ find_methods (struct type *t, const char *name,
   if (class_name)
     {
       int method_counter;
+      lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+      symbol_name_matcher_ftype *symbol_name_compare
+	= language_get_symbol_name_matcher (language_def (t_lang), lookup_name);
 
       t = check_typedef (t);
 
@@ -1246,7 +1244,7 @@ find_methods (struct type *t, const char *name,
 		method_name = dem_opname;
 	    }
 
-	  if (strcmp_iw (method_name, name) == 0)
+	  if (symbol_name_compare (method_name, lookup_name, NULL))
 	    {
 	      int field_counter;
 
@@ -2855,15 +2853,19 @@ linespec_complete_function (completion_tracker &tracker,
 			    const char *source_filename)
 {
   complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
+  symbol_name_match_type func_match_type = symbol_name_match_type::WILD;
 
   if (source_filename != NULL)
     {
-      collect_file_symbol_completion_matches (tracker, mode,
-					      function, function,
-					      source_filename);
+      collect_file_symbol_completion_matches (tracker, mode, func_match_type,
+					      function, function, source_filename);
     }
   else
-    collect_symbol_completion_matches (tracker, mode, function, function);
+    {
+      collect_symbol_completion_matches (tracker, mode, func_match_type,
+					 function, function);
+
+    }
 }
 
 /* Helper for complete_linespec to simplify it.  SOURCE_FILENAME is
@@ -3579,14 +3581,18 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
   struct symtab *elt;
   decode_compound_collector collector;
 
+  lookup_name_info lookup_name (class_name, symbol_name_match_type::FULL);
+
   for (ix = 0; VEC_iterate (symtab_ptr, file_symtabs, ix, elt); ++ix)
     {
       if (elt == NULL)
 	{
-	  iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN,
-					     NULL, false, collector);
-	  iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN,
-					     NULL, false, collector);
+	  iterate_over_all_matching_symtabs (state, lookup_name,
+					     STRUCT_DOMAIN, NULL, false,
+					     collector);
+	  iterate_over_all_matching_symtabs (state, lookup_name,
+					     VAR_DOMAIN, NULL, false,
+					     collector);
 	}
       else
 	{
@@ -3594,8 +3600,8 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
 	     been filtered out earlier.  */
 	  gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
 	  set_current_program_space (SYMTAB_PSPACE (elt));
-	  iterate_over_file_blocks (elt, class_name, STRUCT_DOMAIN, collector);
-	  iterate_over_file_blocks (elt, class_name, VAR_DOMAIN, collector);
+	  iterate_over_file_blocks (elt, lookup_name, STRUCT_DOMAIN, collector);
+	  iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN, collector);
 	}
     }
 
@@ -3676,12 +3682,14 @@ add_all_symbol_names_from_pspace (struct collect_info *info,
   const char *iter;
 
   for (ix = 0; VEC_iterate (const_char_ptr, names, ix, iter); ++ix)
-    add_matching_symbols_to_info (iter, info, pspace);
+    add_matching_symbols_to_info (iter,
+				  symbol_name_match_type::FULL,
+				  info, pspace);
 }
 
 static void
 find_superclass_methods (VEC (typep) *superclasses,
-			 const char *name,
+			 const char *name, enum language name_lang,
 			 VEC (const_char_ptr) **result_names)
 {
   int old_len = VEC_length (const_char_ptr, *result_names);
@@ -3697,7 +3705,7 @@ find_superclass_methods (VEC (typep) *superclasses,
 
       make_cleanup (VEC_cleanup (typep), &new_supers);
       for (ix = 0; VEC_iterate (typep, iter_classes, ix, t); ++ix)
-	find_methods (t, name, result_names, &new_supers);
+	find_methods (t, name_lang, name, result_names, &new_supers);
 
       if (VEC_length (const_char_ptr, *result_names) != old_len
 	  || VEC_empty (typep, new_supers))
@@ -3764,7 +3772,8 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs,
       gdb_assert (!pspace->executing_startup);
       set_current_program_space (pspace);
       t = check_typedef (SYMBOL_TYPE (sym));
-      find_methods (t, method_name, &result_names, &superclass_vec);
+      find_methods (t, SYMBOL_LANGUAGE (sym),
+		    method_name, &result_names, &superclass_vec);
 
       /* Handle all items from a single program space at once; and be
 	 sure not to miss the last batch.  */
@@ -3777,7 +3786,7 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs,
 	     this program space, consider superclasses.  */
 	  if (VEC_length (const_char_ptr, result_names) == last_result_len)
 	    find_superclass_methods (superclass_vec, method_name,
-				     &result_names);
+				     SYMBOL_LANGUAGE (sym), &result_names);
 
 	  /* We have a list of candidate symbol names, so now we
 	     iterate over the symbol tables looking for all
@@ -3944,7 +3953,8 @@ find_function_symbols (struct linespec_state *state,
     add_all_symbol_names_from_pspace (&info, state->search_pspace,
 				      symbol_names);
   else
-    add_matching_symbols_to_info (name, &info, state->search_pspace);
+    add_matching_symbols_to_info (name, symbol_name_match_type::WILD,
+				  &info, state->search_pspace);
 
   do_cleanups (cleanup);
 
@@ -3971,28 +3981,10 @@ find_function_symbols (struct linespec_state *state,
 static void
 find_linespec_symbols (struct linespec_state *state,
 		       VEC (symtab_ptr) *file_symtabs,
-		       const char *name,
+		       const char *lookup_name,
 		       VEC (symbolp) **symbols,
 		       VEC (bound_minimal_symbol_d) **minsyms)
 {
-  demangle_result_storage demangle_storage;
-  std::string ada_lookup_storage;
-  const char *lookup_name;
-
-  if (state->language->la_language == language_ada)
-    {
-      /* In Ada, the symbol lookups are performed using the encoded
-         name rather than the demangled name.  */
-      ada_lookup_storage = ada_name_for_lookup (name);
-      lookup_name = ada_lookup_storage.c_str ();
-    }
-  else
-    {
-      lookup_name = demangle_for_lookup (name,
-					 state->language->la_language,
-					 demangle_storage);
-    }
-
   std::string canon = cp_canonicalize_string_no_typedefs (lookup_name);
   if (!canon.empty ())
     lookup_name = canon.c_str ();
@@ -4472,7 +4464,8 @@ add_minsym (struct minimal_symbol *minsym, void *d)
    restrict results to the given SYMTAB.  */
 
 static void
-search_minsyms_for_name (struct collect_info *info, const char *name,
+search_minsyms_for_name (struct collect_info *info,
+			 const lookup_name_info &name,
 			 struct program_space *search_pspace,
 			 struct symtab *symtab)
 {
@@ -4514,8 +4507,7 @@ search_minsyms_for_name (struct collect_info *info, const char *name,
 	{
 	  set_current_program_space (SYMTAB_PSPACE (symtab));
 	  local.objfile = SYMTAB_OBJFILE(symtab);
-	  iterate_over_minimal_symbols (local.objfile, name, add_minsym,
-					&local);
+	  iterate_over_minimal_symbols (local.objfile, name, add_minsym, &local);
 	}
     }
 
@@ -4557,20 +4549,24 @@ search_minsyms_for_name (struct collect_info *info, const char *name,
 
 static void
 add_matching_symbols_to_info (const char *name,
+			      symbol_name_match_type name_match_type,
 			      struct collect_info *info,
 			      struct program_space *pspace)
 {
   int ix;
   struct symtab *elt;
 
+  lookup_name_info lookup_name (name, name_match_type);
+
   for (ix = 0; VEC_iterate (symtab_ptr, info->file_symtabs, ix, elt); ++ix)
     {
       if (elt == NULL)
 	{
-	  iterate_over_all_matching_symtabs (info->state, name, VAR_DOMAIN,
+	  iterate_over_all_matching_symtabs (info->state, lookup_name,
+					     VAR_DOMAIN,
 					     pspace, true, [&] (symbol *sym)
 	    { return info->add_symbol (sym); });
-	  search_minsyms_for_name (info, name, pspace, NULL);
+	  search_minsyms_for_name (info, lookup_name, pspace, NULL);
 	}
       else if (pspace == NULL || pspace == SYMTAB_PSPACE (elt))
 	{
@@ -4580,7 +4576,8 @@ add_matching_symbols_to_info (const char *name,
 	     been filtered out earlier.  */
 	  gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
 	  set_current_program_space (SYMTAB_PSPACE (elt));
-	  iterate_over_file_blocks (elt, name, VAR_DOMAIN, [&] (symbol *sym)
+	  iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN,
+				    [&] (symbol *sym)
 	    { return info->add_symbol (sym); });
 
 	  /* If no new symbols were found in this iteration and this symtab
@@ -4589,7 +4586,7 @@ add_matching_symbols_to_info (const char *name,
 	     this case.  */
 	  if (prev_len == VEC_length (symbolp, info->result.symbols)
 	      && elt->language == language_asm)
-	    search_minsyms_for_name (info, name, pspace, elt);
+	    search_minsyms_for_name (info, lookup_name, pspace, elt);
 	}
     }
 }
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 66ac34c..fbda2be 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -394,7 +394,7 @@ extern const struct language_defn m2_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index c93eaa3..842ae07 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -51,6 +51,7 @@
 #include "language.h"
 #include "cli/cli-utils.h"
 #include "symbol.h"
+#include <algorithm>
 
 /* Accumulate the minimal symbols for each objfile in bunches of BUNCH_SIZE.
    At the end, copy them all into one newly allocated location on an objfile's
@@ -114,15 +115,140 @@ add_minsym_to_hash_table (struct minimal_symbol *sym,
    TABLE.  */
 static void
 add_minsym_to_demangled_hash_table (struct minimal_symbol *sym,
-                                  struct minimal_symbol **table)
+				    struct objfile *objfile)
 {
   if (sym->demangled_hash_next == NULL)
     {
-      unsigned int hash = msymbol_hash_iw (MSYMBOL_SEARCH_NAME (sym))
-	% MINIMAL_SYMBOL_HASH_SIZE;
+      struct minimal_symbol **table
+	= objfile->per_bfd->msymbol_demangled_hash;
+      unsigned int hash_index;
+      unsigned int hash;
+
+      hash = search_name_hash (MSYMBOL_LANGUAGE (sym),
+			       MSYMBOL_SEARCH_NAME (sym));
+
+      auto &vec = objfile->per_bfd->demangled_hash_languages;
+      auto it = std::lower_bound (vec.begin (), vec.end (),
+				  MSYMBOL_LANGUAGE (sym));
+      if (it == vec.end () || *it != MSYMBOL_LANGUAGE (sym))
+	vec.insert (it, MSYMBOL_LANGUAGE (sym));
+
+      hash_index = hash % MINIMAL_SYMBOL_HASH_SIZE;
+      sym->demangled_hash_next = table[hash_index];
+      table[hash_index] = sym;
+    }
+}
 
-      sym->demangled_hash_next = table[hash];
-      table[hash] = sym;
+/* Worker object for lookup_minimal_symbol.  Stores temporary results
+   while walking the symbol tables.  */
+
+struct found_minimal_symbols
+{
+  /* External symbols are best.  */
+  bound_minimal_symbol external_symbol {};
+
+  /* File-local symbols are next best.  */
+  bound_minimal_symbol file_symbol {};
+
+  /* Symbols for shared library trampolines are next best.  */
+  bound_minimal_symbol trampoline_symbol {};
+
+  /* Called when a symbol name matches.  Check if the minsym is a
+     better type than what we had already found, and record it in one
+     of the members fields if so.  Returns true if we already have the
+     real symbol.  */
+  bool maybe_collect (const char *sfile, objfile *objf,
+		      minimal_symbol *msymbol);
+};
+
+bool
+found_minimal_symbols::maybe_collect (const char *sfile,
+				      struct objfile *objfile,
+				      minimal_symbol *msymbol)
+{
+  switch (MSYMBOL_TYPE (msymbol))
+    {
+    case mst_file_text:
+    case mst_file_data:
+    case mst_file_bss:
+      if (sfile == NULL
+	  || filename_cmp (msymbol->filename, sfile) == 0)
+	{
+	  file_symbol.minsym = msymbol;
+	  file_symbol.objfile = objfile;
+	}
+      break;
+
+    case mst_solib_trampoline:
+
+      /* If a trampoline symbol is found, we prefer to keep
+	 looking for the *real* symbol.  If the actual symbol
+	 is not found, then we'll use the trampoline
+	 entry.  */
+      if (trampoline_symbol.minsym == NULL)
+	{
+	  trampoline_symbol.minsym = msymbol;
+	  trampoline_symbol.objfile = objfile;
+	}
+      break;
+
+    case mst_unknown:
+    default:
+      external_symbol.minsym = msymbol;
+      external_symbol.objfile = objfile;
+      /* We have the real symbol.  No use looking further.  */
+      return true;
+    }
+
+  /* Keep looking.  */
+  return false;
+}
+
+/* Walk the mangled name hash table, and pass each symbol whose name
+   matches LOOKUP_NAME according to NAMECMP to RES.  */
+
+static void
+lookup_minimal_symbol_mangled (const char *lookup_name,
+			       const char *sfile,
+			       struct objfile *objfile,
+			       struct minimal_symbol **table,
+			       unsigned int hash,
+			       int (*namecmp) (const char *, const char *),
+			       found_minimal_symbols &found)
+{
+  for (minimal_symbol *msymbol = table[hash];
+       msymbol != NULL;
+       msymbol = msymbol->hash_next)
+    {
+      const char *symbol_name = MSYMBOL_LINKAGE_NAME (msymbol);
+
+      if (namecmp (symbol_name, lookup_name) == 0
+	  && found.maybe_collect (sfile, objfile, msymbol))
+	return;
+    }
+}
+
+/* Walk the demangled name hash table, and pass each symbol whose name
+   matches LOOKUP_NAME according to MATCHER to RES.  */
+
+static void
+lookup_minimal_symbol_demangled (const lookup_name_info &lookup_name,
+				 const char *sfile,
+				 struct objfile *objfile,
+				 struct minimal_symbol **table,
+				 unsigned int hash,
+				 symbol_name_matcher_ftype *matcher,
+				 found_minimal_symbols &found)
+{
+  for (minimal_symbol *msymbol = table[hash];
+       msymbol != NULL;
+       msymbol = msymbol->demangled_hash_next)
+    {
+      const char *symbol_name = MSYMBOL_SEARCH_NAME (msymbol);
+
+      if (matcher (symbol_name, lookup_name, NULL)
+	  && found.maybe_collect (sfile, objfile, msymbol))
+	return;
     }
 }
 
@@ -151,32 +277,22 @@ lookup_minimal_symbol (const char *name, const char *sfile,
 		       struct objfile *objf)
 {
   struct objfile *objfile;
-  struct bound_minimal_symbol found_symbol = { NULL, NULL };
-  struct bound_minimal_symbol found_file_symbol = { NULL, NULL };
-  struct bound_minimal_symbol trampoline_symbol = { NULL, NULL };
+  found_minimal_symbols found;
 
-  unsigned int hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  unsigned int dem_hash = msymbol_hash_iw (name) % MINIMAL_SYMBOL_HASH_SIZE;
+  unsigned int mangled_hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
 
-  const char *modified_name = name;
+  auto *mangled_cmp
+    = (case_sensitivity == case_sensitive_on
+       ? strcmp
+       : strcasecmp);
 
   if (sfile != NULL)
     sfile = lbasename (sfile);
 
-  /* For C++, canonicalize the input name.  */
-  std::string modified_name_storage;
-  if (current_language->la_language == language_cplus)
-    {
-      std::string cname = cp_canonicalize_string (name);
-      if (!cname.empty ())
-	{
-	  std::swap (modified_name_storage, cname);
-	  modified_name = modified_name_storage.c_str ();
-	}
-    }
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
   for (objfile = object_files;
-       objfile != NULL && found_symbol.minsym == NULL;
+       objfile != NULL && found.external_symbol.minsym == NULL;
        objfile = objfile->next)
     {
       struct minimal_symbol *msymbol;
@@ -184,131 +300,95 @@ lookup_minimal_symbol (const char *name, const char *sfile,
       if (objf == NULL || objf == objfile
 	  || objf == objfile->separate_debug_objfile_backlink)
 	{
+	  if (symbol_lookup_debug)
+	    {
+	      fprintf_unfiltered (gdb_stdlog,
+				  "lookup_minimal_symbol (%s, %s, %s)\n",
+				  name, sfile != NULL ? sfile : "NULL",
+				  objfile_debug_name (objfile));
+	    }
+
 	  /* Do two passes: the first over the ordinary hash table,
 	     and the second over the demangled hash table.  */
-        int pass;
-
-	if (symbol_lookup_debug)
-	  {
-	    fprintf_unfiltered (gdb_stdlog,
-				"lookup_minimal_symbol (%s, %s, %s)\n",
-				name, sfile != NULL ? sfile : "NULL",
-				objfile_debug_name (objfile));
-	  }
+	  lookup_minimal_symbol_mangled (name, sfile, objfile,
+					 objfile->per_bfd->msymbol_hash,
+					 mangled_hash, mangled_cmp, found);
 
-        for (pass = 1; pass <= 2 && found_symbol.minsym == NULL; pass++)
+	  /* If not found, try the demangled hash table.  */
+	  if (found.external_symbol.minsym == NULL)
 	    {
-            /* Select hash list according to pass.  */
-            if (pass == 1)
-              msymbol = objfile->per_bfd->msymbol_hash[hash];
-            else
-              msymbol = objfile->per_bfd->msymbol_demangled_hash[dem_hash];
-
-            while (msymbol != NULL && found_symbol.minsym == NULL)
+	      /* Once for each language in the demangled hash names
+		 table (usually just zero or one).  */
+	      for (auto lang : objfile->per_bfd->demangled_hash_languages)
 		{
-		  int match;
-
-		  if (pass == 1)
-		    {
-		      int (*cmp) (const char *, const char *);
-
-		      cmp = (case_sensitivity == case_sensitive_on
-		             ? strcmp : strcasecmp);
-		      match = cmp (MSYMBOL_LINKAGE_NAME (msymbol),
-				   modified_name) == 0;
-		    }
-		  else
-		    {
-		      /* The function respects CASE_SENSITIVITY.  */
-		      match = MSYMBOL_MATCHES_SEARCH_NAME (msymbol,
-							  modified_name);
-		    }
-
-		  if (match)
-		    {
-                    switch (MSYMBOL_TYPE (msymbol))
-                      {
-                      case mst_file_text:
-                      case mst_file_data:
-                      case mst_file_bss:
-                        if (sfile == NULL
-			    || filename_cmp (msymbol->filename, sfile) == 0)
-			  {
-			    found_file_symbol.minsym = msymbol;
-			    found_file_symbol.objfile = objfile;
-			  }
-                        break;
-
-                      case mst_solib_trampoline:
-
-                        /* If a trampoline symbol is found, we prefer to
-                           keep looking for the *real* symbol.  If the
-                           actual symbol is not found, then we'll use the
-                           trampoline entry.  */
-                        if (trampoline_symbol.minsym == NULL)
-			  {
-			    trampoline_symbol.minsym = msymbol;
-			    trampoline_symbol.objfile = objfile;
-			  }
-                        break;
-
-                      case mst_unknown:
-                      default:
-                        found_symbol.minsym = msymbol;
-			found_symbol.objfile = objfile;
-                        break;
-                      }
-		    }
-
-                /* Find the next symbol on the hash chain.  */
-                if (pass == 1)
-                  msymbol = msymbol->hash_next;
-                else
-                  msymbol = msymbol->demangled_hash_next;
+		  unsigned int hash
+		    = (lookup_name.search_name_hash (lang)
+		       % MINIMAL_SYMBOL_HASH_SIZE);
+
+		  symbol_name_matcher_ftype *match
+		    = language_get_symbol_name_matcher (language_def (lang),
+							lookup_name);
+		  struct minimal_symbol **msymbol_demangled_hash
+		    = objfile->per_bfd->msymbol_demangled_hash;
+
+		  lookup_minimal_symbol_demangled (lookup_name, sfile, objfile,
+						   msymbol_demangled_hash,
+						   hash, match, found);
+
+		  if (found.external_symbol.minsym != NULL)
+		    break;
 		}
 	    }
 	}
     }
 
   /* External symbols are best.  */
-  if (found_symbol.minsym != NULL)
+  if (found.external_symbol.minsym != NULL)
     {
       if (symbol_lookup_debug)
 	{
+	  minimal_symbol *minsym = found.external_symbol.minsym;
+
 	  fprintf_unfiltered (gdb_stdlog,
-			      "lookup_minimal_symbol (...) = %s"
-			      " (external)\n",
-			      host_address_to_string (found_symbol.minsym));
+			      "lookup_minimal_symbol (...) = %s (external)\n",
+			      host_address_to_string (minsym));
 	}
-      return found_symbol;
+      return found.external_symbol;
     }
 
   /* File-local symbols are next best.  */
-  if (found_file_symbol.minsym != NULL)
+  if (found.file_symbol.minsym != NULL)
     {
       if (symbol_lookup_debug)
 	{
+	  minimal_symbol *minsym = found.file_symbol.minsym;
+
 	  fprintf_unfiltered (gdb_stdlog,
-			      "lookup_minimal_symbol (...) = %s"
-			      " (file-local)\n",
-			      host_address_to_string
-			        (found_file_symbol.minsym));
+			      "lookup_minimal_symbol (...) = %s (file-local)\n",
+			      host_address_to_string (minsym));
 	}
-      return found_file_symbol;
+      return found.file_symbol;
     }
 
   /* Symbols for shared library trampolines are next best.  */
-  if (symbol_lookup_debug)
+  if (found.trampoline_symbol.minsym != NULL)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "lookup_minimal_symbol (...) = %s%s\n",
-			  trampoline_symbol.minsym != NULL
-			  ? host_address_to_string (trampoline_symbol.minsym)
-			  : "NULL",
-			  trampoline_symbol.minsym != NULL
-			  ? " (trampoline)" : "");
+      if (symbol_lookup_debug)
+	{
+	  minimal_symbol *minsym = found.trampoline_symbol.minsym;
+
+	  fprintf_unfiltered (gdb_stdlog,
+			      "lookup_minimal_symbol (...) = %s (trampoline)\n",
+			      host_address_to_string (minsym));
+	}
+
+      return found.trampoline_symbol;
     }
-  return trampoline_symbol;
+
+  /* Not found.  */
+  if (symbol_lookup_debug)
+    fprintf_unfiltered (gdb_stdlog, "lookup_minimal_symbol (...) = NULL\n");
+  return {};
 }
 
 /* See minsyms.h.  */
@@ -337,34 +417,47 @@ find_minimal_symbol_address (const char *name, CORE_ADDR *addr,
 /* See minsyms.h.  */
 
 void
-iterate_over_minimal_symbols (struct objfile *objf, const char *name,
+iterate_over_minimal_symbols (struct objfile *objf,
+			      const lookup_name_info &lookup_name,
 			      void (*callback) (struct minimal_symbol *,
 						void *),
 			      void *user_data)
 {
-  unsigned int hash;
-  struct minimal_symbol *iter;
-  int (*cmp) (const char *, const char *);
 
   /* The first pass is over the ordinary hash table.  */
-  hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  iter = objf->per_bfd->msymbol_hash[hash];
-  cmp = (case_sensitivity == case_sensitive_on ? strcmp : strcasecmp);
-  while (iter)
     {
-      if (cmp (MSYMBOL_LINKAGE_NAME (iter), name) == 0)
-	(*callback) (iter, user_data);
-      iter = iter->hash_next;
+      const char *name = lookup_name.name ().c_str ();
+      unsigned int hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
+      auto *mangled_cmp
+	= (case_sensitivity == case_sensitive_on
+	   ? strcmp
+	   : strcasecmp);
+
+      for (minimal_symbol *iter = objf->per_bfd->msymbol_hash[hash];
+	   iter != NULL;
+	   iter = iter->hash_next)
+	{
+	  if (mangled_cmp (MSYMBOL_LINKAGE_NAME (iter), name) == 0)
+	    (*callback) (iter, user_data);
+	}
     }
 
-  /* The second pass is over the demangled table.  */
-  hash = msymbol_hash_iw (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  iter = objf->per_bfd->msymbol_demangled_hash[hash];
-  while (iter)
+  /* The second pass is over the demangled table.  Once for each
+     language in the demangled hash names table (usually just zero or
+     one).  */
+  for (auto lang : objf->per_bfd->demangled_hash_languages)
     {
-      if (MSYMBOL_MATCHES_SEARCH_NAME (iter, name))
-	(*callback) (iter, user_data);
-      iter = iter->demangled_hash_next;
+      const language_defn *lang_def = language_def (lang);
+      symbol_name_matcher_ftype *name_match
+	= language_get_symbol_name_matcher (lang_def, lookup_name);
+
+      unsigned int hash
+	= lookup_name.search_name_hash (lang) % MINIMAL_SYMBOL_HASH_SIZE;
+      for (minimal_symbol *iter = objf->per_bfd->msymbol_demangled_hash[hash];
+	   iter != NULL;
+	   iter = iter->demangled_hash_next)
+	if (name_match (MSYMBOL_SEARCH_NAME (iter), lookup_name, NULL))
+	  (*callback) (iter, user_data);
     }
 }
 
@@ -1170,8 +1263,7 @@ build_minimal_symbol_hash_tables (struct objfile *objfile)
 
       msym->demangled_hash_next = 0;
       if (MSYMBOL_SEARCH_NAME (msym) != MSYMBOL_LINKAGE_NAME (msym))
-	add_minsym_to_demangled_hash_table (msym,
-                                            objfile->per_bfd->msymbol_demangled_hash);
+	add_minsym_to_demangled_hash_table (msym, objfile);
     }
 }
 
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index b82a22a..c4a1d21 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -258,7 +258,7 @@ struct bound_minimal_symbol lookup_minimal_symbol_by_pc (CORE_ADDR);
    USER_DATA as arguments.  */
 
 void iterate_over_minimal_symbols (struct objfile *objf,
-				   const char *name,
+				   const lookup_name_info &name,
 				   void (*callback) (struct minimal_symbol *,
 						     void *),
 				   void *user_data);
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 4dbc7f2..22ae16e 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -403,7 +403,7 @@ extern const struct language_defn objc_language_defn = {
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 3260425..44e6d9d 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -27,6 +27,7 @@
 #include "progspace.h"
 #include "registry.h"
 #include "gdb_bfd.h"
+#include <vector>
 
 struct bcache;
 struct htab;
@@ -265,6 +266,12 @@ struct objfile_per_bfd_storage
      demangled names.  */
 
   minimal_symbol *msymbol_demangled_hash[MINIMAL_SYMBOL_HASH_SIZE] {};
+
+  /* All the different languages of symbols found in the demangled
+     hash table.  A flat/vector-based map is more efficient than a map
+     or hash table here, since this will only usually contain one
+     entry.  */
+  std::vector<enum language> demangled_hash_languages;
 };
 
 /* Master structure for keeping track of each file from which
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index e5aff0d..a60726c 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1083,7 +1083,7 @@ extern const struct language_defn opencl_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 2dca923..e93c15b 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -454,7 +454,7 @@ extern const struct language_defn pascal_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_compare_symbol_for_completion */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 4077fb3..b853266 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -45,7 +45,7 @@ static struct partial_symbol *match_partial_symbol (struct objfile *,
 						    struct partial_symtab *,
 						    int,
 						    const char *, domain_enum,
-						    symbol_compare_ftype *,
+						    symbol_name_match_type,
 						    symbol_compare_ftype *);
 
 static struct partial_symbol *lookup_partial_symbol (struct objfile *,
@@ -507,6 +507,8 @@ psym_lookup_symbol (struct objfile *objfile,
   const int psymtab_index = (block_index == GLOBAL_BLOCK ? 1 : 0);
   struct compunit_symtab *stab_best = NULL;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
   {
     if (!ps->readin && lookup_partial_symbol (objfile, ps, name,
@@ -529,10 +531,10 @@ psym_lookup_symbol (struct objfile *objfile,
 	   information (but NAME might contain it).  */
 
 	if (sym != NULL
-	    && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
+	    && SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	  return stab;
 	if (with_opaque != NULL
-	    && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
+	    && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, lookup_name))
 	  stab_best = stab;
 
 	/* Keep looking through other psymtabs.  */
@@ -554,7 +556,7 @@ static struct partial_symbol *
 match_partial_symbol (struct objfile *objfile,
 		      struct partial_symtab *pst, int global,
 		      const char *name, domain_enum domain,
-		      symbol_compare_ftype *match,
+		      symbol_name_match_type match_type,
 		      symbol_compare_ftype *ordered_compare)
 {
   struct partial_symbol **start, **psym;
@@ -563,7 +565,10 @@ match_partial_symbol (struct objfile *objfile,
   int do_linear_search = 1;
 
   if (length == 0)
-      return NULL;
+    return NULL;
+
+  lookup_name_info lookup_name (name, match_type);
+
   start = (global ?
 	   objfile->global_psymbols.list + pst->globals_offset :
 	   objfile->static_psymbols.list + pst->statics_offset);
@@ -592,12 +597,18 @@ match_partial_symbol (struct objfile *objfile,
 	}
       gdb_assert (top == bottom);
 
-      while (top <= real_top
-	     && match (SYMBOL_SEARCH_NAME (*top), name) == 0)
+      while (top <= real_top)
 	{
-	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
-				     SYMBOL_DOMAIN (*top), domain))
-	    return *top;
+	  const language_defn *lang = language_def (SYMBOL_LANGUAGE (*top));
+	  symbol_name_matcher_ftype *name_match
+	    = language_get_symbol_name_matcher (lang, lookup_name);
+
+	  if (name_match (SYMBOL_SEARCH_NAME (*top), lookup_name, NULL))
+	    {
+	      if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
+					 SYMBOL_DOMAIN (*top), domain))
+		return *top;
+	    }
 	  top++;
 	}
     }
@@ -610,9 +621,17 @@ match_partial_symbol (struct objfile *objfile,
       for (psym = start; psym < start + length; psym++)
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*psym),
-				     SYMBOL_DOMAIN (*psym), domain)
-	      && match (SYMBOL_SEARCH_NAME (*psym), name) == 0)
-	    return *psym;
+				     SYMBOL_DOMAIN (*psym), domain))
+	    {
+	      const language_defn *lang
+		= language_def (SYMBOL_LANGUAGE (*psym));
+	      symbol_name_matcher_ftype *name_match
+		= language_get_symbol_name_matcher (lang, lookup_name);
+
+	      if (name_match (SYMBOL_SEARCH_NAME (*psym), lookup_name,
+			      NULL))
+		return *psym;
+	    }
 	}
     }
 
@@ -671,6 +690,9 @@ lookup_partial_symbol (struct objfile *objfile,
 
   search_name = psymtab_search_name (name);
   cleanup = make_cleanup (xfree, search_name);
+
+  lookup_name_info lookup_name (search_name, symbol_name_match_type::FULL);
+
   start = (global ?
 	   objfile->global_psymbols.list + pst->globals_offset :
 	   objfile->static_psymbols.list + pst->statics_offset);
@@ -710,13 +732,13 @@ lookup_partial_symbol (struct objfile *objfile,
 
       /* For `case_sensitivity == case_sensitive_off' strcmp_iw_ordered will
 	 search more exactly than what matches SYMBOL_MATCHES_SEARCH_NAME.  */
-      while (top >= start && SYMBOL_MATCHES_SEARCH_NAME (*top, search_name))
+      while (top >= start && SYMBOL_MATCHES_SEARCH_NAME (*top, lookup_name))
 	top--;
 
       /* Fixup to have a symbol which matches SYMBOL_MATCHES_SEARCH_NAME.  */
       top++;
 
-      while (top <= real_top && SYMBOL_MATCHES_SEARCH_NAME (*top, search_name))
+      while (top <= real_top && SYMBOL_MATCHES_SEARCH_NAME (*top, lookup_name))
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
 				     SYMBOL_DOMAIN (*top), domain))
@@ -737,7 +759,7 @@ lookup_partial_symbol (struct objfile *objfile,
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*psym),
 				     SYMBOL_DOMAIN (*psym), domain)
-	      && SYMBOL_MATCHES_SEARCH_NAME (*psym, search_name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (*psym, lookup_name))
 	    {
 	      do_cleanups (cleanup);
 	      return *psym;
@@ -1231,13 +1253,16 @@ static int
 map_block (const char *name, domain_enum domain, struct objfile *objfile,
 	   struct block *block,
 	   int (*callback) (struct block *, struct symbol *, void *),
-	   void *data, symbol_compare_ftype *match)
+	   void *data, symbol_name_match_type match)
 {
   struct block_iterator iter;
   struct symbol *sym;
 
-  for (sym = block_iter_match_first (block, name, match, &iter);
-       sym != NULL; sym = block_iter_match_next (name, match, &iter))
+  lookup_name_info lookup_name (name, match);
+
+  for (sym = block_iter_match_first (block, lookup_name, &iter);
+       sym != NULL;
+       sym = block_iter_match_next (lookup_name, &iter))
     {
       if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				 SYMBOL_DOMAIN (sym), domain))
@@ -1260,7 +1285,7 @@ psym_map_matching_symbols (struct objfile *objfile,
 			   int (*callback) (struct block *,
 					    struct symbol *, void *),
 			   void *data,
-			   symbol_compare_ftype *match,
+			   symbol_name_match_type match,
 			   symbol_compare_ftype *ordered_compare)
 {
   const int block_kind = global ? GLOBAL_BLOCK : STATIC_BLOCK;
@@ -1288,6 +1313,16 @@ psym_map_matching_symbols (struct objfile *objfile,
     }
 }
 
+static bool
+psymbol_name_matches (partial_symbol *psym,
+		      const lookup_name_info &lookup_name)
+{
+  const language_defn *lang = language_def (SYMBOL_LANGUAGE (psym));
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (lang, lookup_name);
+  return name_match (SYMBOL_SEARCH_NAME (psym), lookup_name, NULL);
+}
+
 /* A helper for psym_expand_symtabs_matching that handles searching
    included psymtabs.  This returns true if a symbol is found, and
    false otherwise.  It also updates the 'searched_flag' on the
@@ -1295,7 +1330,8 @@ psym_map_matching_symbols (struct objfile *objfile,
 
 static bool
 recursively_search_psymtabs
-  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain kind,
+  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain domain,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> sym_matcher)
 {
   struct partial_symbol **psym;
@@ -1318,7 +1354,8 @@ recursively_search_psymtabs
 	continue;
 
       r = recursively_search_psymtabs (ps->dependencies[i],
-				       objfile, kind, sym_matcher);
+				       objfile, domain, lookup_name,
+				       sym_matcher);
       if (r != 0)
 	{
 	  ps->searched_flag = PST_SEARCHED_AND_FOUND;
@@ -1352,15 +1389,16 @@ recursively_search_psymtabs
 	{
 	  QUIT;
 
-	  if ((kind == ALL_DOMAIN
-	       || (kind == VARIABLES_DOMAIN
+	  if ((domain == ALL_DOMAIN
+	       || (domain == VARIABLES_DOMAIN
 		   && PSYMBOL_CLASS (*psym) != LOC_TYPEDEF
 		   && PSYMBOL_CLASS (*psym) != LOC_BLOCK)
-	       || (kind == FUNCTIONS_DOMAIN
+	       || (domain == FUNCTIONS_DOMAIN
 		   && PSYMBOL_CLASS (*psym) == LOC_BLOCK)
-	       || (kind == TYPES_DOMAIN
+	       || (domain == TYPES_DOMAIN
 		   && PSYMBOL_CLASS (*psym) == LOC_TYPEDEF))
-	      && sym_matcher (SYMBOL_SEARCH_NAME (*psym)))
+	      && psymbol_name_matches (*psym, lookup_name)
+	      && (sym_matcher == NULL || sym_matcher (SYMBOL_SEARCH_NAME (*psym))))
 	    {
 	      /* Found a match, so notify our caller.  */
 	      result = PST_SEARCHED_AND_FOUND;
@@ -1381,9 +1419,10 @@ static void
 psym_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
-   enum search_domain kind)
+   enum search_domain domain)
 {
   struct partial_symtab *ps;
 
@@ -1425,7 +1464,8 @@ psym_expand_symtabs_matching
 	    continue;
 	}
 
-      if (recursively_search_psymtabs (ps, objfile, kind, symbol_matcher))
+      if (recursively_search_psymtabs (ps, objfile, domain,
+				       lookup_name, symbol_matcher))
 	{
 	  struct compunit_symtab *symtab =
 	    psymtab_to_symtab (objfile, ps);
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index f0d9968..04a83de 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2188,7 +2188,7 @@ extern const struct language_defn rust_language_defn =
   default_pass_by_reference,
   c_get_string,
   rust_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
index 5ca1fa7..7533c32 100644
--- a/gdb/symfile-debug.c
+++ b/gdb/symfile-debug.c
@@ -260,7 +260,7 @@ debug_qf_map_matching_symbols (struct objfile *objfile,
 			       int (*callback) (struct block *,
 						struct symbol *, void *),
 			       void *data,
-			       symbol_compare_ftype *match,
+			       symbol_name_match_type match,
 			       symbol_compare_ftype *ordered_compare)
 {
   const struct debug_sym_fns_data *debug_data
@@ -273,7 +273,7 @@ debug_qf_map_matching_symbols (struct objfile *objfile,
 		    domain_name (domain), global,
 		    host_address_to_string (callback),
 		    host_address_to_string (data),
-		    host_address_to_string (match),
+		    plongest ((LONGEST) match),
 		    host_address_to_string (ordered_compare));
 
   debug_data->real_sf->qf->map_matching_symbols (objfile, name,
@@ -287,6 +287,7 @@ static void
 debug_qf_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -305,6 +306,7 @@ debug_qf_expand_symtabs_matching
 
   debug_data->real_sf->qf->expand_symtabs_matching (objfile,
 						    file_matcher,
+						    lookup_name,
 						    symbol_matcher,
 						    expansion_notify,
 						    kind);
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 7892d17..a387197 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -3838,6 +3838,7 @@ symfile_free_objfile (struct objfile *objfile)
 void
 expand_symtabs_matching
   (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -3848,6 +3849,7 @@ expand_symtabs_matching
   {
     if (objfile->sf)
       objfile->sf->qf->expand_symtabs_matching (objfile, file_matcher,
+						lookup_name,
 						symbol_matcher,
 						expansion_notify, kind);
   }
diff --git a/gdb/symfile.h b/gdb/symfile.h
index ab536e8..c37bafb 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -262,7 +262,7 @@ struct quick_symbol_functions
 				int (*callback) (struct block *,
 						 struct symbol *, void *),
 				void *data,
-				symbol_compare_ftype *match,
+				symbol_name_match_type match,
 				symbol_compare_ftype *ordered_compare);
 
   /* Expand all symbol tables in OBJFILE matching some criteria.
@@ -286,6 +286,7 @@ struct quick_symbol_functions
   void (*expand_symtabs_matching)
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info &lookup_name,
      gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
      gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
      enum search_domain kind);
@@ -560,6 +561,7 @@ extern scoped_restore_tmpl<int> increment_reading_symtab (void);
 
 void expand_symtabs_matching
   (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind);
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index 32a5331..ad829bf 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -979,6 +979,7 @@ maintenance_expand_symtabs (char *args, int from_tty)
 	       return (!basenames
 		       && (regexp == NULL || re_exec (filename)));
 	     },
+	     lookup_name_info::match_any (),
 	     [] (const char *symname)
 	     {
 	       /* Since we're not searching on symbols, just return true.  */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index a875c4d..41348bd 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -946,6 +946,16 @@ symbol_search_name (const struct general_symbol_info *gsymbol)
     return symbol_natural_name (gsymbol);
 }
 
+bool
+symbol_matches_search_name (const struct general_symbol_info *gsymbol,
+			    const lookup_name_info &name)
+{
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (language_def (gsymbol->language),
+					name);
+  return name_match (symbol_search_name (gsymbol), name, NULL);
+}
+
 /* Initialize the structure fields to zero values.  */
 
 void
@@ -1110,11 +1120,12 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
     }
   else if (slot_name != NULL && name != NULL)
     {
-      /* It's important that we use the same comparison that was done the
-	 first time through.  If the slot records a found symbol, then this
-	 means using strcmp_iw on SYMBOL_SEARCH_NAME.  See dictionary.c.
-	 It also means using symbol_matches_domain for found symbols.
-	 See block.c.
+      /* It's important that we use the same comparison that was done
+	 the first time through.  If the slot records a found symbol,
+	 then this means using the symbol name comparison function of
+	 the symbol's language with SYMBOL_SEARCH_NAME.  See
+	 dictionary.c.  It also means using symbol_matches_domain for
+	 found symbols.  See block.c.
 
 	 If the slot records a not-found symbol, then require a precise match.
 	 We could still be lax with whitespace like strcmp_iw though.  */
@@ -1129,8 +1140,11 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
       else
 	{
 	  struct symbol *sym = slot->value.found.symbol;
-
-	  if (strcmp_iw (slot_name, name) != 0)
+	  const language_defn *lang = language_def (SYMBOL_LANGUAGE (sym));
+	  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+	  symbol_name_matcher_ftype *matcher
+	    = language_get_symbol_name_matcher (lang, lookup_name);
+	  if (!matcher (slot_name, lookup_name, NULL))
 	    return 0;
 	  if (!symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				      slot_domain, domain))
@@ -1750,6 +1764,25 @@ fixup_symbol_section (struct symbol *sym, struct objfile *objfile)
   return sym;
 }
 
+demangle_for_lookup_info::demangle_for_lookup_info (const lookup_name_info &lookup_name,
+						    language lang)
+{
+  demangle_result_storage storage;
+
+  m_demangled_name = demangle_for_lookup (lookup_name.name ().c_str (),
+					  lang, storage);
+}
+
+const lookup_name_info &
+lookup_name_info::match_any ()
+{
+  /* Lookup any symbol that "" would complete.  I.e., this matches all
+     symbol names.  */
+  static lookup_name_info lookup_name ({}, symbol_name_match_type::FULL, true);
+
+  return lookup_name;
+}
+
 /* Compute the demangled form of NAME as used by the various symbol
    lookup functions.  The result can either be the input NAME
    directly, or a pointer to a buffer owned by the STORAGE object.
@@ -2773,7 +2806,8 @@ basic_lookup_transparent_type (const char *name)
    search continues.  */
 
 void
-iterate_over_symbols (const struct block *block, const char *name,
+iterate_over_symbols (const struct block *block,
+		      const lookup_name_info &name,
 		      const domain_enum domain,
 		      gdb::function_view<symbol_found_callback_ftype> callback)
 {
@@ -4315,6 +4349,7 @@ search_symbols (const char *regexp, enum search_domain kind,
 			     return file_matches (filename, files, nfiles,
 						  basenames);
 			   },
+			   lookup_name_info::match_any (),
 			   [&] (const char *symname)
 			   {
 			     return (!preg_p || regexec (&preg, symname,
@@ -4742,13 +4777,33 @@ rbreak_command (char *regexp, int from_tty)
    information.  */
 
 static int
-compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
-{
-  int (*ncmp) (const char *, const char *, size_t);
+compare_symbol_name (const char *name,
+		     language symbol_language,
+		     const lookup_name_info &lookup_name,
+		     const char *sym_text, int sym_text_len,
+		     completion_match_result &match_res)
+{
+  const language_defn *lang;
+
+  /* If we're completing for an expression and the symbol doesn't have
+     an explicit language set, fallback to the current language.  Ada
+     minimal symbols won't have their language set to Ada, for
+     example, and if we compared using the default/C-like matcher,
+     then when completing e.g., symbols in a package named "pck", we'd
+     match internal Ada symbols like "pckS", which are invalid in an
+     Ada expression, unless you wrap them in '<' '>' to request a
+     verbatim match.  */
+  if (symbol_language == language_auto
+      && lookup_name.match_type () == symbol_name_match_type::EXPRESSION)
+    lang = current_language;
+  else
+    lang = language_def (symbol_language);
 
-  ncmp = (case_sensitivity == case_sensitive_on ? strncmp : strncasecmp);
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (lang, lookup_name);
 
-  if (ncmp (name, sym_text, sym_text_len) != 0)
+  /* Clip symbols that cannot match.  */
+  if (!name_match (name, lookup_name, &match_res.match))
     return 0;
 
   if (sym_text[sym_text_len] == '(')
@@ -4770,16 +4825,30 @@ compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
    demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
    characters.  If so, add it to the current completion list.  */
 
-static void
+void
 completion_list_add_name (completion_tracker &tracker,
+			  language symbol_language,
 			  const char *symname,
+			  const lookup_name_info &lookup_name,
 			  const char *sym_text, int sym_text_len,
 			  const char *text, const char *word)
 {
+  completion_match_result &match_res
+    = tracker.reset_completion_match_result ();
+
   /* Clip symbols that cannot match.  */
-  if (!compare_symbol_name (symname, sym_text, sym_text_len))
+  if (!compare_symbol_name (symname, symbol_language,
+			    lookup_name,
+			    sym_text, sym_text_len,
+			    match_res))
     return;
 
+  /* Refresh SYMNAME from the match string.  It's potentially
+     different depending on language.  (E.g., on Ada, the match may be
+     the encoded symbol name wrapped in "<>").  */
+  symname = match_res.match.match ();
+  gdb_assert (symname != NULL);
+
   /* We have a match for a completion, so add SYMNAME to the current list
      of matches.  Note that the name is moved to freshly malloc'd space.  */
 
@@ -4817,11 +4886,13 @@ completion_list_add_name (completion_tracker &tracker,
 static void
 completion_list_add_symbol (completion_tracker &tracker,
 			    symbol *sym,
+			    const lookup_name_info &lookup_name,
 			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
-  completion_list_add_name (tracker, SYMBOL_NATURAL_NAME (sym),
-			    sym_text, sym_text_len, text, word);
+  completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+			    SYMBOL_NATURAL_NAME (sym),
+			    lookup_name, sym_text, sym_text_len, text, word);
 }
 
 /* completion_list_add_name wrapper for struct minimal_symbol.  */
@@ -4829,19 +4900,23 @@ completion_list_add_symbol (completion_tracker &tracker,
 static void
 completion_list_add_msymbol (completion_tracker &tracker,
 			     minimal_symbol *sym,
+			     const lookup_name_info &lookup_name,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
-  completion_list_add_name (tracker, MSYMBOL_NATURAL_NAME (sym),
-			    sym_text, sym_text_len, text, word);
+  completion_list_add_name (tracker, MSYMBOL_LANGUAGE (sym),
+			    MSYMBOL_NATURAL_NAME (sym),
+			    lookup_name, sym_text, sym_text_len, text, word);
 }
 
+
 /* ObjC: In case we are completing on a selector, look as the msymbol
    again and feed all the selectors into the mill.  */
 
 static void
 completion_list_objc_symbol (completion_tracker &tracker,
 			     struct minimal_symbol *msymbol,
+			     const lookup_name_info &lookup_name,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
@@ -4859,7 +4934,9 @@ completion_list_objc_symbol (completion_tracker &tracker,
 
   if (sym_text[0] == '[')
     /* Complete on shortened method method.  */
-    completion_list_add_name (tracker, method + 1,
+    completion_list_add_name (tracker, language_objc,
+			      method + 1,
+			      lookup_name,
 			      sym_text, sym_text_len, text, word);
 
   while ((strlen (method) + 1) >= tmplen)
@@ -4881,10 +4958,12 @@ completion_list_objc_symbol (completion_tracker &tracker,
       memcpy (tmp, method, (category - method));
       tmp[category - method] = ' ';
       memcpy (tmp + (category - method) + 1, selector, strlen (selector) + 1);
-      completion_list_add_name (tracker, tmp,
+      completion_list_add_name (tracker, language_objc, tmp,
+				lookup_name,
 				sym_text, sym_text_len, text, word);
       if (sym_text[0] == '[')
-	completion_list_add_name (tracker, tmp + 1,
+	completion_list_add_name (tracker, language_objc, tmp + 1,
+				  lookup_name,
 				  sym_text, sym_text_len, text, word);
     }
 
@@ -4896,7 +4975,8 @@ completion_list_objc_symbol (completion_tracker &tracker,
       if (tmp2 != NULL)
 	*tmp2 = '\0';
 
-      completion_list_add_name (tracker, tmp,
+      completion_list_add_name (tracker, language_objc, tmp,
+				lookup_name,
 				sym_text, sym_text_len, text, word);
     }
 }
@@ -4950,6 +5030,7 @@ language_search_unquoted_string (const char *text, const char *p)
 static void
 completion_list_add_fields (completion_tracker &tracker,
 			    struct symbol *sym,
+			    const lookup_name_info &lookup_name,
 			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
@@ -4962,7 +5043,9 @@ completion_list_add_fields (completion_tracker &tracker,
       if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
 	for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
 	  if (TYPE_FIELD_NAME (t, j))
-	    completion_list_add_name (tracker, TYPE_FIELD_NAME (t, j),
+	    completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+				      TYPE_FIELD_NAME (t, j),
+				      lookup_name,
 				      sym_text, sym_text_len, text, word);
     }
 }
@@ -4972,6 +5055,7 @@ completion_list_add_fields (completion_tracker &tracker,
 static void
 add_symtab_completions (struct compunit_symtab *cust,
 			completion_tracker &tracker,
+			const lookup_name_info &lookup_name,
 			const char *sym_text, int sym_text_len,
 			const char *text, const char *word,
 			enum type_code code)
@@ -4994,6 +5078,7 @@ add_symtab_completions (struct compunit_symtab *cust,
 	      || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
 	    completion_list_add_symbol (tracker, sym,
+					lookup_name,
 					sym_text, sym_text_len,
 					text, word);
 	}
@@ -5002,8 +5087,8 @@ add_symtab_completions (struct compunit_symtab *cust,
 
 void
 default_collect_symbol_completion_matches_break_on
-  (completion_tracker &tracker,
-   complete_symbol_mode mode,
+  (completion_tracker &tracker, complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
    const char *text, const char *word,
    const char *break_on, enum type_code code)
 {
@@ -5095,6 +5180,9 @@ default_collect_symbol_completion_matches_break_on
     }
   gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
 
+  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
+				name_match_type, true);
+
   /* At this point scan through the misc symbol vectors and add each
      symbol you find to the list.  Eventually we want to ignore
      anything that isn't a text symbol (everything else will be
@@ -5106,34 +5194,30 @@ default_collect_symbol_completion_matches_break_on
 	{
 	  QUIT;
 
-	  completion_list_add_msymbol (tracker,
-				       msymbol, sym_text, sym_text_len,
+	  completion_list_add_msymbol (tracker, msymbol, lookup_name,
+				       sym_text, sym_text_len,
 				       text, word);
 
-	  completion_list_objc_symbol (tracker,
-				       msymbol, sym_text, sym_text_len,
-				       text, word);
+	  completion_list_objc_symbol (tracker, msymbol, lookup_name,
+				       sym_text, sym_text_len, text,
+				       word);
 	}
     }
 
   /* Add completions for all currently loaded symbol tables.  */
   ALL_COMPUNITS (objfile, cust)
-    add_symtab_completions (cust, tracker,
+    add_symtab_completions (cust, tracker, lookup_name,
 			    sym_text, sym_text_len, text, word, code);
 
   /* Look through the partial symtabs for all symbols which begin by
      matching SYM_TEXT.  Expand all CUs that you find to the list.  */
   expand_symtabs_matching (NULL,
-			   [&] (const char *name) /* symbol matcher */
-			     {
-			       return compare_symbol_name (name,
-							   sym_text,
-							   sym_text_len);
-			     },
+			   lookup_name,
+			   NULL,
 			   [&] (compunit_symtab *symtab) /* expansion notify */
 			     {
 			       add_symtab_completions (symtab,
-						       tracker,
+						       tracker, lookup_name,
 						       sym_text, sym_text_len,
 						       text, word, code);
 			     },
@@ -5156,16 +5240,16 @@ default_collect_symbol_completion_matches_break_on
 	  {
 	    if (code == TYPE_CODE_UNDEF)
 	      {
-		completion_list_add_symbol (tracker, sym,
+		completion_list_add_symbol (tracker, sym, lookup_name,
 					    sym_text, sym_text_len, text,
 					    word);
-		completion_list_add_fields (tracker, sym,
+		completion_list_add_fields (tracker, sym, lookup_name,
 					    sym_text, sym_text_len, text,
 					    word);
 	      }
 	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
-	      completion_list_add_symbol (tracker, sym,
+	      completion_list_add_symbol (tracker, sym, lookup_name,
 					  sym_text, sym_text_len, text,
 					  word);
 	  }
@@ -5184,12 +5268,12 @@ default_collect_symbol_completion_matches_break_on
     {
       if (surrounding_static_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
-	  completion_list_add_fields (tracker, sym,
+	  completion_list_add_fields (tracker, sym, lookup_name,
 				      sym_text, sym_text_len, text, word);
 
       if (surrounding_global_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
-	  completion_list_add_fields (tracker, sym,
+	  completion_list_add_fields (tracker, sym, lookup_name,
 				      sym_text, sym_text_len, text, word);
     }
 
@@ -5206,7 +5290,10 @@ default_collect_symbol_completion_matches_break_on
 				 macro_source_file *,
 				 int)
 	{
-	  completion_list_add_name (tracker, macro_name,
+	  completion_list_add_name (tracker,
+				    language_c,
+				    macro_name,
+				    lookup_name,
 				    sym_text, sym_text_len,
 				    text, word);
 	};
@@ -5234,10 +5321,12 @@ default_collect_symbol_completion_matches_break_on
 void
 default_collect_symbol_completion_matches (completion_tracker &tracker,
 					   complete_symbol_mode mode,
+					   symbol_name_match_type name_match_type,
 					   const char *text, const char *word,
 					   enum type_code code)
 {
   return default_collect_symbol_completion_matches_break_on (tracker, mode,
+							     name_match_type,
 							     text, word, "",
 							     code);
 }
@@ -5249,9 +5338,11 @@ default_collect_symbol_completion_matches (completion_tracker &tracker,
 void
 collect_symbol_completion_matches (completion_tracker &tracker,
 				   complete_symbol_mode mode,
+				   symbol_name_match_type name_match_type,
 				   const char *text, const char *word)
 {
   current_language->la_collect_symbol_completion_matches (tracker, mode,
+							  name_match_type,
 							  text, word,
 							  TYPE_CODE_UNDEF);
 }
@@ -5265,11 +5356,13 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 					enum type_code code)
 {
   complete_symbol_mode mode = complete_symbol_mode::EXPRESSION;
+  symbol_name_match_type name_match_type = symbol_name_match_type::EXPRESSION;
 
   gdb_assert (code == TYPE_CODE_UNION
 	      || code == TYPE_CODE_STRUCT
 	      || code == TYPE_CODE_ENUM);
   current_language->la_collect_symbol_completion_matches (tracker, mode,
+							  name_match_type,
 							  text, word, code);
 }
 
@@ -5279,6 +5372,7 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 void
 collect_file_symbol_completion_matches (completion_tracker &tracker,
 					complete_symbol_mode mode,
+					symbol_name_match_type name_match_type,
 					const char *text, const char *word,
 					const char *srcfile)
 {
@@ -5335,12 +5429,15 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
 
   sym_text_len = strlen (sym_text);
 
+  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
+				name_match_type, true);
+
   /* Go through symtabs for SRCFILE and check the externs and statics
      for symbols which match.  */
   iterate_over_symtabs (srcfile, [&] (symtab *s)
     {
       add_symtab_completions (SYMTAB_COMPUNIT (s),
-			      tracker,
+			      tracker, lookup_name,
 			      sym_text, sym_text_len,
 			      text, word, TYPE_CODE_UNDEF);
       return false;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 20904e4..4c8b1f6 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -21,10 +21,12 @@
 #define SYMTAB_H 1
 
 #include <vector>
+#include <string>
 #include "gdb_vecs.h"
 #include "gdbtypes.h"
 #include "common/enum-flags.h"
 #include "common/function-view.h"
+#include "common/gdb_optional.h"
 #include "completer.h"
 
 /* Opaque declarations.  */
@@ -43,6 +45,249 @@ struct probe;
 struct common_block;
 struct obj_section;
 struct cmd_list_element;
+struct lookup_name_info;
+
+/* How to match a lookup name against a symbol search name.  */
+enum class symbol_name_match_type
+{
+  /* Wild matching.  Matches unqualified symbol names in all
+     namespace/module/packages, etc.  */
+  WILD,
+
+  /* Full matching.  The lookup name indicates a fully-qualified name,
+     and only matches symbol search names in the specified
+     namespace/module/package.  */
+  FULL,
+
+  /* Expression matching.  The same as FULL matching on most
+     languages.  The same as WILD matching in Ada.  */
+  EXPRESSION,
+};
+
+extern unsigned int search_name_hash (enum language language,
+				      const char *search_name);
+
+/* Ada-specific bits of a lookup_name_info object.  This is lazily
+   constructed on demand.  */
+
+class ada_lookup_name_info final
+{
+ public:
+  /* Construct.  */
+  explicit ada_lookup_name_info (const lookup_name_info &lookup_name);
+
+  /* Compare SYMBOL_SEARCH_NAME with our lookup name, using MATCH_TYPE
+     as name match type.  Returns true if there's a match, false
+     otherwise.  If non-NULL, store the matching results in MATCH.  */
+  bool matches (const char *symbol_search_name,
+		symbol_name_match_type match_type,
+		completion_match *match) const;
+
+  /* The Ada-encoded lookup name.  */
+  const std::string &lookup_name () const
+  { return m_encoded_name; }
+
+  /* Return true if we're supposed to be doing a wild match look
+     up.  */
+  bool wild_match_p () const
+  { return m_wild_match_p; }
+
+  /* Return true if we're looking up a name inside package
+     Standard.  */
+  bool standard_p () const
+  { return m_standard_p; }
+
+ private:
+  /* The Ada-encoded lookup name.  */
+  std::string m_encoded_name;
+
+  /* Whether the user-provided lookup name was Ada encoded.  If so,
+     then return encoded names in the matches method's completion
+     match result outputs.  */
+  bool m_encoded_p : 1;
+
+  /* True if really doing wild matching.  Even if the user request
+     wild matching, some cases require full matching.  */
+  bool m_wild_match_p : 1;
+
+  /* True if doing a verbatim match.  This is true if the decoded
+     version of the symbol name is wrapped in '<'/'>'.  This is an
+     escape hatch users can use to look up symbols the Ada encoding
+     does not understand.  */
+  bool m_verbatim_p : 1;
+
+   /* True if the user specified a symbol name that is inside package
+      Standard.  Symbol names inside package Standard are handled
+      specially.  We always do a non-wild matching of the symbol name
+      without the "standard__" prefix, and only search static and
+      global symbols.  This was primarily introduced in order to allow
+      the user to specifically access the standard exceptions using,
+      for instance, Standard.Constraint_Error when Constraint_Error is
+      ambiguous (due to the user defining its own Constraint_Error
+      entity inside its program).  */
+  bool m_standard_p : 1;
+};
+
+/* Language-specific bits of a lookup_name_info object, for languages
+   that do name searching using demangled names (C++/D/Go).  This is
+   lazily constructed on demand.  */
+
+struct demangle_for_lookup_info final
+{
+public:
+  explicit demangle_for_lookup_info (const lookup_name_info &lookup_name,
+				     language lang);
+
+  /* The demangled lookup name.  */
+  const std::string &lookup_name () const
+  { return m_demangled_name; }
+
+private:
+  /* The demangled lookup name.  */
+  std::string m_demangled_name;
+};
+
+/* Object that aggregates all information relative to a symbol lookup
+   name.  I.e., the name that is matched against the symbol's search
+   name.  Caches per-language information so that it doesn't require
+   recomputing for every symbol comparison, like for example the Ada
+   encoded name and the symbol's name hash for a given language.  The
+   object is conceptually immutable once constructed, and thus has no
+   setters.  This is to avoid the case of a code path tweaking some
+   property of the lookup name for some local reason, and accidentally
+   causing other parts of symbol search that continue searching using
+   the same lookup name be affected accidentally.  lookup_name_info
+   objects are generally passed around as a const reference to
+   reinforce that.  (They're not passed around by value because
+   they're not small.)  */
+class lookup_name_info final
+{
+ public:
+  /* Create a new object.  */
+  lookup_name_info (std::string name,
+		    symbol_name_match_type match_type,
+		    bool completion_mode = false)
+    : m_match_type (match_type),
+      m_completion_mode (completion_mode),
+      m_name (std::move (name))
+  {}
+
+  /* Getters.  See description of each correspond field.  */
+  symbol_name_match_type match_type () const { return m_match_type; }
+  bool completion_mode () const { return m_completion_mode; }
+  const std::string &name () const { return m_name; }
+
+  /* Get the search name hash for searches in language LANG.  */
+  unsigned int search_name_hash (language lang) const
+  {
+    /* Only compute each language's hash once.  */
+    if (!m_demangled_hashes_p[lang])
+      {
+	m_demangled_hashes[lang]
+	  = ::search_name_hash (lang, language_lookup_name (lang).c_str ());
+	m_demangled_hashes_p[lang] = true;
+      }
+    return m_demangled_hashes[lang];
+  }
+
+  /* Get the search name for searches in language LANG.  */
+  const std::string &language_lookup_name (language lang) const
+  {
+    switch (lang)
+      {
+      case language_ada:
+	return ada ().lookup_name ();
+      case language_cplus:
+	return cplus ().lookup_name ();
+      case language_d:
+	return d ().lookup_name ();
+      case language_go:
+	return go ().lookup_name ();
+      default:
+	return m_name;
+      }
+  }
+
+  /* Get the Ada-specific lookup info.  */
+  const ada_lookup_name_info &ada () const
+  {
+    maybe_init (m_ada);
+    return *m_ada;
+  }
+
+  /* Get the C++-specific lookup info.  */
+  const demangle_for_lookup_info &cplus () const
+  {
+    maybe_init (m_cplus, language_cplus);
+    return *m_cplus;
+  }
+
+  /* Get the D-specific lookup info.  */
+  const demangle_for_lookup_info &d () const
+  {
+    maybe_init (m_d, language_d);
+    return *m_d;
+  }
+
+  /* Get the Go-specific lookup info.  */
+  const demangle_for_lookup_info &go () const
+  {
+    maybe_init (m_go, language_go);
+    return *m_go;
+  }
+
+  /* Get a reference to a lookup_name_info object that matches any
+     symbol name.  */
+  static const lookup_name_info &match_any ();
+
+private:
+  /* Initialize FIELD, if not initialized yet.  */
+  template <typename Field, typename... Args>
+  void maybe_init (Field &field, Args&&... args) const
+  {
+    if (!field)
+      field.emplace (*this, std::forward<Args>(args)...);
+  }
+
+  /* The lookup info as passed to the ctor.  */
+  symbol_name_match_type m_match_type;
+  bool m_completion_mode;
+  std::string m_name;
+
+  /* Language-specific info.  These fields are filled lazily the first
+     time a lookup is done in the corresponding language.  They're
+     mutable because lookup_name_info objects are typically passed
+     around by const reference (see intro), and they're conceptually
+     "cache" that can always be reconstructed from the non-mutable
+     fields.  */
+  mutable gdb::optional<ada_lookup_name_info> m_ada;
+  mutable gdb::optional<demangle_for_lookup_info> m_cplus;
+  mutable gdb::optional<demangle_for_lookup_info> m_d;
+  mutable gdb::optional<demangle_for_lookup_info> m_go;
+
+  /* The demangled hashes.  Stored in an array with one entry for each
+     possible language.  The second array holds a
+     boolean-disguised-as-byte, that records whether we've already
+     computed the language's hash.  */
+  mutable std::array<unsigned int, nr_languages> m_demangled_hashes;
+  mutable std::array<unsigned char, nr_languages> m_demangled_hashes_p {};
+};
+
+/* Comparison function for completion symbol lookup.
+
+   Returns true if the symbol name matches against LOOKUP_NAME.
+
+   SYMBOL_SEARCH_NAME should be a symbol's "search" name.
+
+   On success and if non-NULL, MATCH is set to point to the symbol
+   name as should be presented to the user as a completion match list
+   element.  In most languages, this is the same as the symbol's
+   search name, but in some, like Ada, the display name is dynamically
+   computed within the comparison routine.  */
+typedef bool (symbol_name_matcher_ftype)
+  (const char *symbol_search_name,
+   const lookup_name_info &lookup_name,
+   completion_match *match);
 
 /* Some of the structures in this file are space critical.
    The space-critical structures are:
@@ -269,13 +514,16 @@ extern int demangle;
    returns the same value (same pointer) as SYMBOL_LINKAGE_NAME.  */
 #define SYMBOL_SEARCH_NAME(symbol)					 \
    (symbol_search_name (&(symbol)->ginfo))
-extern const char *symbol_search_name (const struct general_symbol_info *);
+extern const char *symbol_search_name (const struct general_symbol_info *ginfo);
 
-/* Return non-zero if NAME matches the "search" name of SYMBOL.
-   Whitespace and trailing parentheses are ignored.
-   See strcmp_iw for details about its behavior.  */
-#define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
-  (strcmp_iw (SYMBOL_SEARCH_NAME (symbol), (name)) == 0)
+/* Return true if NAME matches the "search" name of SYMBOL, according
+   to the symbol's language.  */
+#define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)                       \
+  symbol_matches_search_name (&(symbol)->ginfo, (name))
+
+extern bool symbol_matches_search_name
+  (const struct general_symbol_info *gsymbol,
+   const lookup_name_info &name);
 
 extern unsigned int search_name_hash (enum language language,
 				      const char *search_name);
@@ -425,8 +673,6 @@ struct minimal_symbol
   (symbol_set_language (&(symbol)->mginfo, (language), (obstack)))
 #define MSYMBOL_SEARCH_NAME(symbol)					 \
    (symbol_search_name (&(symbol)->mginfo))
-#define MSYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
-  (strcmp_iw (MSYMBOL_SEARCH_NAME (symbol), (name)) == 0)
 #define MSYMBOL_SET_NAMES(symbol,linkage_name,len,copy_name,objfile)	\
   symbol_set_names (&(symbol)->mginfo, linkage_name, len, copy_name, objfile)
 
@@ -1516,26 +1762,30 @@ enum class complete_symbol_mode
 extern void default_collect_symbol_completion_matches_break_on
   (completion_tracker &tracker,
    complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
    const char *text, const char *word, const char *break_on,
    enum type_code code);
 extern void default_collect_symbol_completion_matches
   (completion_tracker &tracker,
    complete_symbol_mode,
+   symbol_name_match_type name_match_type,
    const char *,
    const char *,
    enum type_code);
-extern void collect_symbol_completion_matches (completion_tracker &tracker,
-					       complete_symbol_mode,
-					       const char *, const char *);
+extern void collect_symbol_completion_matches
+  (completion_tracker &tracker,
+   complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
+   const char *, const char *);
 extern void collect_symbol_completion_matches_type (completion_tracker &tracker,
 						    const char *, const char *,
 						    enum type_code);
 
-extern void collect_file_symbol_completion_matches (completion_tracker &tracker,
-						    complete_symbol_mode,
-						    const char *,
-						    const char *,
-						    const char *);
+extern void collect_file_symbol_completion_matches
+  (completion_tracker &tracker,
+   complete_symbol_mode,
+   symbol_name_match_type name_match_type,
+   const char *, const char *, const char *);
 
 extern completion_list
   make_source_files_completion_list (const char *, const char *);
@@ -1650,7 +1900,8 @@ std::vector<CORE_ADDR> find_pcs_for_symtab_line
 
 typedef bool (symbol_found_callback_ftype) (symbol *sym);
 
-void iterate_over_symbols (const struct block *block, const char *name,
+void iterate_over_symbols (const struct block *block,
+			   const lookup_name_info &name,
 			   const domain_enum domain,
 			   gdb::function_view<symbol_found_callback_ftype> callback);
 
@@ -1698,4 +1949,11 @@ void initialize_objfile_symbol (struct symbol *);
 
 struct template_symbol *allocate_template_symbol (struct objfile *);
 
+void completion_list_add_name (completion_tracker &tracker,
+			       language symbol_language,
+			       const char *symname,
+			       const lookup_name_info &lookup_name,
+			       const char *sym_text, int sym_text_len,
+			       const char *text, const char *word);
+
 #endif /* !defined(SYMTAB_H) */
diff --git a/gdb/testsuite/gdb.ada/complete.exp b/gdb/testsuite/gdb.ada/complete.exp
index 906c85a..c3631c7 100644
--- a/gdb/testsuite/gdb.ada/complete.exp
+++ b/gdb/testsuite/gdb.ada/complete.exp
@@ -83,6 +83,16 @@ test_gdb_no_completion "exported"
 test_gdb_complete "<Exported" \
                   "p <Exported_Capitalized>"
 
+# While at it, make sure we can print the symbol too, using the '<'
+# notation.
+gdb_test "p <Exported_Capitalized>" " = 2"
+
+# Confirm that we can't print the symbol without the '<' notation.
+gdb_test "p Exported_Capitalized" \
+    "No definition of \"exported_capitalized\" in current context."
+gdb_test "p exported_capitalized" \
+    "No definition of \"exported_capitalized\" in current context."
+
 # A global symbol, created by the binder, that starts with __gnat...
 test_gdb_complete "__gnat_ada_main_progra" \
                   "p __gnat_ada_main_program_name"
diff --git a/gdb/utils.c b/gdb/utils.c
index 9f1c83a..9798edc 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2418,21 +2418,9 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
     }
 }
 
-/* Modes of operation for strncmp_iw_with_mode.  */
-
-enum class strncmp_iw_mode
-{
-  /* Work like strncmp, while ignoring whitespace.  */
-  NORMAL,
-
-  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
-     string1=="FOO(PARAMS)" matches string2=="FOO".  */
-  MATCH_PARAMS,
-};
-
-/* Helper for strncmp_iw and strcmp_iw.  */
+/* See utils.h.  */
 
-static int
+int
 strncmp_iw_with_mode (const char *string1, const char *string2,
 		      size_t string2_len, strncmp_iw_mode mode)
 {
diff --git a/gdb/utils.h b/gdb/utils.h
index 2396dcd..9e531e0 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -31,6 +31,33 @@ extern void initialize_utils (void);
 
 extern int sevenbit_strings;
 
+/* Modes of operation for strncmp_iw_with_mode.  */
+
+enum class strncmp_iw_mode
+{
+/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
+   differences in whitespace.  Returns 0 if they match, non-zero if they
+   don't (slightly different than strcmp()'s range of return values).
+
+   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
+   This "feature" is useful when searching for matching C++ function names
+   (such as if the user types 'break FOO', where FOO is a mangled C++
+   function).  */
+  NORMAL,
+
+  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
+     string1=="FOO(PARAMS)" matches string2=="FOO".  */
+  MATCH_PARAMS,
+};
+
+/* Helper for strcmp_iw and strncmp_iw.  Exported so that languages
+   can implement both NORMAL and MATCH_PARAMS variants in a single
+   function and defer part of the work to strncmp_iw_with_mode.  */
+extern int strncmp_iw_with_mode (const char *string1,
+				 const char *string2,
+				 size_t string2_len,
+				 strncmp_iw_mode mode);
+
 /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
    differences in whitespace.  STRING2_LEN is STRING2's length.
    Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
-- 
2.5.5

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

* [PATCH 26/40] Optimize .gdb_index symbol name searching
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (38 preceding siblings ...)
  2017-06-02 12:39 ` [PATCH 23/40] Make language_def O(1) Pedro Alves
@ 2017-06-02 12:39 ` Pedro Alves
  2017-08-08 20:32   ` Keith Seitz
  2017-11-18  5:23   ` [PATCH 26/40] Optimize .gdb_index symbol name searching Simon Marchi
  2017-06-02 15:26 ` [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
  40 siblings, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:39 UTC (permalink / raw)
  To: gdb-patches

As mentioned in the previous patch, .gdb_index name lookup got
significantly slower with the previous patch.

This patch addresses that, and in the process makes .gdb_index name
searching faster than what we had before the previous patch, even.
Using the same test:

 $ cat script.cmd
 set pagination off
 set $count = 0
 while $count < 400
   complete b string_prin
   printf "count = %d\n", $count
   set $count = $count + 1
 end

 $ time gdb --batch -q ./gdb-with-index -ex "source script.cmd"

I got, before the previous patch (-O2, x86-64):

 real    0m1.773s
 user    0m1.737s
 sys     0m0.040s

and after this patch:

 real    0m1.361s
 user    0m1.315s
 sys     0m0.040s

The basic idea here is simple: instead of always iterating over all
the symbol names in the index, we build an accelerator/sorted name
table and binary search names in it.

Later in the series, we'll want to support wild matching for C++ too,
so this mechanism already considers that.  For example, say that
you're looking up functions/methods named "func", no matter the
containing namespace/class.  If we sorted the table by qualified name,
then we obviously wouldn't be able to find those symbols with a binary
search:

  func
  ns1::a::b::func
  ns1::b::func
  ns2::func

(function symbol names in .gdb_index have no parameter info, like psymbols)

To address that out, we put an entry for each name component in the
sorted table.  something like this:

  Table Entry       Actual symbol
  ---------------------------------
  func              func

  func              ns1::a::b::func
  b::func           ns1::a::b::func
  a::b::func        ns1::a::b::func
  ns1::a::b::func   ns1::a::b::func

  func              ns1::b::func
  b::func           ns1::b::func
  ns1::b::func      ns1::b::func

  func              ns2::func
  ns2::func         ns2::func

Which sorted results in this:

  Table Entry       Actual symbol
  ---------------------------------
  a::b::func        ns1::a::b::func
  b::func           ns1::a::b::func
  b::func           ns1::b::func
  func              func
  func              ns1::a::b::func
  func              ns1::b::func
  func              ns2::func
  ns1::a::b::func   ns1::a::b::func
  ns1::b::func      ns1::b::func
  ns2::func         ns2::func

And we can binary search this.

Note that a binary search approach works for both completion and
regular lookup, while a name hashing approach only works for normal
symbol looking, since obviously "fun" and "func" have different
hashes.

At first I was a bit wary of these tables potentially growing GDB's
memory significantly.  But I did an experiment that convinced it's not
a worry at all.  I hacked gdb to count the total number of entries in
all the tables, attached that gdb to my system/Fedora's Firefox
(Fedora's debug packages uses .gdb_index), did "set max-completions
unlimited", and then hit "b [TAB]" to cause everything to expand.

That resulted in 1351355 name_components.  Each entry takes 8 bytes,
so that's 10810840 bytes (ignoring std::vector overhead), or ~10.3 MB.
That's IMO too small to worry about, given GDB was using over 7400MB
total at that point.  I.e., we're talking about 0.1% increase.

dw2_expand_symtabs_matching unit tests covering this will be added in
a follow up patch.

If the size of this table turns out to be a concern, I have an idea to
reduce the size of the table further at the expense of a bit more code
-- the vast majority of the name offsets are either 0 or fit in
8-bits:

 total name_component = 1351355, of which,
 name_component::name_offset instances need  0 bits = 679531
 name_component::name_offset instances need  8 bits = 669526
 name_component::name_offset instances need 16 bits = 2298
 name_component::name_offset instances need 32 bits = 0
 name_component::idx instances need 0 bits  = 51
 name_component::idx instances need 8 bits  = 8361
 name_component::idx instances need 16 bits = 280329
 name_component::idx instances need 32 bits = 1062614

so we could have separate tables for 0 name_offset, 8-bit name_offset
and 32-bit name_offset.  That'd give us roughly:

 679531 * 0 + 669526 * 1 + 2298 * 4 + 1062614 * 4 = 4929174, or ~4.7MB

with only 8-bit and 32-bit tables, that'd be:

 1349057 * 1 + 2298 * 4 + 4 * 1351355 = 6763669 bytes, or ~6.5MB.

I don't think we need to bother though.

I also timed:

 $ time gdb --batch -q -p `pidof firefox`
 $ time gdb --batch -q -p `pidof firefox` -ex "b main"
 $ time gdb --batch -q -p `pidof firefox` -ex "set max-completion unlimited" -ex "complete b "

and compared before previous patch vs this patch, and I didn't see a
significant difference, seemingly because time to read debug info
dominates.  The "complete b " variant of the test takes ~2min
currently...  (I have a follow up series that speeds that up
somewhat.)

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* dwarf2read.c (byte_swap, MAYBE_SWAP): Move higher up in file.
	(struct name_component): New.
	(mapped_index::name_components): New field.
	(mapped_index::symbol_name_at): New method.
	(create_addrmap_from_index): Call mapped_index ctor.
	(dw2_map_matching_symbols): Add comment about name_components
	table.
	(dw2_expand_symtabs_matching): Factor part to...
	(dw2_expand_symtabs_matching_symbol): ... this new function.
	Build name components table, and lookup symbols in it before
	calling the name matcher.
	(dw2_expand_marked_cus): New, factored out from
	dw2_expand_symtabs_matching.
	(dwarf2_per_objfile_free): Call the mapped_index's dtor.
---
 gdb/dwarf2read.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 281 insertions(+), 42 deletions(-)

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index f523326..e955131 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -178,6 +178,51 @@ DEF_VEC_I (offset_type);
     GDB_INDEX_CU_SET_VALUE((cu_index), (value)); \
   } while (0)
 
+#if WORDS_BIGENDIAN
+
+/* Convert VALUE between big- and little-endian.  */
+static offset_type
+byte_swap (offset_type value)
+{
+  offset_type result;
+
+  result = (value & 0xff) << 24;
+  result |= (value & 0xff00) << 8;
+  result |= (value & 0xff0000) >> 8;
+  result |= (value & 0xff000000) >> 24;
+  return result;
+}
+
+#define MAYBE_SWAP(V)  byte_swap (V)
+
+#else
+#define MAYBE_SWAP(V) (V)
+#endif /* WORDS_BIGENDIAN */
+
+/* An index into a (C++) symbol name component in a symbol name as
+   recorded in the mapped_index's symbol table.  For each C++ symbol
+   in the symbol table, we record one entry for the start of each
+   component in the symbol in a table of name components, and then
+   sort the table, in order to be able to binary search symbol names,
+   ignoring leading namespaces, both completion and regular look up.
+   For example, for symbol "A::B::C", we'll have an entry that points
+   to "A::B::C", another that points to "B::C", and another for "C".
+   Note that function symbols in GDB index have no parameter
+   information, just the function/method names.  You can convert a
+   name_component to a "const char *" using the
+   'mapped_index::symbol_name_at(offset_type)' method.  */
+struct name_component
+{
+  /* Offset in the symbol name where the component starts.  Stored as
+     a (32-bit) offset instead of a pointer to save memory and improve
+     locality on 64-bit architectures.  */
+  offset_type name_offset;
+
+  /* The symbol's index in the symbol and constant pool tables of a
+     mapped_index.  */
+  offset_type idx;
+};
+
 /* A description of the mapped index.  The file format is described in
    a comment by the code that writes the index.  */
 struct mapped_index
@@ -202,6 +247,15 @@ struct mapped_index
 
   /* A pointer to the constant pool.  */
   const char *constant_pool;
+
+  /* The name_component table (a sorted vector).  See name_component's
+     description above.  */
+  std::vector<name_component> name_components;
+
+  /* Convenience method to get at the name of the symbol at IDX in the
+     symbol table.  */
+  const char *symbol_name_at (offset_type idx) const
+  { return this->constant_pool + MAYBE_SWAP (this->symbol_table[idx]); }
 };
 
 typedef struct dwarf2_per_cu_data *dwarf2_per_cu_ptr;
@@ -2131,26 +2185,6 @@ line_header_eq_voidp (const void *item_lhs, const void *item_rhs)
 }
 
 \f
-#if WORDS_BIGENDIAN
-
-/* Convert VALUE between big- and little-endian.  */
-static offset_type
-byte_swap (offset_type value)
-{
-  offset_type result;
-
-  result = (value & 0xff) << 24;
-  result |= (value & 0xff00) << 8;
-  result |= (value & 0xff0000) >> 8;
-  result |= (value & 0xff000000) >> 24;
-  return result;
-}
-
-#define MAYBE_SWAP(V)  byte_swap (V)
-
-#else
-#define MAYBE_SWAP(V) (V)
-#endif /* WORDS_BIGENDIAN */
 
 /* Read the given attribute value as an address, taking the attribute's
    form into account.  */
@@ -3390,6 +3424,7 @@ dwarf2_read_index (struct objfile *objfile)
   create_addrmap_from_index (objfile, &local_map);
 
   map = XOBNEW (&objfile->objfile_obstack, struct mapped_index);
+  map = new (map) mapped_index ();
   *map = local_map;
 
   dwarf2_per_objfile->index_table = map;
@@ -4016,7 +4051,11 @@ dw2_map_matching_symbols (struct objfile *objfile,
 
      Since each language has its own symbol name matching algorithm,
      and we don't know which language is the right one, we must match
-     each symbol against all languages.
+     each symbol against all languages.  This would be a potential
+     performance problem if it were not mitigated by the
+     mapped_index::name_components lookup table, which significantly
+     reduces the number of times we need to call into this matcher,
+     making it a non-issue.
 
    - Symbol names in the index have no overload (parameter)
      information. I.e., in C++, "foo(int)" and "foo(long)" both appear
@@ -4095,6 +4134,22 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
 }
 
 static void
+dw2_expand_marked_cus
+  (mapped_index &index, offset_type idx,
+   struct objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+   search_domain kind);
+
+static void
+dw2_expand_symtabs_matching_symbol
+  (mapped_index &index,
+   const lookup_name_info &lookup_name_in,
+   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+   enum search_domain kind,
+   gdb::function_view<void (offset_type)> on_match);
+
+static void
 dw2_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
@@ -4105,14 +4160,12 @@ dw2_expand_symtabs_matching
 {
   int i;
   offset_type iter;
-  struct mapped_index *index;
 
   dw2_setup (objfile);
 
   /* index_table is NULL if OBJF_READNOW.  */
   if (!dwarf2_per_objfile->index_table)
     return;
-  index = dwarf2_per_objfile->index_table;
 
   if (file_matcher != NULL)
     {
@@ -4186,30 +4239,214 @@ dw2_expand_symtabs_matching
 	}
     }
 
-  gdb_index_symbol_name_matcher lookup_name_matcher (lookup_name);
+  mapped_index &index = *dwarf2_per_objfile->index_table;
 
-  for (iter = 0; iter < index->symbol_table_slots; ++iter)
+  dw2_expand_symtabs_matching_symbol (index, lookup_name,
+				      symbol_matcher,
+				      kind, [&] (offset_type idx)
     {
-      offset_type idx = 2 * iter;
-      const char *name;
-      offset_type *vec, vec_len, vec_idx;
-      int global_seen = 0;
+      dw2_expand_marked_cus (index, idx, objfile, file_matcher,
+			     expansion_notify, kind);
+    });
+}
 
-      QUIT;
+/* Helper for dw2_expand_symtabs_matching that works with a
+   mapped_index instead of the containing objfile.  This is split to a
+   separate function in order to be able to unit test the
+   name_components matching using a mock mapped_index.  For each
+   symbol name that matches, calls MATCH_CALLBACK, passing it the
+   symbol's index in the mapped_index symbol table.  */
 
-      if (index->symbol_table[idx] == 0 && index->symbol_table[idx + 1] == 0)
-	continue;
+static void
+dw2_expand_symtabs_matching_symbol
+  (mapped_index &index,
+   const lookup_name_info &lookup_name,
+   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+   enum search_domain kind,
+   gdb::function_view<void (offset_type)> match_callback)
+{
+  gdb_index_symbol_name_matcher lookup_name_matcher
+    (lookup_name);
+
+  auto *name_cmp = case_sensitivity == case_sensitive_on ? strcmp : strcasecmp;
+
+  /* Build the symbol name component sorted vector, if we haven't yet.
+     The code below only knows how to break apart components of C++
+     symbol names (and other languages that use '::' as
+     namespace/module separator).  If we add support for wild matching
+     to some language that uses some other operator (E.g., Ada, Go and
+     D use '.'), then we'll need to try splitting the symbol name
+     according to that language too.  Note that Ada does support wild
+     matching, but doesn't currently support .gdb_index.  */
+  if (index.name_components.empty ())
+    {
+      for (size_t iter = 0; iter < index.symbol_table_slots; ++iter)
+	{
+	  offset_type idx = 2 * iter;
+
+	  if (index.symbol_table[idx] == 0
+	      && index.symbol_table[idx + 1] == 0)
+	    continue;
+
+	  const char *name = index.symbol_name_at (idx);
+
+	  /* Add each name component to the name component table.  */
+	  unsigned int previous_len = 0;
+	  for (unsigned int current_len = cp_find_first_component (name);
+	       name[current_len] != '\0';
+	       current_len += cp_find_first_component (name + current_len))
+	    {
+	      gdb_assert (name[current_len] == ':');
+	      index.name_components.push_back ({previous_len, idx});
+	      /* Skip the '::'.  */
+	      current_len += 2;
+	      previous_len = current_len;
+	    }
+	  index.name_components.push_back ({previous_len, idx});
+	}
+
+      /* Sort name_comp elements by name.   */
+      auto name_comp_compare = [&] (const name_component &left,
+				    const name_component &right)
+	{
+	  const char *left_qualified = index.symbol_name_at (left.idx);
+	  const char *right_qualified = index.symbol_name_at (right.idx);
+
+	  const char *left_name = left_qualified + left.name_offset;
+	  const char *right_name = right_qualified + right.name_offset;
+
+	  return name_cmp (left_name, right_name) < 0;
+	};
+
+      std::sort (index.name_components.begin (),
+		 index.name_components.end (),
+		 name_comp_compare);
+    }
+
+  const char *cplus
+    = lookup_name.cplus ().lookup_name ().c_str ();
 
-      name = index->constant_pool + MAYBE_SWAP (index->symbol_table[idx]);
+  /* Comparison function object for lower_bound that matches against a
+     given symbol name.  */
+  auto lookup_compare_lower = [&] (const name_component &elem,
+				   const char *name)
+    {
+      const char *elem_qualified = index.symbol_name_at (elem.idx);
+      const char *elem_name = elem_qualified + elem.name_offset;
+      return name_cmp (elem_name, name) < 0;
+    };
+
+  /* Comparison function object for upper_bound that matches against a
+     given symbol name.  */
+  auto lookup_compare_upper = [&] (const char *name,
+				   const name_component &elem)
+    {
+      const char *elem_qualified = index.symbol_name_at (elem.idx);
+      const char *elem_name = elem_qualified + elem.name_offset;
+      return name_cmp (name, elem_name) < 0;
+    };
+
+  auto begin = index.name_components.begin ();
+  auto end = index.name_components.end ();
+
+  /* Find the lower bound.  */
+  auto lower = [&] ()
+    {
+      if (lookup_name.completion_mode () && cplus[0] == '\0')
+	return begin;
+      else
+	return std::lower_bound (begin, end, cplus, lookup_compare_lower);
+    } ();
 
-      if (!lookup_name_matcher.matches (name)
-	  || (symbol_matcher != NULL && !symbol_matcher (name)))
+  /* Find the upper bound.  */
+  auto upper = [&] ()
+    {
+      if (lookup_name.completion_mode ())
+	{
+	  /* The string frobbing below won't work if the string is
+	     empty.  We don't need it then, anyway -- if we're
+	     completing an empty string, then we want to iterate over
+	     the whole range.  */
+	  if (cplus[0] == '\0')
+	    return end;
+
+	  /* In completion mode, increment the last character because
+	     we want UPPER to point past all symbols names that have
+	     the same prefix.  */
+	  std::string after = cplus;
+
+	  gdb_assert (after.back () != 0xff);
+	  after.back ()++;
+
+	  return std::upper_bound (lower, end, after.c_str (),
+				   lookup_compare_upper);
+	}
+      else
+	return std::upper_bound (lower, end, cplus, lookup_compare_upper);
+    } ();
+
+  /* Now for each symbol name in range, check to see if we have a name
+     match, and if so, call the MATCH_CALLBACK callback.  */
+
+  /* The same symbol may appear more than once in the range though.
+     E.g., if we're looking for symbols that complete "w", and we have
+     a symbol named "w1::w2", we'll find the two name components for
+     that same symbol in the range.  To be sure we only call the
+     callback once per symbol, we first collect the symbol name
+     indexes that matched in a temporary vector and ignore
+     duplicates.  */
+  std::vector<offset_type> matches;
+  matches.reserve (std::distance (lower, upper));
+
+  for (;lower != upper; ++lower)
+    {
+      const char *qualified = index.symbol_name_at (lower->idx);
+
+      if (!lookup_name_matcher.matches (qualified)
+	  || (symbol_matcher != NULL && !symbol_matcher (qualified)))
 	continue;
 
-      /* The name was matched, now expand corresponding CUs that were
-	 marked.  */
-      vec = (offset_type *) (index->constant_pool
-			     + MAYBE_SWAP (index->symbol_table[idx + 1]));
+      matches.push_back (lower->idx);
+    }
+
+  std::sort (matches.begin (), matches.end ());
+
+  /* Finally call the callback, once per match.  */
+  ULONGEST prev = -1;
+  for (offset_type idx : matches)
+    {
+      if (prev != idx)
+	{
+	  match_callback (idx);
+	  prev = idx;
+	}
+    }
+
+  /* Above we use a type wider than idx's for 'prev', since 0 and
+     (offset_type)-1 are both possible values.  */
+  static_assert (sizeof (prev) > sizeof (offset_type), "");
+}
+
+/* Helper for dw2_expand_matching symtabs.  Called on each symbol
+   matched, to expand corresponding CUs that were marked.  IDX is the
+   index of the symbol name that matched.  */
+
+static void
+dw2_expand_marked_cus
+  (mapped_index &index, offset_type idx,
+   struct objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+   search_domain kind)
+{
+  const char *name;
+  offset_type *vec, vec_len, vec_idx;
+  bool global_seen = false;
+
+  /* XXX reindent code below before pushing.  */
+
+      vec = (offset_type *) (index.constant_pool
+			     + MAYBE_SWAP (index.symbol_table[idx + 1]));
       vec_len = MAYBE_SWAP (vec[0]);
       for (vec_idx = 0; vec_idx < vec_len; ++vec_idx)
 	{
@@ -4225,7 +4462,7 @@ dw2_expand_symtabs_matching
 	     and indices >= 7 may elide them for certain symbols
 	     (gold does this).  */
 	  int attrs_valid =
-	    (index->version >= 7
+	    (index.version >= 7
 	     && symbol_kind != GDB_INDEX_SYMBOL_KIND_NONE);
 
 	  /* Work around gold/15646.  */
@@ -4234,7 +4471,7 @@ dw2_expand_symtabs_matching
 	      if (!is_static && global_seen)
 		continue;
 	      if (!is_static)
-		global_seen = 1;
+		global_seen = true;
 	    }
 
 	  /* Only check the symbol's kind if it has one.  */
@@ -4285,7 +4522,6 @@ dw2_expand_symtabs_matching
 		}
 	    }
 	}
-    }
 }
 
 /* A helper for dw2_find_pc_sect_compunit_symtab which finds the most specific
@@ -23285,6 +23521,9 @@ dwarf2_per_objfile_free (struct objfile *objfile, void *d)
 
   if (data->dwz_file && data->dwz_file->dwz_bfd)
     gdb_bfd_unref (data->dwz_file->dwz_bfd);
+
+  if (data->index_table != NULL)
+    data->index_table->~mapped_index ();
 }
 
 \f
-- 
2.5.5

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

* [PATCH 23/40] Make language_def O(1)
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (37 preceding siblings ...)
  2017-06-02 12:39 ` [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching Pedro Alves
@ 2017-06-02 12:39 ` Pedro Alves
  2017-07-17 23:03   ` Keith Seitz
  2017-06-02 12:39 ` [PATCH 26/40] Optimize .gdb_index symbol name searching Pedro Alves
  2017-06-02 15:26 ` [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
  40 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 12:39 UTC (permalink / raw)
  To: gdb-patches

Profiling GDB with the rest of series applied, I saw calls to
language_def showing up high in some runs.  The problem is that
language_def is O(N) currently, since walk the languages vector each
time to find the matching language_defn.

IMO, the add_language mechanism is pointless, because "enum language"
implies the core of GDB needs to know about all languages anyway.  So
simply make the languages vector array be an array where each
element's index is the corresponding enum language enumerator.  Note
that "local_language_defn" is gone along the way.  AFAICT, it's just a
copy of "auto", so the new code simply maps one to the other.  One
fewer place to update when we need to change the language vector...

Also, a while ago the output of "set language" change and was made out
of order as side effect of some other change.  While I was at it, I
made them sorted again.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_language_defn): Make extern.
	(_initialize_ada_language): Remove add_language call.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Make extern.
	(_initialize_c_language): Delete.
	* d-lang.c (d_language_defn): Make extern.
	(_initialize_d_language): Remove add_language calls.
	* defs.h (enum language): Add comment.
	* f-lang.c (f_language_defn): Make extern.
	(_initialize_f_language): Remove add_language call.
	* go-lang.c (go_language_defn): Make extern.
	(_initialize_go_language): Remove add_language call.
	* language.c: Include <algorithm>.
	(languages): Redefine as const array.
	(languages_size, languages_allocsize, DEFAULT_ALLOCSIZE): Delete.
	(set_language_command): Handle "local".  Use for-range loop.
	(set_language): Remove loop.
	(language_enum): Rewrite.
	(language_def, language_str): Remove loops.
	(add_language): Delete.
	(compare_cstrings): New.
	(build_language_commands): New, based on add_languages.
	(skip_language_trampoline): Adjust.
	(local_language_defn): Delete.
	(language_gdbarch_post_init): Adjust.
	(_initialize_language): Remove add_language calls.  Call
	build_language_commands.
	* language.h (add_language): Delete.
	(auto_language_defn)
	(unknown_language_defn, minimal_language_defn, ada_language_defn)
	(asm_language_defn, c_language_defn, cplus_language_defn)
	(d_language_defn, f_language_defn, go_language_defn)
	(m2_language_defn, objc_language_defn, opencl_language_defn)
	(pascal_language_defn, rust_language_defn): Declare.
	* m2-lang.c (m2_language_defn): Make extern.
	(_initialize_m2_language): Remove add_language call.
	* objc-lang.c (objc_language_defn): Make extern.
	(_initialize_objc_language): Remove add_language call.
	* opencl-lang.c (opencl_language_defn): Make extern.
	(_initialize_opencl_language): Remove add_language call.
	* p-lang.c (pascal_language_defn): Make extern.
	(_initialize_pascal_language): Delete.
	* rust-lang.c (rust_language_defn): Make extern.
	(_initialize_rust_language): Delete.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/default.exp (set language): Adjust expected output.
---
 gdb/ada-lang.c                     |   4 +-
 gdb/c-lang.c                       |  19 +--
 gdb/d-lang.c                       |   4 +-
 gdb/defs.h                         |   3 +-
 gdb/f-lang.c                       |   4 +-
 gdb/go-lang.c                      |   4 +-
 gdb/language.c                     | 252 ++++++++++++++-----------------------
 gdb/language.h                     |  23 +++-
 gdb/m2-lang.c                      |   4 +-
 gdb/objc-lang.c                    |   3 +-
 gdb/opencl-lang.c                  |   3 +-
 gdb/p-lang.c                       |  11 +-
 gdb/rust-lang.c                    |  10 +-
 gdb/testsuite/gdb.base/default.exp |   2 +-
 14 files changed, 129 insertions(+), 217 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index c5f7f8c..f582c56 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13992,7 +13992,7 @@ static const char *ada_extensions[] =
   ".adb", ".ads", ".a", ".ada", ".dg", NULL
 };
 
-const struct language_defn ada_language_defn = {
+extern const struct language_defn ada_language_defn = {
   "ada",                        /* Language name */
   "Ada",
   language_ada,
@@ -14125,8 +14125,6 @@ ada_free_objfile_observer (struct objfile *objfile)
 void
 _initialize_ada_language (void)
 {
-  add_language (&ada_language_defn);
-
   initialize_ada_catchpoint_ops ();
 
   add_prefix_cmd ("ada", no_class, set_ada_command,
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 695b237..f86e26e 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -35,8 +35,6 @@
 #include <ctype.h>
 #include "gdbcore.h"
 
-extern void _initialize_c_language (void);
-
 /* Given a C string type, STR_TYPE, return the corresponding target
    character set name.  */
 
@@ -831,7 +829,7 @@ static const char *c_extensions[] =
   ".c", NULL
 };
 
-const struct language_defn c_language_defn =
+extern const struct language_defn c_language_defn =
 {
   "c",				/* Language name */
   "C",
@@ -975,7 +973,7 @@ static const char *cplus_extensions[] =
   ".C", ".cc", ".cp", ".cpp", ".cxx", ".c++", NULL
 };
 
-const struct language_defn cplus_language_defn =
+extern const struct language_defn cplus_language_defn =
 {
   "c++",			/* Language name */
   "C++",
@@ -1028,7 +1026,7 @@ static const char *asm_extensions[] =
   ".s", ".sx", ".S", NULL
 };
 
-const struct language_defn asm_language_defn =
+extern const struct language_defn asm_language_defn =
 {
   "asm",			/* Language name */
   "assembly",
@@ -1081,7 +1079,7 @@ const struct language_defn asm_language_defn =
    to do some simple operations when debugging applications that use
    a language currently not supported by GDB.  */
 
-const struct language_defn minimal_language_defn =
+extern const struct language_defn minimal_language_defn =
 {
   "minimal",			/* Language name */
   "Minimal",
@@ -1128,12 +1126,3 @@ const struct language_defn minimal_language_defn =
   NULL,
   LANG_MAGIC
 };
-
-void
-_initialize_c_language (void)
-{
-  add_language (&c_language_defn);
-  add_language (&cplus_language_defn);
-  add_language (&asm_language_defn);
-  add_language (&minimal_language_defn);
-}
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 74cceaa..941d3ed 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -204,7 +204,7 @@ static const char *d_extensions[] =
   ".d", NULL
 };
 
-static const struct language_defn d_language_defn =
+extern const struct language_defn d_language_defn =
 {
   "d",
   "D",
@@ -349,6 +349,4 @@ void
 _initialize_d_language (void)
 {
   d_type_data = gdbarch_data_register_post_init (build_d_types);
-
-  add_language (&d_language_defn);
 }
diff --git a/gdb/defs.h b/gdb/defs.h
index a1a97bb..a5f2772 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -198,7 +198,8 @@ extern void quit_serial_event_clear (void);
    several languages.  For that reason, the constants here are sorted
    in the order we'll attempt demangling them.  For example: Rust uses
    C++ mangling, so must come after C++; Ada must come last (see
-   ada_sniff_from_mangled_name).  */
+   ada_sniff_from_mangled_name).  (Keep this order in sync with the
+   'languages' array in language.c.)  */
 
 enum language
   {
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 937ebff..903cfd1 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -245,7 +245,7 @@ static const char *f_extensions[] =
   NULL
 };
 
-const struct language_defn f_language_defn =
+extern const struct language_defn f_language_defn =
 {
   "fortran",
   "Fortran",
@@ -369,6 +369,4 @@ void
 _initialize_f_language (void)
 {
   f_type_data = gdbarch_data_register_post_init (build_fortran_types);
-
-  add_language (&f_language_defn);
 }
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index f3b4f5c..60bb3c5 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -565,7 +565,7 @@ go_language_arch_info (struct gdbarch *gdbarch,
   lai->bool_type_default = builtin->builtin_bool;
 }
 
-static const struct language_defn go_language_defn =
+extern const struct language_defn go_language_defn =
 {
   "go",
   "Go",
@@ -676,6 +676,4 @@ void
 _initialize_go_language (void)
 {
   go_type_data = gdbarch_data_register_post_init (build_go_types);
-
-  add_language (&go_language_defn);
 }
diff --git a/gdb/language.c b/gdb/language.c
index d30f4f0..df4f3cd 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -44,6 +44,7 @@
 #include "cp-support.h"
 #include "frame.h"
 #include "c-lang.h"
+#include <algorithm>
 
 extern void _initialize_language (void);
 
@@ -91,12 +92,26 @@ enum language_mode language_mode = language_mode_auto;
 
 const struct language_defn *expected_language;
 
-/* The list of supported languages.  The list itself is malloc'd.  */
-
-static const struct language_defn **languages;
-static unsigned languages_size;
-static unsigned languages_allocsize;
-#define	DEFAULT_ALLOCSIZE 4
+/* The list of supported languages.  Keep this in the same order as
+   the 'enum language' values.  */
+
+static const struct language_defn *languages[] = {
+  &unknown_language_defn,
+  &auto_language_defn,
+  &c_language_defn,
+  &objc_language_defn,
+  &cplus_language_defn,
+  &d_language_defn,
+  &go_language_defn,
+  &f_language_defn,
+  &m2_language_defn,
+  &asm_language_defn,
+  &pascal_language_defn,
+  &opencl_language_defn,
+  &rust_language_defn,
+  &minimal_language_defn,
+  &ada_language_defn,
+};
 
 /* The current values of the "set language/type/range" enum
    commands.  */
@@ -148,16 +163,19 @@ show_language_command (struct ui_file *file, int from_tty,
 static void
 set_language_command (char *ignore, int from_tty, struct cmd_list_element *c)
 {
-  int i;
   enum language flang = language_unknown;
 
+  /* "local" is a synonym of "auto".  */
+  if (strcmp (language, "local") == 0)
+    language = "auto";
+
   /* Search the list of languages for a match.  */
-  for (i = 0; i < languages_size; i++)
+  for (const auto &lang : languages)
     {
-      if (strcmp (languages[i]->la_name, language) == 0)
+      if (strcmp (lang->la_name, language) == 0)
 	{
 	  /* Found it!  Go into manual mode, and use this language.  */
-	  if (languages[i]->la_language == language_auto)
+	  if (lang->la_language == language_auto)
 	    {
 	      /* Enter auto mode.  Set to the current frame's language, if
                  known, or fallback to the initial language.  */
@@ -186,7 +204,7 @@ set_language_command (char *ignore, int from_tty, struct cmd_list_element *c)
 	    {
 	      /* Enter manual mode.  Set the specified language.  */
 	      language_mode = language_mode_manual;
-	      current_language = languages[i];
+	      current_language = lang;
 	      set_range_case ();
 	      expected_language = current_language;
 	      return;
@@ -364,21 +382,11 @@ set_range_case (void)
 enum language
 set_language (enum language lang)
 {
-  int i;
   enum language prev_language;
 
   prev_language = current_language->la_language;
-
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i]->la_language == lang)
-	{
-	  current_language = languages[i];
-	  set_range_case ();
-	  break;
-	}
-    }
-
+  current_language = languages[lang];
+  set_range_case ();
   return prev_language;
 }
 \f
@@ -474,11 +482,12 @@ range_error (const char *string,...)
 enum language
 language_enum (char *str)
 {
-  int i;
+  for (const auto &lang : languages)
+    if (strcmp (lang->la_name, str) == 0)
+      return lang->la_language;
 
-  for (i = 0; i < languages_size; i++)
-    if (strcmp (languages[i]->la_name, str) == 0)
-      return languages[i]->la_language;
+  if (strcmp (str, "local") == 0)
+    return language_auto;
 
   return language_unknown;
 }
@@ -488,32 +497,15 @@ language_enum (char *str)
 const struct language_defn *
 language_def (enum language lang)
 {
-  int i;
-
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i]->la_language == lang)
-	{
-	  return languages[i];
-	}
-    }
-  return NULL;
+  return languages[lang];
 }
 
 /* Return the language as a string.  */
+
 const char *
 language_str (enum language lang)
 {
-  int i;
-
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i]->la_language == lang)
-	{
-	  return languages[i]->la_name;
-	}
-    }
-  return "Unknown";
+  return languages[lang]->la_name;
 }
 
 static void
@@ -530,55 +522,51 @@ show_check (char *ignore, int from_tty)
   cmd_show_list (showchecklist, from_tty, "");
 }
 \f
-/* Add a language to the set of known languages.  */
 
-void
-add_language (const struct language_defn *lang)
+/* Compare C strings for std::sort.  */
+
+static bool
+compare_cstrings (const char *str1, const char *str2)
 {
-  /* For the "set language" command.  */
-  static const char **language_names = NULL;
-  /* For the "help set language" command.  */
+  return strcmp (str1, str2) < 0;
+}
 
-  if (lang->la_magic != LANG_MAGIC)
-    {
-      fprintf_unfiltered (gdb_stderr,
-			  "Magic number of %s language struct wrong\n",
-			  lang->la_name);
-      internal_error (__FILE__, __LINE__,
-		      _("failed internal consistency check"));
-    }
+static void
+build_language_commands ()
+{
+  static const char **language_names;
 
-  if (!languages)
-    {
-      languages_allocsize = DEFAULT_ALLOCSIZE;
-      languages = XNEWVEC (const struct language_defn *, languages_allocsize);
-    }
-  if (languages_size >= languages_allocsize)
+  /* Build the language names array, to be used as enumeration in the
+     "set language" enum command.  +1 for "local" and +1 for NULL
+     termination.  */
+  language_names = new const char *[ARRAY_SIZE (languages) + 2];
+
+  /* Display "auto", "local" and "unknown" first, and then the rest,
+     alpha sorted.  */
+  const char **language_names_p = language_names;
+  *language_names_p++ = auto_language_defn.la_name;
+  *language_names_p++ = "local";
+  *language_names_p++ = unknown_language_defn.la_name;
+  const char **sort_begin = language_names_p;
+  for (const auto &lang : languages)
     {
-      languages_allocsize *= 2;
-      languages = (const struct language_defn **) xrealloc ((char *) languages,
-				 languages_allocsize * sizeof (*languages));
+      /* Already handled above.  */
+      if (lang->la_language == language_auto
+	  || lang->la_language == language_unknown)
+	continue;
+      *language_names_p++ = lang->la_name;
     }
-  languages[languages_size++] = lang;
-
-  /* Build the language names array, to be used as enumeration in the
-     set language" enum command.  */
-  language_names = XRESIZEVEC (const char *, language_names,
-			       languages_size + 1);
-
-  for (int i = 0; i < languages_size; ++i)
-    language_names[i] = languages[i]->la_name;
-  language_names[languages_size] = NULL;
+  *language_names_p = NULL;
+  std::sort (sort_begin, language_names_p, compare_cstrings);
 
   /* Add the filename extensions.  */
-  if (lang->la_filename_extensions != NULL)
-    {
-      int i;
-
-      for (i = 0; lang->la_filename_extensions[i] != NULL; ++i)
-	add_filename_language (lang->la_filename_extensions[i],
-			       lang->la_language);
-    }
+  for (const auto &lang : languages)
+    if (lang->la_filename_extensions != NULL)
+      {
+	for (size_t i = 0; lang->la_filename_extensions[i] != NULL; ++i)
+	  add_filename_language (lang->la_filename_extensions[i],
+				 lang->la_language);
+      }
 
   /* Build the "help set language" docs.  */
   string_file doc;
@@ -587,24 +575,24 @@ add_language (const struct language_defn *lang)
 		"The currently understood settings are:\n\nlocal or "
 		"auto    Automatic setting based on source file\n"));
 
-  for (int i = 0; i < languages_size; ++i)
+  for (const auto &lang : languages)
     {
       /* Already dealt with these above.  */
-      if (languages[i]->la_language == language_unknown
-	  || languages[i]->la_language == language_auto)
+      if (lang->la_language == language_unknown
+	  || lang->la_language == language_auto)
 	continue;
 
       /* FIXME: i18n: for now assume that the human-readable name is
 	 just a capitalization of the internal name.  */
       doc.printf ("%-16s Use the %c%s language\n",
-		  languages[i]->la_name,
+		  lang->la_name,
 		  /* Capitalize first letter of language name.  */
-		  toupper (languages[i]->la_name[0]),
-		  languages[i]->la_name + 1);
+		  toupper (lang->la_name[0]),
+		  lang->la_name + 1);
     }
 
   add_setshow_enum_cmd ("language", class_support,
-			(const char **) language_names,
+			language_names,
 			&language,
 			doc.c_str (),
 			_("Show the current source language."),
@@ -620,13 +608,11 @@ add_language (const struct language_defn *lang)
 CORE_ADDR 
 skip_language_trampoline (struct frame_info *frame, CORE_ADDR pc)
 {
-  int i;
-
-  for (i = 0; i < languages_size; i++)
+  for (const auto &lang : languages)
     {
-      if (languages[i]->skip_trampoline)
+      if (lang->skip_trampoline != NULL)
 	{
-	  CORE_ADDR real_pc = (languages[i]->skip_trampoline) (frame, pc);
+	  CORE_ADDR real_pc = lang->skip_trampoline (frame, pc);
 
 	  if (real_pc)
 	    return real_pc;
@@ -919,53 +905,6 @@ const struct language_defn auto_language_defn =
   LANG_MAGIC
 };
 
-const struct language_defn local_language_defn =
-{
-  "local",
-  "Local",
-  language_auto,
-  range_check_off,
-  case_sensitive_on,
-  array_row_major,
-  macro_expansion_no,
-  NULL,
-  &exp_descriptor_standard,
-  unk_lang_parser,
-  unk_lang_error,
-  null_post_parser,
-  unk_lang_printchar,		/* Print character constant */
-  unk_lang_printstr,
-  unk_lang_emit_char,
-  unk_lang_print_type,		/* Print a type using appropriate syntax */
-  default_print_typedef,	/* Print a typedef using appropriate syntax */
-  unk_lang_val_print,		/* Print a value using appropriate syntax */
-  unk_lang_value_print,		/* Print a top-level value */
-  default_read_var_value,	/* la_read_var_value */
-  unk_lang_trampoline,		/* Language specific skip_trampoline */
-  "this", 		        /* name_of_this */
-  basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
-  basic_lookup_transparent_type,/* lookup_transparent_type */
-  unk_lang_demangle,		/* Language specific symbol demangler */
-  NULL,
-  unk_lang_class_name,		/* Language specific
-				   class_name_from_physname */
-  unk_op_print_tab,		/* expression operators for printing */
-  1,				/* c-style arrays */
-  0,				/* String lower bound */
-  default_word_break_characters,
-  default_collect_symbol_completion_matches,
-  unknown_language_arch_info,	/* la_language_arch_info.  */
-  default_print_array_index,
-  default_pass_by_reference,
-  default_get_string,
-  c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
-  iterate_over_symbols,
-  &default_varobj_ops,
-  NULL,
-  NULL,
-  LANG_MAGIC
-};
 \f
 /* Per-architecture language information.  */
 
@@ -982,16 +921,15 @@ static void *
 language_gdbarch_post_init (struct gdbarch *gdbarch)
 {
   struct language_gdbarch *l;
-  int i;
 
   l = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct language_gdbarch);
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i] != NULL
-	  && languages[i]->la_language_arch_info != NULL)
-	languages[i]->la_language_arch_info
-	  (gdbarch, l->arch_info + languages[i]->la_language);
-    }
+  for (const auto &lang : languages)
+    if (lang != NULL && lang->la_language_arch_info != NULL)
+      {
+	lang->la_language_arch_info (gdbarch,
+				     l->arch_info + lang->la_language);
+      }
+
   return l;
 }
 
@@ -1205,9 +1143,7 @@ For Fortran the default is off; for other languages the default is on."),
 			show_case_command,
 			&setlist, &showlist);
 
-  add_language (&auto_language_defn);
-  add_language (&local_language_defn);
-  add_language (&unknown_language_defn);
+  build_language_commands ();
 
   language = xstrdup ("auto");
   type = xstrdup ("auto");
diff --git a/gdb/language.h b/gdb/language.h
index 75b9438..f4852c1 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -568,10 +568,6 @@ extern const struct language_defn *language_def (enum language);
 
 extern const char *language_str (enum language);
 
-/* Add a language to the set known by GDB (at initialization time).  */
-
-extern void add_language (const struct language_defn *);
-
 /* Check for a language-specific trampoline.  */
 
 extern CORE_ADDR skip_language_trampoline (struct frame_info *, CORE_ADDR pc);
@@ -618,4 +614,23 @@ void default_get_string (struct value *value, gdb_byte **buffer, int *length,
 void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 		   struct type **char_type, const char **charset);
 
+/* The languages supported by GDB.  */
+
+extern const struct language_defn auto_language_defn;
+extern const struct language_defn unknown_language_defn;
+extern const struct language_defn minimal_language_defn;
+
+extern const struct language_defn ada_language_defn;
+extern const struct language_defn asm_language_defn;
+extern const struct language_defn c_language_defn;
+extern const struct language_defn cplus_language_defn;
+extern const struct language_defn d_language_defn;
+extern const struct language_defn f_language_defn;
+extern const struct language_defn go_language_defn;
+extern const struct language_defn m2_language_defn;
+extern const struct language_defn objc_language_defn;
+extern const struct language_defn opencl_language_defn;
+extern const struct language_defn pascal_language_defn;
+extern const struct language_defn rust_language_defn;
+
 #endif /* defined (LANGUAGE_H) */
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 0035a52..b9ab2b3 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -354,7 +354,7 @@ const struct exp_descriptor exp_descriptor_modula2 =
   evaluate_subexp_modula2
 };
 
-const struct language_defn m2_language_defn =
+extern const struct language_defn m2_language_defn =
 {
   "modula-2",
   "Modula-2",
@@ -439,6 +439,4 @@ void
 _initialize_m2_language (void)
 {
   m2_type_data = gdbarch_data_register_post_init (build_m2_types);
-
-  add_language (&m2_language_defn);
 }
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index af27268..6ffe85e 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -364,7 +364,7 @@ static const char *objc_extensions[] =
   ".m", NULL
 };
 
-const struct language_defn objc_language_defn = {
+extern const struct language_defn objc_language_defn = {
   "objective-c",		/* Language name */
   "Objective-C",
   language_objc,
@@ -1377,7 +1377,6 @@ extern initialize_file_ftype _initialize_objc_language;
 void
 _initialize_objc_language (void)
 {
-  add_language (&objc_language_defn);
   add_info ("selectors", selectors_info,    /* INFO SELECTORS command.  */
 	    _("All Objective-C selectors, or those matching REGEXP."));
   add_info ("classes", classes_info, 	    /* INFO CLASSES   command.  */
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index 116d107..9b0f015 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1043,7 +1043,7 @@ const struct exp_descriptor exp_descriptor_opencl =
   evaluate_subexp_opencl
 };
 
-const struct language_defn opencl_language_defn =
+extern const struct language_defn opencl_language_defn =
 {
   "opencl",			/* Language name */
   "OpenCL C",
@@ -1185,5 +1185,4 @@ void
 _initialize_opencl_language (void)
 {
   opencl_type_data = gdbarch_data_register_post_init (build_opencl_types);
-  add_language (&opencl_language_defn);
 }
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 77913fc..439a377 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -32,9 +32,6 @@
 #include <ctype.h>
 #include "c-lang.h"
 
-extern void _initialize_pascal_language (void);
-
-
 /* All GPC versions until now (2007-09-27) also define a symbol called
    '_p_initialize'.  Check for the presence of this symbol first.  */
 static const char GPC_P_INITIALIZE[] = "_p_initialize";
@@ -418,7 +415,7 @@ static const char *p_extensions[] =
   ".pas", ".p", ".pp", NULL
 };
 
-const struct language_defn pascal_language_defn =
+extern const struct language_defn pascal_language_defn =
 {
   "pascal",			/* Language name */
   "Pascal",
@@ -464,9 +461,3 @@ const struct language_defn pascal_language_defn =
   NULL,
   LANG_MAGIC
 };
-
-void
-_initialize_pascal_language (void)
-{
-  add_language (&pascal_language_defn);
-}
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index ef41f56..817976a 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -35,8 +35,6 @@
 #include <string>
 #include <vector>
 
-extern initialize_file_ftype _initialize_rust_language;
-
 /* Returns the last segment of a Rust path like foo::bar::baz.  Will
    not handle cases where the last segment contains generics.  This
    will return NULL if the last segment cannot be found.  */
@@ -2150,7 +2148,7 @@ static const char *rust_extensions[] =
   ".rs", NULL
 };
 
-static const struct language_defn rust_language_defn =
+extern const struct language_defn rust_language_defn =
 {
   "rust",
   "Rust",
@@ -2197,9 +2195,3 @@ static const struct language_defn rust_language_defn =
   NULL,
   LANG_MAGIC
 };
-
-void
-_initialize_rust_language (void)
-{
-  add_language (&rust_language_defn);
-}
diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 8c0b29c..c25069a 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -511,7 +511,7 @@ gdb_test "set history size" "Argument required .integer to set it to.*" "set his
 #test set history
 gdb_test "set history" "\"set history\" must be followed by the name of a history subcommand.(\[^\r\n\]*\[\r\n\])+List of set history subcommands:(\[^\r\n\]*\[\r\n\])+set history expansion -- Set history expansion on command input(\[^\r\n\]*\[\r\n\])+set history filename -- Set the filename in which to record the command history(\[^\r\n\]*\[\r\n\])+set history save -- Set saving of the history record on exit(\[^\r\n\]*\[\r\n\])+set history size -- Set the size of the command history(\[^\r\n\]*\[\r\n\])+Type \"help set history\" followed by set history subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set history"
 #test set language
-gdb_test "set language" "Requires an argument. Valid arguments are ada, c, c.., asm, minimal, d, fortran, go, auto, local, unknown, modula-2, objective-c, opencl, pascal, rust." "set language"
+gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, asm, c, c.., d, fortran, go, minimal, modula-2, objective-c, opencl, pascal, rust." "set language"
 #test set listsize
 gdb_test "set listsize" "Argument required .integer to set it to.*" "set listsize"
 #test set print "p" abbreviation
-- 
2.5.5

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-02 12:23 ` [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS) Pedro Alves
@ 2017-06-02 13:01   ` Eli Zaretskii
  2017-06-02 13:33     ` Pedro Alves
  2017-06-21 13:32     ` Pedro Alves
  0 siblings, 2 replies; 183+ messages in thread
From: Eli Zaretskii @ 2017-06-02 13:01 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> From: Pedro Alves <palves@redhat.com>
> Date: Fri,  2 Jun 2017 13:22:38 +0100
> 
> +For example, assuming a program with symbols named @code{A::B::func}
> +and @code{B::func}, both commands @code{break -function func} and
> +@code{break -function B::func} set a breakpoint on both symbols.

The 2 commands are long and include whitespace, so I'd suggest
enclosing each one in @w{..}, to avoid a line break in the middle of a
command.

Also, I think @kbd is more appropriate here than @code, since you mean
commands the user will type, not just command names.

I'm surprised you didn't change anything where the manual discusses
quoting of names and symbols.  For example, the node "Completion"
explicitly describes a use case with overloaded functions in C++; the
node "Symbols" describes a case with "::" that requires quoting.
There's another example in "Machine Code", and also in "Ambiguous
Expression", and in "Variables".  Maybe you already reviewed all of
those and concluded no changes were necessary, but I just thought I'd
mention them.

> +  ** GDB now has a much improved linespec and explicit locations TAB
> +     completion support, that better understands what you're
> +     completing and offers better suggestions.

Is this a general improvement, or is it limited to C++ symbols?  If
the latter, suggest to mention that.

> +  ** GDB can now complete function parameters in linespecs and
> +     explicit locations, even without quoting.  When setting
> +     breakpoints, quoting around functions names to help with
> +     TAB-completion is generally no longer necessary.

Likewise.

> +  ** GDB can now set breakpoints functions marked with [abi:cxx11]
> +     tags.

"on functions", I think.

And btw, what are those tags?  I don't think I see them documented in
the manual; did I miss something?

Thanks.

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-02 13:01   ` Eli Zaretskii
@ 2017-06-02 13:33     ` Pedro Alves
  2017-06-21 15:50       ` Pedro Alves
  2017-06-21 13:32     ` Pedro Alves
  1 sibling, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 13:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On 06/02/2017 02:01 PM, Eli Zaretskii wrote:
>> From: Pedro Alves <palves@redhat.com>
>> Date: Fri,  2 Jun 2017 13:22:38 +0100
>>
>> +For example, assuming a program with symbols named @code{A::B::func}
>> +and @code{B::func}, both commands @code{break -function func} and
>> +@code{break -function B::func} set a breakpoint on both symbols.
> 
> The 2 commands are long and include whitespace, so I'd suggest
> enclosing each one in @w{..}, to avoid a line break in the middle of a
> command.
> 
> Also, I think @kbd is more appropriate here than @code, since you mean
> commands the user will type, not just command names.
> 
> I'm surprised you didn't change anything where the manual discusses
> quoting of names and symbols.  For example, the node "Completion"
> explicitly describes a use case with overloaded functions in C++; the
> node "Symbols" describes a case with "::" that requires quoting.
> There's another example in "Machine Code", and also in "Ambiguous
> Expression", and in "Variables".  Maybe you already reviewed all of
> those and concluded no changes were necessary, but I just thought I'd
> mention them.

Thanks, I just didn't think of looking for quoting bits in the
manual, for some reason.  I'll take a closer look.

> 
>> +  ** GDB now has a much improved linespec and explicit locations TAB
>> +     completion support, that better understands what you're
>> +     completing and offers better suggestions.
> 
> Is this a general improvement, or is it limited to C++ symbols?  If
> the latter, suggest to mention that.

You're right.  It's more general than C++.

> 
>> +  ** GDB can now complete function parameters in linespecs and
>> +     explicit locations, even without quoting.  When setting
>> +     breakpoints, quoting around functions names to help with
>> +     TAB-completion is generally no longer necessary.
> 
> Likewise.
> 
>> +  ** GDB can now set breakpoints functions marked with [abi:cxx11]
>> +     tags.
> 
> "on functions", I think.
> 
> And btw, what are those tags?  

This is the best document that I know describing them:

 https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/

Functions that ended up requiring an abi tag are demangled like this:

  string_printf[abi:cxx11](char const*, ...)
               ^^^^^^^^^^^

That's an actual function in GDB.  It ended requiring an ABI tag
because it returns std::string.  See intro of patch 39 for more.

> I don't think I see them documented in the manual; did I miss something?

Hmm, I guess we could add something, indeed.

Thanks,
Pedro Alves

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

* Re: [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more
  2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
                   ` (39 preceding siblings ...)
  2017-06-02 12:39 ` [PATCH 26/40] Optimize .gdb_index symbol name searching Pedro Alves
@ 2017-06-02 15:26 ` Pedro Alves
  40 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-02 15:26 UTC (permalink / raw)
  To: GDB Patches

I realized that I forgot to mention that I pushed this to
the users/palves/cxx-breakpoint-improvements branch, so others
can conveniently try it without having to apply the patches manually.

Thanks,
Pedro Alves

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-02 13:01   ` Eli Zaretskii
  2017-06-02 13:33     ` Pedro Alves
@ 2017-06-21 13:32     ` Pedro Alves
  2017-06-21 18:26       ` Eli Zaretskii
  1 sibling, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-21 13:32 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

Hi Eli,

On 06/02/2017 02:01 PM, Eli Zaretskii wrote:
>> From: Pedro Alves <palves@redhat.com>
>> > Date: Fri,  2 Jun 2017 13:22:38 +0100
>> > 
>> > +For example, assuming a program with symbols named @code{A::B::func}
>> > +and @code{B::func}, both commands @code{break -function func} and
>> > +@code{break -function B::func} set a breakpoint on both symbols.
> The 2 commands are long and include whitespace, so I'd suggest
> enclosing each one in @w{..}, to avoid a line break in the middle of a
> command.
> 
> Also, I think @kbd is more appropriate here than @code, since you mean
> commands the user will type, not just command names.

I was addressing this comment, and found myself a bit confused
on the distinction between kbd vs code, and what you mean by
"commands the user will type, not just command names".  Here I'm
referring to how the commands with those names behave, not explicitly
discussing typing.  E.g., the commands behave the same way
when found in a script too.  Doesn't that suggest @code instead?
If not, what would be an example of "just command names", ones that
the user will NOT type?  I.e., when should we use @code instead?

From here: 
 https://www.gnu.org/software/texinfo/manual/texinfo/html_node/_0040kbd.html
I got the impression that a use of @kbd would be when describing
the completion machinery.  Or then the text clearly says something around
"type @kbd{....}".  What specifically confused me was this example
in that page that uses @code for a command name not unlike one of GDB's:

~~~
To give the @code{logout} command,
type the characters @kbd{l o g o u t @key{RET}}.
~~~

We seemingly use @code throughout in GDB's manual to refer to
command names, which adds to my confusion.  They may all be
incorrect, and I'm not trying to justify adding more wrong usages
at all.  I'd just like to understand better the distinction you have
in mind, so I can follow it consistently too.

Thanks,
Pedro Alves

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-02 13:33     ` Pedro Alves
@ 2017-06-21 15:50       ` Pedro Alves
  2017-06-21 19:14         ` Pedro Alves
  2017-06-22 19:42         ` Eli Zaretskii
  0 siblings, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-21 15:50 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On 06/02/2017 02:33 PM, Pedro Alves wrote:
> On 06/02/2017 02:01 PM, Eli Zaretskii wrote:
>>> From: Pedro Alves <palves@redhat.com>
>>> Date: Fri,  2 Jun 2017 13:22:38 +0100
>>>
>>> +For example, assuming a program with symbols named @code{A::B::func}
>>> +and @code{B::func}, both commands @code{break -function func} and
>>> +@code{break -function B::func} set a breakpoint on both symbols.
>>
>> The 2 commands are long and include whitespace, so I'd suggest
>> enclosing each one in @w{..}, to avoid a line break in the middle of a
>> command.
>>
>> Also, I think @kbd is more appropriate here than @code, since you mean
>> commands the user will type, not just command names.
>>
>> I'm surprised you didn't change anything where the manual discusses
>> quoting of names and symbols.  For example, the node "Completion"
>> explicitly describes a use case with overloaded functions in C++; the
>> node "Symbols" describes a case with "::" that requires quoting.
>> There's another example in "Machine Code", and also in "Ambiguous
>> Expression", and in "Variables".  Maybe you already reviewed all of
>> those and concluded no changes were necessary, but I just thought I'd
>> mention them.
> 
> Thanks, I just didn't think of looking for quoting bits in the
> manual, for some reason.  I'll take a closer look.

I've now reviewed these, and it looks to be like only the "Completion"
node needs adjustment.  I replaced the example there with another
example that came to mind when I tried to think of when I use quoting.
I'm not sure it's "the most frequent" case, so I softened the "the"
to "a".  I also found there an ancient paragraph saying that GDB
inserts quoting automatically, which is plain false (and I don't
recall ever running a version of GDB that did that).  I also
added a cross reference to elsewhere in the manual where we discuss
the need for quoting of symbol names.

> 
>>
>>> +  ** GDB now has a much improved linespec and explicit locations TAB
>>> +     completion support, that better understands what you're
>>> +     completing and offers better suggestions.
>>
>> Is this a general improvement, or is it limited to C++ symbols?  If
>> the latter, suggest to mention that.
> 
> You're right.  It's more general than C++.

I've reordered the sections and split appart the C++-related
subsections to their own sections, to hopefully make things
a bit clearer.  I've also adjusted the wording in a couple
cases, and extended some cases with examples.

> 
>>
>>> +  ** GDB can now complete function parameters in linespecs and
>>> +     explicit locations, even without quoting.  When setting
>>> +     breakpoints, quoting around functions names to help with
>>> +     TAB-completion is generally no longer necessary.
>>
>> Likewise.
>>
>>> +  ** GDB can now set breakpoints functions marked with [abi:cxx11]
>>> +     tags.
>>
>> "on functions", I think.

Fixed.

>>
>> And btw, what are those tags?  
> 
> This is the best document that I know describing them:
> 
>  https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/
> 
> Functions that ended up requiring an abi tag are demangled like this:
> 
>   string_printf[abi:cxx11](char const*, ...)
>                ^^^^^^^^^^^
> 
> That's an actual function in GDB.  It ended requiring an ABI tag
> because it returns std::string.  See intro of patch 39 for more.
> 
>> I don't think I see them documented in the manual; did I miss something?
> 
> Hmm, I guess we could add something, indeed.

I've added something now.  The "Debugging C Plus Plus" node has a 
section talking about setting breakpoints on overloaded functions
which looked like the right place for this.

The only thing I didn't address was the @code vs @kbd point.
See the other email.

Otherwise, how does this version look?

From 746f2009e4d27da6d43844c36cc4b04ce71fe1d5 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 21 Jun 2017 16:04:40 +0100
Subject: [PATCH] Document breakpoints / linespec & co improvements (manual +
 NEWS)

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* NEWS: Mention breakpoints, linespecs and explicit locations, and
	completion improvements.

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Completion): Update need-quoting examples.  Remove
	false claim that GDB inserts quoting automatically.
	(Linespec Locations): Document how "function" is
	interpreted in C++.
	(Explicit Locations): Document how "-function" is interpreted in
	C++.  Document -qualified.
	(Debugging C Plus Plus): Document setting breakpoints in functions
	with ABI tags.
---
 gdb/doc/gdb.texinfo | 131 +++++++++++++++++++++++++++++++++++++++++++---------
 gdb/NEWS            |  71 ++++++++++++++++++++++++++++
 2 files changed, 180 insertions(+), 22 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9fb70f6..eff1f55 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1638,39 +1638,56 @@ its notion of a word.  To permit word completion to work in this
 situation, you may enclose words in @code{'} (single quote marks) in
 @value{GDBN} commands.
 
-The most likely situation where you might need this is in typing the
-name of a C@t{++} function.  This is because C@t{++} allows function
-overloading (multiple definitions of the same function, distinguished
-by argument type).  For example, when you want to set a breakpoint you
-may need to distinguish whether you mean the version of @code{name}
-that takes an @code{int} parameter, @code{name(int)}, or the version
-that takes a @code{float} parameter, @code{name(float)}.  To use the
-word-completion facilities in this situation, type a single quote
+A likely situation where you might need this is in typing an
+expression that involves a C@t{++} symbol name with template
+parameters.  This is because when completing expressions, GDB treats
+the @samp{<} character as word delimiter, assuming that it's the
+less-than comparison operator (@pxref{C Operators, , C and C@t{++}
+Operators}).
+
+For example, when you want to call a C@t{++} template function
+interactively using the @code{print} or @code{call} commands, you may
+need to distinguish whether you mean the version of @code{name} that
+was specialized for @code{int}, @code{name<int>()}, or the version
+that was specialized for @code{float}, @code{name<float>()}.  To use
+the word-completion facilities in this situation, type a single quote
 @code{'} at the beginning of the function name.  This alerts
 @value{GDBN} that it may need to consider more information than usual
 when you press @key{TAB} or @kbd{M-?} to request word completion:
 
 @smallexample
-(@value{GDBP}) b 'bubble( @kbd{M-?}
-bubble(double,double)    bubble(int,int)
-(@value{GDBP}) b 'bubble(
+(@value{GDBP}) p 'func< @kbd{M-?}
+func<int>()    func<float>()
+(@value{GDBP}) p 'func<
 @end smallexample
 
-In some cases, @value{GDBN} can tell that completing a name requires using
-quotes.  When this happens, @value{GDBN} inserts the quote for you (while
-completing as much as it can) if you do not type the quote in the first
-place:
+When setting breakpoints however (@pxref{Specify Location}), you don't
+usually need to type a quote before the function name, because
+@value{GDBN} understands that you want to set a breakpoint on a
+function:
 
 @smallexample
-(@value{GDBP}) b bub @key{TAB}
-@exdent @value{GDBN} alters your input line to the following, and rings a bell:
-(@value{GDBP}) b 'bubble(
+(@value{GDBP}) b func< @kbd{M-?}
+func<int>()    func<float>()
+(@value{GDBP}) b func<
 @end smallexample
 
-@noindent
-In general, @value{GDBN} can tell that a quote is needed (and inserts it) if
-you have not yet started typing the argument list when you ask for
-completion on an overloaded symbol.
+This is true even in the case of typing the name of C@t{++} overloaded
+functions (multiple definitions of the same function, distinguished by
+argument type).  For example, when you want to set a breakpoint you
+don't need to distinguish whether you mean the version of @code{name}
+that takes an @code{int} parameter, @code{name(int)}, or the version
+that takes a @code{float} parameter, @code{name(float)}.
+
+@smallexample
+(@value{GDBP}) b bubble( @kbd{M-?}
+bubble(int)    bubble(double)
+(@value{GDBP}) b bubble(dou @kbd{M-?}
+bubble(double)
+@end smallexample
+
+See @ref{quoting names} for a description of other scenarios that
+require quoting.
 
 For more information about overloaded functions, see @ref{C Plus Plus
 Expressions, ,C@t{++} Expressions}.  You can use the command @code{set
@@ -7845,6 +7862,16 @@ name of @file{/build/trunk/gcc/expr.c}, but not
 Specifies the line that begins the body of the function @var{function}.
 For example, in C, this is the line with the open brace.
 
+For C@t{++}, @var{function} is interpreted as specifying all functions
+named @var{function} ignoring missing leading specifiers (namespaces
+and classes).
+
+For example, assuming a program with symbols named @code{A::B::func}
+and @code{B::func}, both commands @w{@code{break func}} and
+@w{@code{break B::func}} set a breakpoint on both symbols.  To
+override this, you can use the explicit location option
+@code{-qualified}.  @xref{Explicit Locations}.
+
 @item @var{function}:@var{label}
 Specifies the line where @var{label} appears in @var{function}.
 
@@ -7909,6 +7936,22 @@ on function locations unmodified by other options (such as @code{-label}
 or @code{-line}) refer to the line that begins the body of the function.
 In C, for example, this is the line with the open brace.
 
+For C@t{++}, @var{function} is interpreted as specifying all functions
+named @var{function} ignoring missing leading specifiers (namespaces
+and classes).
+
+For example, assuming a program with symbols named @code{A::B::func}
+and @code{B::func}, both commands @code{break -function func} and
+@code{break -function B::func} set a breakpoint on both symbols.
+
+@item -qualified @var{function}
+Like @code{-function}, but the value specifies a fully qualified name
+of a function.
+
+For example, assuming a C@t{++} program with symbols named
+@code{A::B::func} and @code{B::func}, the @code{break -qualified
+B::func} command sets a breakpoint on @code{B::func}, only.
+
 @item -label @var{label}
 The value specifies the name of a label.  When the function
 name is not specified, the label is searched in the function of the currently
@@ -14992,6 +15035,49 @@ the same notation that is used to declare such symbols in C@t{++}: type
 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 functions with ABI tags}
+
+The GNU C@t{++} compiler introduced the notion of ABI ``tags'', which
+correspond to changes in the ABI of a type, function, or variable that
+would not otherwise be reflected in a mangled name.  See
+@url{https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/}
+for more detail.
+
+The ABI tags are visible in C@t{++} demangled names.  For example, a
+function that returns a std::string:
+
+@smallexample
+std::string function(int);
+@end smallexample
+
+@noindent
+when compiled for the C++11 ABI is marked with the @code{cxx11} ABI
+tag, and @value{GDBN} displays the symbol like this:
+
+@smallexample
+function[abi:cxx11](int)
+@end smallexample
+
+You can set a breakpoint on such functions simply as if they had no
+tag.  For example:
+
+@smallexample
+(gdb) b function(int)
+Breakpoint 2 at 0x40060d: file main.cc, line 10.
+(gdb) info breakpoints
+Num     Type           Disp Enb Address    What
+1       breakpoint     keep y   0x0040060d in function[abi:cxx11](int)
+                                           at main.cc:10
+@end smallexample
+
+On the rare occasion you need to disambiguate between different ABI
+tags, you can do so by simply including the ABI tag in the function
+name, like:
+
+@smallexample
+(@value{GDBP}) b ambiguous[abi:other_tag](int)
+@end smallexample
 @end table
 
 @node Decimal Floating Point
@@ -16818,6 +16904,7 @@ file-management commands (@pxref{Files, ,Commands to Specify Files}).
 @cindex symbol names
 @cindex names of symbols
 @cindex quoting names
+@anchor{quoting names}
 Occasionally, you may need to refer to symbols that contain unusual
 characters, which @value{GDBN} ordinarily treats as word delimiters.  The
 most frequent case is in referring to static variables in other
diff --git a/gdb/NEWS b/gdb/NEWS
index 112aa2f..eebe89a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,77 @@
 
 *** Changes since GDB 8.0
 
+* Completion improvements
+
+  ** GDB can now complete function parameters in linespecs and
+     explicit locations without quoting.  When setting breakpoints,
+     quoting around functions names to help with TAB-completion is
+     generally no longer necessary.  For example, this now complete
+     correctly:
+
+      (gdb) b function(in[TAB]
+      (gdb) b function(int)
+
+     Related, GDB is no longer confused with completing functions in
+     C++ anonymous namespaces:
+
+      (gdb) b (anon[TAB]
+      (gdb) b (anonymous namespace)::[TAB][TAB]
+      (anonymous namespace)::a_function()
+      (anonymous namespace)::b_function()
+
+  ** GDB now has much improved linespec and explicit locations TAB
+     completion support, that better understands what you're
+     completing and offers better suggestions.  For example, GDB no
+     longer offers data symbols as possible completions when you're
+     setting a breakpoint.
+
+  ** GDB now TAB-completes label symbol names.
+
+  ** The "complete" command now mimics TAB completion accurately.
+
+* Breakpoints on C++ functions now ignore leading namespaces/classes
+
+  By default, breakpoints on functions/methods are now interpreted as
+  specifying all functions with the given name ignoring missing
+  leading specifiers (namespaces and classes).
+
+  For example, assuming a C++ program with symbols named:
+
+    A::B::func
+    B::func
+
+  both commands "break func" and "break B::func" set a breakpoint on
+  both symbols.  The explicit location option "-function" was changed
+  accordingly.
+
+  To override this, you can specify an explicitly fully qualified
+  name, usign the new explicit location option "-qualified".  For
+  example, using the same C++ program, the "break -qualified B::func"
+  command sets a breakpoint on "B::func", only.
+
+* Breakpoints on functions marked with C++ ABI tags
+
+  GDB can now set breakpoints on functions marked with C++ ABI tags
+  (e.g., [abi:cxx11]).  See here for a description of ABI tags:
+  https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/
+
+  Functions with a C++11 abi tag are demangled/displayed like this:
+
+    function[abi:cxx11](int)
+            ^^^^^^^^^^^
+
+  You can now set a breakpoint on such functions simply as if they had
+  no tag, like:
+
+    (gdb) b function(int)
+
+  Or if you need to disambiguate between tags, like:
+
+    (gdb) b function[abi:other_tag](int)
+
+  Tab completion was adjusted accordingly as well.
+
 *** Changes in GDB 8.0
 
 * GDB now supports access to the PKU register on GNU/Linux. The register is
-- 
2.5.5


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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-21 13:32     ` Pedro Alves
@ 2017-06-21 18:26       ` Eli Zaretskii
  2017-06-21 19:01         ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Eli Zaretskii @ 2017-06-21 18:26 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> Cc: gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 21 Jun 2017 14:32:54 +0100
> 
> > Also, I think @kbd is more appropriate here than @code, since you mean
> > commands the user will type, not just command names.
> 
> I was addressing this comment, and found myself a bit confused
> on the distinction between kbd vs code, and what you mean by
> "commands the user will type, not just command names".  Here I'm
> referring to how the commands with those names behave, not explicitly
> discussing typing.  E.g., the commands behave the same way
> when found in a script too.  Doesn't that suggest @code instead?

Not IMO.

We use @code for names of individual commands, like @code{help} or
@code{run}.  When we need to show a full command line, it is better to
use @kbd because it is the markup suitable for commands that users
type.  I don't think it matters much whether they type it at GDB's
prompt or into a script.

In a nutshell, when I see a string with embedded whitespace marked up
in @code, I raise a brow ;-)

(And yes, I know that we use @code{set FOO} a lot.  That is IMO an
exception, since the "set" command alone makes very little sense in
GDB.)

> >From here: 
>  https://www.gnu.org/software/texinfo/manual/texinfo/html_node/_0040kbd.html
> I got the impression that a use of @kbd would be when describing
> the completion machinery.  Or then the text clearly says something around
> "type @kbd{....}".

My rule is "when talking about something that users might/will type,
explicitly or implicitly".  It's admittedly a fine line sometimes,
which is why I use the "whitespace" heuristics (which is also
imperfect).

> What specifically confused me was this example
> in that page that uses @code for a command name not unlike one of GDB's:
> 
> ~~~
> To give the @code{logout} command,
> type the characters @kbd{l o g o u t @key{RET}}.
> ~~~

Why is that confusing?  This text specifically makes a point of
differentiating between a command's name and the way it is typed at
the keyboard.  It's a rare case, but it does happen.

> We seemingly use @code throughout in GDB's manual to refer to
> command names, which adds to my confusion.  They may all be
> incorrect, and I'm not trying to justify adding more wrong usages
> at all.  I'd just like to understand better the distinction you have
> in mind, so I can follow it consistently too.

I hope I explained at least some of that.

Thanks.

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-21 18:26       ` Eli Zaretskii
@ 2017-06-21 19:01         ` Pedro Alves
  2017-06-22 19:43           ` Eli Zaretskii
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-21 19:01 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches


On 06/21/2017 07:26 PM, Eli Zaretskii wrote:
>> Cc: gdb-patches@sourceware.org
>> From: Pedro Alves <palves@redhat.com>
>> Date: Wed, 21 Jun 2017 14:32:54 +0100
>>
>>> Also, I think @kbd is more appropriate here than @code, since you mean
>>> commands the user will type, not just command names.
>>
>> I was addressing this comment, and found myself a bit confused
>> on the distinction between kbd vs code, and what you mean by
>> "commands the user will type, not just command names".  Here I'm
>> referring to how the commands with those names behave, not explicitly
>> discussing typing.  E.g., the commands behave the same way
>> when found in a script too.  Doesn't that suggest @code instead?
> 
> Not IMO.
> 
> We use @code for names of individual commands, like @code{help} or
> @code{run}.  When we need to show a full command line, it is better to
> use @kbd because it is the markup suitable for commands that users
> type.  I don't think it matters much whether they type it at GDB's
> prompt or into a script.
> 
> In a nutshell, when I see a string with embedded whitespace marked up
> in @code, I raise a brow ;-)
> 
> (And yes, I know that we use @code{set FOO} a lot.  That is IMO an
> exception, since the "set" command alone makes very little sense in
> GDB.)

OK, I think I get it.  In this case, I'm saying
"break B::func", which is an example of an invocation of the
command (I'm passing in a specific function name).  Maybe
that's another way to put it -- when talking about invocations
of commands, use @kbd.  A bit like "type" vs "value".

>> What specifically confused me was this example
>> in that page that uses @code for a command name not unlike one of GDB's:
>>
>> ~~~
>> To give the @code{logout} command,
>> type the characters @kbd{l o g o u t @key{RET}}.
>> ~~~
> 
> Why is that confusing?  This text specifically makes a point of
> differentiating between a command's name and the way it is typed at
> the keyboard.  It's a rare case, but it does happen.

The confusion was that I was seeing the cases in my patch
like the @code{logout} case here.  I looked for @kbd instances
in gdb.texinfo, and saw many preceded by type/typing, etc.,
that's why I thought that we'd only use kbd in those cases.
I see now that there are more cases that don't
say "type" explicitly.

>> We seemingly use @code throughout in GDB's manual to refer to
>> command names, which adds to my confusion.  They may all be
>> incorrect, and I'm not trying to justify adding more wrong usages
>> at all.  I'd just like to understand better the distinction you have
>> in mind, so I can follow it consistently too.
> 
> I hope I explained at least some of that.

Thanks, that was sufficient, I think.

I'm merging this on top of the patch that I sent.  The last
hunk fixes a pre-existing case around the text that I'm fixing.

From ff6dfe14de6f207dfe6d227749b7145ad7e95d6f Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 21 Jun 2017 19:41:43 +0100
Subject: [PATCH] kbd

---
 gdb/doc/gdb.texinfo | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index eff1f55..1db8690 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7867,8 +7867,8 @@ named @var{function} ignoring missing leading specifiers (namespaces
 and classes).
 
 For example, assuming a program with symbols named @code{A::B::func}
-and @code{B::func}, both commands @w{@code{break func}} and
-@w{@code{break B::func}} set a breakpoint on both symbols.  To
+and @code{B::func}, both commands @w{@kbd{break func}} and
+@w{@kbd{break B::func}} set a breakpoint on both symbols.  To
 override this, you can use the explicit location option
 @code{-qualified}.  @xref{Explicit Locations}.
 
@@ -7941,16 +7941,16 @@ named @var{function} ignoring missing leading specifiers (namespaces
 and classes).
 
 For example, assuming a program with symbols named @code{A::B::func}
-and @code{B::func}, both commands @code{break -function func} and
-@code{break -function B::func} set a breakpoint on both symbols.
+and @code{B::func}, both commands @w{@kbd{break -function func}} and
+@w{@kdb{break -function B::func}} set a breakpoint on both symbols.
 
 @item -qualified @var{function}
 Like @code{-function}, but the value specifies a fully qualified name
 of a function.
 
 For example, assuming a C@t{++} program with symbols named
-@code{A::B::func} and @code{B::func}, the @code{break -qualified
-B::func} command sets a breakpoint on @code{B::func}, only.
+@code{A::B::func} and @code{B::func}, the @w{@kbd{break -qualified
+B::func}} command sets a breakpoint on @code{B::func}, only.
 
 @item -label @var{label}
 The value specifies the name of a label.  When the function
@@ -7965,7 +7965,7 @@ relative to the current line.
 @end table
 
 Explicit location options may be abbreviated by omitting any non-unique
-trailing characters from the option name, e.g., @code{break -s main.c -li 3}.
+trailing characters from the option name, e.g., @w{@kdb{break -s main.c -li 3}}.
 
 @node Address Locations
 @subsection Address Locations
-- 
2.5.5

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-21 15:50       ` Pedro Alves
@ 2017-06-21 19:14         ` Pedro Alves
  2017-06-22 19:45           ` Eli Zaretskii
  2017-06-22 19:42         ` Eli Zaretskii
  1 sibling, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-21 19:14 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On 06/21/2017 04:50 PM, Pedro Alves wrote:
> The only thing I didn't address was the @code vs @kbd point.
> See the other email.
> 
> Otherwise, how does this version look?

And here's the version addresses that too.

From 35a8155edc16951cbfe173d0c6dbd34c39f85be4 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 21 Jun 2017 20:12:41 +0100
Subject: [PATCH] Document breakpoints / linespec & co improvements (manual +
 NEWS)

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* NEWS: Mention breakpoints, linespecs and explicit locations, and
	completion improvements.

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Completion): Update need-quoting examples.  Remove
	false claim that GDB inserts quoting automatically.
	(Linespec Locations): Document how "function" is
	interpreted in C++.
	(Explicit Locations): Document how "-function" is interpreted in
	C++.  Document -qualified.
	(Debugging C Plus Plus): Document setting breakpoints in functions
	with ABI tags.
---
 gdb/doc/gdb.texinfo | 133 +++++++++++++++++++++++++++++++++++++++++++---------
 gdb/NEWS            |  71 ++++++++++++++++++++++++++++
 2 files changed, 181 insertions(+), 23 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9fb70f6..1db8690 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1638,39 +1638,56 @@ its notion of a word.  To permit word completion to work in this
 situation, you may enclose words in @code{'} (single quote marks) in
 @value{GDBN} commands.
 
-The most likely situation where you might need this is in typing the
-name of a C@t{++} function.  This is because C@t{++} allows function
-overloading (multiple definitions of the same function, distinguished
-by argument type).  For example, when you want to set a breakpoint you
-may need to distinguish whether you mean the version of @code{name}
-that takes an @code{int} parameter, @code{name(int)}, or the version
-that takes a @code{float} parameter, @code{name(float)}.  To use the
-word-completion facilities in this situation, type a single quote
+A likely situation where you might need this is in typing an
+expression that involves a C@t{++} symbol name with template
+parameters.  This is because when completing expressions, GDB treats
+the @samp{<} character as word delimiter, assuming that it's the
+less-than comparison operator (@pxref{C Operators, , C and C@t{++}
+Operators}).
+
+For example, when you want to call a C@t{++} template function
+interactively using the @code{print} or @code{call} commands, you may
+need to distinguish whether you mean the version of @code{name} that
+was specialized for @code{int}, @code{name<int>()}, or the version
+that was specialized for @code{float}, @code{name<float>()}.  To use
+the word-completion facilities in this situation, type a single quote
 @code{'} at the beginning of the function name.  This alerts
 @value{GDBN} that it may need to consider more information than usual
 when you press @key{TAB} or @kbd{M-?} to request word completion:
 
 @smallexample
-(@value{GDBP}) b 'bubble( @kbd{M-?}
-bubble(double,double)    bubble(int,int)
-(@value{GDBP}) b 'bubble(
+(@value{GDBP}) p 'func< @kbd{M-?}
+func<int>()    func<float>()
+(@value{GDBP}) p 'func<
 @end smallexample
 
-In some cases, @value{GDBN} can tell that completing a name requires using
-quotes.  When this happens, @value{GDBN} inserts the quote for you (while
-completing as much as it can) if you do not type the quote in the first
-place:
+When setting breakpoints however (@pxref{Specify Location}), you don't
+usually need to type a quote before the function name, because
+@value{GDBN} understands that you want to set a breakpoint on a
+function:
 
 @smallexample
-(@value{GDBP}) b bub @key{TAB}
-@exdent @value{GDBN} alters your input line to the following, and rings a bell:
-(@value{GDBP}) b 'bubble(
+(@value{GDBP}) b func< @kbd{M-?}
+func<int>()    func<float>()
+(@value{GDBP}) b func<
 @end smallexample
 
-@noindent
-In general, @value{GDBN} can tell that a quote is needed (and inserts it) if
-you have not yet started typing the argument list when you ask for
-completion on an overloaded symbol.
+This is true even in the case of typing the name of C@t{++} overloaded
+functions (multiple definitions of the same function, distinguished by
+argument type).  For example, when you want to set a breakpoint you
+don't need to distinguish whether you mean the version of @code{name}
+that takes an @code{int} parameter, @code{name(int)}, or the version
+that takes a @code{float} parameter, @code{name(float)}.
+
+@smallexample
+(@value{GDBP}) b bubble( @kbd{M-?}
+bubble(int)    bubble(double)
+(@value{GDBP}) b bubble(dou @kbd{M-?}
+bubble(double)
+@end smallexample
+
+See @ref{quoting names} for a description of other scenarios that
+require quoting.
 
 For more information about overloaded functions, see @ref{C Plus Plus
 Expressions, ,C@t{++} Expressions}.  You can use the command @code{set
@@ -7845,6 +7862,16 @@ name of @file{/build/trunk/gcc/expr.c}, but not
 Specifies the line that begins the body of the function @var{function}.
 For example, in C, this is the line with the open brace.
 
+For C@t{++}, @var{function} is interpreted as specifying all functions
+named @var{function} ignoring missing leading specifiers (namespaces
+and classes).
+
+For example, assuming a program with symbols named @code{A::B::func}
+and @code{B::func}, both commands @w{@kbd{break func}} and
+@w{@kbd{break B::func}} set a breakpoint on both symbols.  To
+override this, you can use the explicit location option
+@code{-qualified}.  @xref{Explicit Locations}.
+
 @item @var{function}:@var{label}
 Specifies the line where @var{label} appears in @var{function}.
 
@@ -7909,6 +7936,22 @@ on function locations unmodified by other options (such as @code{-label}
 or @code{-line}) refer to the line that begins the body of the function.
 In C, for example, this is the line with the open brace.
 
+For C@t{++}, @var{function} is interpreted as specifying all functions
+named @var{function} ignoring missing leading specifiers (namespaces
+and classes).
+
+For example, assuming a program with symbols named @code{A::B::func}
+and @code{B::func}, both commands @w{@kbd{break -function func}} and
+@w{@kdb{break -function B::func}} set a breakpoint on both symbols.
+
+@item -qualified @var{function}
+Like @code{-function}, but the value specifies a fully qualified name
+of a function.
+
+For example, assuming a C@t{++} program with symbols named
+@code{A::B::func} and @code{B::func}, the @w{@kbd{break -qualified
+B::func}} command sets a breakpoint on @code{B::func}, only.
+
 @item -label @var{label}
 The value specifies the name of a label.  When the function
 name is not specified, the label is searched in the function of the currently
@@ -7922,7 +7965,7 @@ relative to the current line.
 @end table
 
 Explicit location options may be abbreviated by omitting any non-unique
-trailing characters from the option name, e.g., @code{break -s main.c -li 3}.
+trailing characters from the option name, e.g., @w{@kdb{break -s main.c -li 3}}.
 
 @node Address Locations
 @subsection Address Locations
@@ -14992,6 +15035,49 @@ the same notation that is used to declare such symbols in C@t{++}: type
 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 functions with ABI tags}
+
+The GNU C@t{++} compiler introduced the notion of ABI ``tags'', which
+correspond to changes in the ABI of a type, function, or variable that
+would not otherwise be reflected in a mangled name.  See
+@url{https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/}
+for more detail.
+
+The ABI tags are visible in C@t{++} demangled names.  For example, a
+function that returns a std::string:
+
+@smallexample
+std::string function(int);
+@end smallexample
+
+@noindent
+when compiled for the C++11 ABI is marked with the @code{cxx11} ABI
+tag, and @value{GDBN} displays the symbol like this:
+
+@smallexample
+function[abi:cxx11](int)
+@end smallexample
+
+You can set a breakpoint on such functions simply as if they had no
+tag.  For example:
+
+@smallexample
+(gdb) b function(int)
+Breakpoint 2 at 0x40060d: file main.cc, line 10.
+(gdb) info breakpoints
+Num     Type           Disp Enb Address    What
+1       breakpoint     keep y   0x0040060d in function[abi:cxx11](int)
+                                           at main.cc:10
+@end smallexample
+
+On the rare occasion you need to disambiguate between different ABI
+tags, you can do so by simply including the ABI tag in the function
+name, like:
+
+@smallexample
+(@value{GDBP}) b ambiguous[abi:other_tag](int)
+@end smallexample
 @end table
 
 @node Decimal Floating Point
@@ -16818,6 +16904,7 @@ file-management commands (@pxref{Files, ,Commands to Specify Files}).
 @cindex symbol names
 @cindex names of symbols
 @cindex quoting names
+@anchor{quoting names}
 Occasionally, you may need to refer to symbols that contain unusual
 characters, which @value{GDBN} ordinarily treats as word delimiters.  The
 most frequent case is in referring to static variables in other
diff --git a/gdb/NEWS b/gdb/NEWS
index 112aa2f..eebe89a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,77 @@
 
 *** Changes since GDB 8.0
 
+* Completion improvements
+
+  ** GDB can now complete function parameters in linespecs and
+     explicit locations without quoting.  When setting breakpoints,
+     quoting around functions names to help with TAB-completion is
+     generally no longer necessary.  For example, this now complete
+     correctly:
+
+      (gdb) b function(in[TAB]
+      (gdb) b function(int)
+
+     Related, GDB is no longer confused with completing functions in
+     C++ anonymous namespaces:
+
+      (gdb) b (anon[TAB]
+      (gdb) b (anonymous namespace)::[TAB][TAB]
+      (anonymous namespace)::a_function()
+      (anonymous namespace)::b_function()
+
+  ** GDB now has much improved linespec and explicit locations TAB
+     completion support, that better understands what you're
+     completing and offers better suggestions.  For example, GDB no
+     longer offers data symbols as possible completions when you're
+     setting a breakpoint.
+
+  ** GDB now TAB-completes label symbol names.
+
+  ** The "complete" command now mimics TAB completion accurately.
+
+* Breakpoints on C++ functions now ignore leading namespaces/classes
+
+  By default, breakpoints on functions/methods are now interpreted as
+  specifying all functions with the given name ignoring missing
+  leading specifiers (namespaces and classes).
+
+  For example, assuming a C++ program with symbols named:
+
+    A::B::func
+    B::func
+
+  both commands "break func" and "break B::func" set a breakpoint on
+  both symbols.  The explicit location option "-function" was changed
+  accordingly.
+
+  To override this, you can specify an explicitly fully qualified
+  name, usign the new explicit location option "-qualified".  For
+  example, using the same C++ program, the "break -qualified B::func"
+  command sets a breakpoint on "B::func", only.
+
+* Breakpoints on functions marked with C++ ABI tags
+
+  GDB can now set breakpoints on functions marked with C++ ABI tags
+  (e.g., [abi:cxx11]).  See here for a description of ABI tags:
+  https://developers.redhat.com/blog/2015/02/05/gcc5-and-the-c11-abi/
+
+  Functions with a C++11 abi tag are demangled/displayed like this:
+
+    function[abi:cxx11](int)
+            ^^^^^^^^^^^
+
+  You can now set a breakpoint on such functions simply as if they had
+  no tag, like:
+
+    (gdb) b function(int)
+
+  Or if you need to disambiguate between tags, like:
+
+    (gdb) b function[abi:other_tag](int)
+
+  Tab completion was adjusted accordingly as well.
+
 *** Changes in GDB 8.0
 
 * GDB now supports access to the PKU register on GNU/Linux. The register is
-- 
2.5.5


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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-21 15:50       ` Pedro Alves
  2017-06-21 19:14         ` Pedro Alves
@ 2017-06-22 19:42         ` Eli Zaretskii
  1 sibling, 0 replies; 183+ messages in thread
From: Eli Zaretskii @ 2017-06-22 19:42 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> Cc: gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 21 Jun 2017 16:50:24 +0100
> 
> +A likely situation where you might need this is in typing an
> +expression that involves a C@t{++} symbol name with template
> +parameters.  This is because when completing expressions, GDB treats
                                                             ^^^
@value{GDBN}

> +See @ref{quoting names} for a description of other scenarios that
   ^^^^^^^^
This is OK, but @xref does the same job.  In any case, it is good
practice to put a comma after the closing brace, or else some versions
of makeinfo will bitch at you.

> +For example, assuming a program with symbols named @code{A::B::func}
> +and @code{B::func}, both commands @code{break -function func} and
> +@code{break -function B::func} set a breakpoint on both symbols.
> +
> +@item -qualified @var{function}
> +Like @code{-function}, but the value specifies a fully qualified name
> +of a function.
> +
> +For example, assuming a C@t{++} program with symbols named

Two "For example" too close to one another.  Suggest to replace the
2nd one with "As another example".

> +@code{A::B::func} and @code{B::func}, the @code{break -qualified
> +B::func} command                          ^^^^^^^^^^^^^^^^^^^^^^
   ^^^^^^^^
This is better put in @w{..}.

> +
>  @item -label @var{label}
>  The value specifies the name of a label.  When the function
>  name is not specified, the label is searched in the function of the currently
> @@ -14992,6 +15035,49 @@ the same notation that is used to declare such symbols in C@t{++}: type
>  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 functions with ABI tags}
> +
> +The GNU C@t{++} compiler introduced the notion of ABI ``tags'', which

"tags" should be in @dfn (which will produce the quotes, so you can
lose them).  Also, I'd add a @cindex entry here about "ABI tags".

> +when compiled for the C++11 ABI is marked with the @code{cxx11} ABI
                         ^^^^^
C@t{++}11, I think.

> +tag, and @value{GDBN} displays the symbol like this:
> +
> +@smallexample
> +function[abi:cxx11](int)
> +@end smallexample

Where does GDB use this format?  Only in "info break", as shown below,
or also in other contexts?  If the latter, I suggest that the above
example show some full output with context.

The patch is OK with those fixed.

Thanks.

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-21 19:01         ` Pedro Alves
@ 2017-06-22 19:43           ` Eli Zaretskii
  0 siblings, 0 replies; 183+ messages in thread
From: Eli Zaretskii @ 2017-06-22 19:43 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> Cc: gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 21 Jun 2017 20:01:34 +0100
> 
> OK, I think I get it.  In this case, I'm saying
> "break B::func", which is an example of an invocation of the
> command (I'm passing in a specific function name).  Maybe
> that's another way to put it -- when talking about invocations
> of commands, use @kbd.  A bit like "type" vs "value".

Right.

> >From ff6dfe14de6f207dfe6d227749b7145ad7e95d6f Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 21 Jun 2017 19:41:43 +0100
> Subject: [PATCH] kbd

This is OK, thanks.

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

* Re: [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS)
  2017-06-21 19:14         ` Pedro Alves
@ 2017-06-22 19:45           ` Eli Zaretskii
  0 siblings, 0 replies; 183+ messages in thread
From: Eli Zaretskii @ 2017-06-22 19:45 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> Cc: gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 21 Jun 2017 20:14:04 +0100
> 
> On 06/21/2017 04:50 PM, Pedro Alves wrote:
> > The only thing I didn't address was the @code vs @kbd point.
> > See the other email.
> > 
> > Otherwise, how does this version look?
> 
> And here's the version addresses that too.

It's fine, with the previously-mentioned gotchas fixed.

Thanks.

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

* Re: [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack
  2017-06-02 12:22 ` [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack Pedro Alves
@ 2017-06-26 13:47   ` Yao Qi
  2017-06-27 10:25     ` Pedro Alves
  2017-06-28 10:36   ` Yao Qi
  1 sibling, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-26 13:47 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

[I am not sure how many patches in the series I can review, so I may
stop at any point.]

> These changes in the parsers may not be obvious:
>
>  -  obstack_init (&name_obstack);
>  -  make_cleanup_obstack_free (&name_obstack);
>  +  name_obstack.clear ();
>
> Here, the 'name_obstack' variable is a global.  The change means that
> the obstack's contents from a previous parse will stay around until
> the next parsing starts.  I.e., memory won't be reclaimed until them.
> I don't think that's a problem, these objects don't really grow much
> at all.

I don't have a better solution to this, so I have to say that your patch
is good enough.  Patch is good to me.

-- 
Yao (齐尧)

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

* Re: [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack
  2017-06-26 13:47   ` Yao Qi
@ 2017-06-27 10:25     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-27 10:25 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/26/2017 02:47 PM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
> [I am not sure how many patches in the series I can review, so I may
> stop at any point.]

Thanks.  I think patches #1 to #10 and #19 to #23 (inclusive), could
go in independently of the main completer and symbol lookup changes.

> 
>> These changes in the parsers may not be obvious:
>>
>>  -  obstack_init (&name_obstack);
>>  -  make_cleanup_obstack_free (&name_obstack);
>>  +  name_obstack.clear ();
>>
>> Here, the 'name_obstack' variable is a global.  The change means that
>> the obstack's contents from a previous parse will stay around until
>> the next parsing starts.  I.e., memory won't be reclaimed until them.
>> I don't think that's a problem, these objects don't really grow much
>> at all.
> 
> I don't have a better solution to this, so I have to say that your patch
> is good enough.  Patch is good to me.

Thanks, I've pushed this one in.

-- 
Pedro Alves

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

* Re: [PATCH 05/40] command.h: Include scoped_restore_command.h
  2017-06-02 12:29 ` [PATCH 05/40] command.h: Include scoped_restore_command.h Pedro Alves
@ 2017-06-27 11:30   ` Yao Qi
  2017-06-27 11:45     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-27 11:30 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> This file depends on scoped_restore:
>
>   extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
>
> But doesn't include the corresponding header.

It is a function declaration, so why does it need scoped_restore?

$ make check-headers CHECK_HEADERS="command.h"

The command above doesn't complain anything.  I tried gcc-5, 6, and 7.

-- 
Yao (齐尧)

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

* Re: [PATCH 05/40] command.h: Include scoped_restore_command.h
  2017-06-27 11:30   ` Yao Qi
@ 2017-06-27 11:45     ` Pedro Alves
  2017-06-27 11:52       ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-27 11:45 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/27/2017 12:30 PM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> This file depends on scoped_restore:
>>
>>   extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
>>
>> But doesn't include the corresponding header.
> 
> It is a function declaration, so why does it need scoped_restore?

I don't understand.  Why wouldn't it?  If the compiler doesn't
know what scoped_restore_tmpl is, how can that compile?

> 
> $ make check-headers CHECK_HEADERS="command.h"
> 
> The command above doesn't complain anything.  I tried gcc-5, 6, and 7.

Odd.  Let me take another look.

I ran into this because at some point I added "#include "command.h"
somewhere, which then failed to compile.

Thanks,
Pedro Alves

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

* Re: [PATCH 05/40] command.h: Include scoped_restore_command.h
  2017-06-27 11:45     ` Pedro Alves
@ 2017-06-27 11:52       ` Pedro Alves
  2017-06-27 12:03         ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-27 11:52 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 06/27/2017 12:45 PM, Pedro Alves wrote:
> 
> On 06/27/2017 12:30 PM, Yao Qi wrote:
>> Pedro Alves <palves@redhat.com> writes:
>>
>>> This file depends on scoped_restore:
>>>
>>>   extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
>>>
>>> But doesn't include the corresponding header.
>>
>> It is a function declaration, so why does it need scoped_restore?
> 
> I don't understand.  Why wouldn't it?  If the compiler doesn't
> know what scoped_restore_tmpl is, how can that compile?
> 
>>
>> $ make check-headers CHECK_HEADERS="command.h"
>>
>> The command above doesn't complain anything.  I tried gcc-5, 6, and 7.
> 
> Odd.  Let me take another look.

OK, so defs.h includes utils.h, which includes scoped_restore.h,
as found via this hack:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 diff --git c/gdb/command.h w/gdb/command.h
 index aa179e9..f587ebd 100644
 --- c/gdb/command.h
 +++ w/gdb/command.h
 @@ -415,6 +415,8 @@ extern void error_no_arg (const char *) ATTRIBUTE_NORETURN;
 
  extern void dont_repeat (void);
 
 +template <typename T> union scoped_restore_tmpl;
 +
  extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
 
  /* Used to mark commands that don't do anything.  If we just leave the

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/gdb/command.h:418:29: error: ‘union’ tag used in naming ‘struct scoped_restore_tmpl<T>’ [-fpermissive]
 template <typename T> union scoped_restore_tmpl;
                             ^
In file included from src/gdb/utils.h:25:0,
                 from src/gdb/defs.h:785,
                 from <command-line>:0:
src/gdb/common/scoped_restore.h:50:7: note: ‘struct scoped_restore_tmpl<T>’ was previously declared here
 class scoped_restore_tmpl : public scoped_restore_base
       ^
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

> I ran into this because at some point I added "#include "command.h"
> somewhere, which then failed to compile.

I don't remember exactly where I added the #include "command.h" that
triggered this.  Must have been somewhere that caused "command.h" to
be included via "defs.h", before the "#include utils.h" line.

Let me try removing it from the series, see if I can still reproduce it.

Thanks,
Pedro Alves

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

* Re: [PATCH 07/40] objfile_per_bfd_storage non-POD
  2017-06-02 12:29 ` [PATCH 07/40] objfile_per_bfd_storage non-POD Pedro Alves
@ 2017-06-27 12:00   ` Yao Qi
  2017-06-27 15:30     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-27 12:00 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> A following patch will want to add a std::vector to
> objfile_per_bfd_storage.  That makes it non-trivially
> constructible/destructible.  Since objfile_per_bfd_storage objects are
> allocated on an obstack, we need to call their ctors/dtors manually.
> This is what this patch does.  And then since we can now rely on
> ctors/dtors being run, make objfile_per_bfd_storage::storage_obstack
> be an auto_obstack.

It is good trick to class-fy some structures which are allocated on
obstack.

>
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>
> 	* objfiles.c (get_objfile_bfd_data): Call bfd_alloc instead of
> 	bfd_zalloc.  Call objfile_per_bfd_storage's ctor.
> 	(free_objfile_per_bfd_storage): Call objfile_per_bfd_storage's
> 	dtor.
> 	* objfiles.h (objfile_per_bfd_storage): Add ctor.  Make
> 	'storage_obstack' field an auto_obstack.  In-class initialize all
> 	non-bitfield fields.  Make minsyms_read bool.
> 	* symfile.c (read_symbols): Adjust.

Patch is good to me.

-- 
Yao (齐尧)

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

* Re: [PATCH 05/40] command.h: Include scoped_restore_command.h
  2017-06-27 11:52       ` Pedro Alves
@ 2017-06-27 12:03         ` Pedro Alves
  2017-06-27 15:46           ` [PATCH 05/40] command.h: Include common/scoped_restore.h Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-27 12:03 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 06/27/2017 12:52 PM, Pedro Alves wrote:

> I don't remember exactly where I added the #include "command.h" that
> triggered this.  Must have been somewhere that caused "command.h" to
> be included via "defs.h", before the "#include utils.h" line.
> 
> Let me try removing it from the series, see if I can still reproduce it.

Found it.  Patch #11 (https://sourceware.org/ml/gdb-patches/2017-06/msg00023.html)
later does:

 diff --git c/gdb/symtab.h w/gdb/symtab.h
 index 5901cf9..fb08479 100644
 --- c/gdb/symtab.h
 +++ w/gdb/symtab.h
 @@ -25,6 +25,7 @@
  #include "gdbtypes.h"
  #include "common/enum-flags.h"
  #include "common/function-view.h"
 +#include "completer.h"

which without this fix would break building all .o files like this:

 In file included from src/gdb/completer.h:21:0,
                  from src/gdb/symtab.h:28,
                  from src/gdb/language.h:26,
                  from src/gdb/frame.h:72,
                  from src/gdb/gdbarch.h:39,
                  from src/gdb/defs.h:636,
                  from src/gdb/top.c:20:
 src/gdb/command.h:434:8: error: ‘scoped_restore_tmpl’ does not name a type
  extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
         ^
 Makefile:1911: recipe for target 'top.o' failed


because completer.h includes command.h, and that include chain
happens before utils.h is included at the end of defs.h.
I think it'd be good if defs.h didn't pull in so much,
but I think that's orthogonal, and the current fix stands on
its own.

I should have included this info in the original submission.
Sorry about that.

Thanks,
Pedro Alves

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

* Re: [PATCH 08/40] completion_list_add_name wrapper functions
  2017-06-02 12:22 ` [PATCH 08/40] completion_list_add_name wrapper functions Pedro Alves
@ 2017-06-27 12:56   ` Yao Qi
  2017-06-27 15:35     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-27 12:56 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> I've had to adjust these macros numerous times while working on this
> series, adding/removing parameters while experimenting with designs.
> At some point, I got fed up with fighting the preprocessor, and
> decided to make them proper functions.
>
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>
> 	* symtab.c (COMPLETION_LIST_ADD_SYMBOL)
> 	(MCOMPLETION_LIST_ADD_SYMBOL): Delete macros, replace with ...
> 	(completion_list_add_symbol, completion_list_add_msymbol):
> 	... these new functions.
> 	(add_symtab_completions)
> 	(default_make_symbol_completion_list_break_on_1): Adjust.

It is obvious.  Patch is good to me.

-- 
Yao (齐尧)

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

* Re: [PATCH 07/40] objfile_per_bfd_storage non-POD
  2017-06-27 12:00   ` Yao Qi
@ 2017-06-27 15:30     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-27 15:30 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/27/2017 01:00 PM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> A following patch will want to add a std::vector to
>> objfile_per_bfd_storage.  That makes it non-trivially
>> constructible/destructible.  Since objfile_per_bfd_storage objects are
>> allocated on an obstack, we need to call their ctors/dtors manually.
>> This is what this patch does.  And then since we can now rely on
>> ctors/dtors being run, make objfile_per_bfd_storage::storage_obstack
>> be an auto_obstack.
> 
> It is good trick to class-fy some structures which are allocated on
> obstack.
> 
>>
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* objfiles.c (get_objfile_bfd_data): Call bfd_alloc instead of
>> 	bfd_zalloc.  Call objfile_per_bfd_storage's ctor.
>> 	(free_objfile_per_bfd_storage): Call objfile_per_bfd_storage's
>> 	dtor.
>> 	* objfiles.h (objfile_per_bfd_storage): Add ctor.  Make
>> 	'storage_obstack' field an auto_obstack.  In-class initialize all
>> 	non-bitfield fields.  Make minsyms_read bool.
>> 	* symfile.c (read_symbols): Adjust.
> 
> Patch is good to me.
> 

Thanks, pushed it in.

Thanks,
Pedro Alves

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

* Re: [PATCH 08/40] completion_list_add_name wrapper functions
  2017-06-27 12:56   ` Yao Qi
@ 2017-06-27 15:35     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-27 15:35 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/27/2017 01:56 PM, Yao Qi wrote:

>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* symtab.c (COMPLETION_LIST_ADD_SYMBOL)
>> 	(MCOMPLETION_LIST_ADD_SYMBOL): Delete macros, replace with ...
>> 	(completion_list_add_symbol, completion_list_add_msymbol):
>> 	... these new functions.
>> 	(add_symtab_completions)
>> 	(default_make_symbol_completion_list_break_on_1): Adjust.
> 
> It is obvious.  Patch is good to me.
> 

Pushed in too.

Thanks,
Pedro Alves

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

* Re: [PATCH 05/40] command.h: Include common/scoped_restore.h
  2017-06-27 12:03         ` Pedro Alves
@ 2017-06-27 15:46           ` Pedro Alves
  2017-06-28  7:54             ` Yao Qi
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-27 15:46 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 06/27/2017 01:02 PM, Pedro Alves wrote:

> I should have included this info in the original submission.
> Sorry about that.
> 

Here's the same patch with commit log updated to include the missing
info, and also fix the silly typo in the filename in the
subject/ChangeLog.

From 63aba45330a791ab60a79acbe90344c9deb260e9 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 1 Jun 2017 17:03:11 +0100
Subject: [PATCH 05/40] gdb/command.h: Include common/scoped_restore.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

command.h depends on scoped_restore:

  extern scoped_restore_tmpl<int> prevent_dont_repeat (void);

But doesn't include the corresponding header
("common/scoped_restore.h").  We haven't noticed a problem because
utils.h includes scoped_restore.h, and defs.h includes utils.h.

However, a patch that makes "symtab.h" include "completer.h", exposed
the issue:
 https://sourceware.org/ml/gdb-patches/2017-06/msg00023.html.

Without this fix that would break building all .o files like this:

 In file included from src/gdb/completer.h:21:0,
                  from src/gdb/symtab.h:28,
                  from src/gdb/language.h:26,
                  from src/gdb/frame.h:72,
                  from src/gdb/gdbarch.h:39,
                  from src/gdb/defs.h:636,
                  from src/gdb/top.c:20:
 src/gdb/command.h:434:8: error: ‘scoped_restore_tmpl’ does not name a type
  extern scoped_restore_tmpl<int> prevent_dont_repeat (void);
         ^
 Makefile:1911: recipe for target 'top.o' failed

because defs.h includes gdbarch.h before it includes utils.h.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* command.h: Include "common/scoped_restore.h".
---
 gdb/command.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gdb/command.h b/gdb/command.h
index aa179e9..4a56a51 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -19,6 +19,7 @@
 #define COMMAND_H 1
 
 #include "gdb_vecs.h"
+#include "common/scoped_restore.h"
 
 /* This file defines the public interface for any code wanting to
    create commands.  */
-- 
2.5.5


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

* Re: [PATCH 05/40] command.h: Include common/scoped_restore.h
  2017-06-27 15:46           ` [PATCH 05/40] command.h: Include common/scoped_restore.h Pedro Alves
@ 2017-06-28  7:54             ` Yao Qi
  2017-06-28 14:20               ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-28  7:54 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> Here's the same patch with commit log updated to include the missing
> info, and also fix the silly typo in the filename in the
> subject/ChangeLog.

That is clear.  Patch is good to me.

-- 
Yao (齐尧)

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

* Re: [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack
  2017-06-02 12:22 ` [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack Pedro Alves
  2017-06-26 13:47   ` Yao Qi
@ 2017-06-28 10:36   ` Yao Qi
  2017-06-28 14:39     ` Pedro Alves
  1 sibling, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-28 10:36 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> +/* An obstack that frees itself on scope exit.  */
> +struct auto_obstack : obstack
> +{
> +  auto_obstack ()
> +  { obstack_init (this); }
> +
> +  ~auto_obstack ()
> +  { obstack_free (this, NULL); }
> +
> +  /* Free all memory in the obstack but leave it valid for further
> +     allocation.  */
> +  void clear ()
> +  { obstack_free (this, obstack_base (this)); }
> +};

Hi Pedro,
Did you consider "upstream" this to include/obstack.h? so that gcc can
use it as well.

-- 
Yao (齐尧)

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

* Re: [PATCH 05/40] command.h: Include common/scoped_restore.h
  2017-06-28  7:54             ` Yao Qi
@ 2017-06-28 14:20               ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-28 14:20 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/28/2017 08:54 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> Here's the same patch with commit log updated to include the missing
>> info, and also fix the silly typo in the filename in the
>> subject/ChangeLog.
> 
> That is clear.  Patch is good to me.

Patch is pushed, then.

Thanks,
Pedro Alves

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

* Re: [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack
  2017-06-28 10:36   ` Yao Qi
@ 2017-06-28 14:39     ` Pedro Alves
  2017-06-28 21:33       ` Yao Qi
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-28 14:39 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/28/2017 11:36 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> +/* An obstack that frees itself on scope exit.  */
>> +struct auto_obstack : obstack
>> +{
>> +  auto_obstack ()
>> +  { obstack_init (this); }
>> +
>> +  ~auto_obstack ()
>> +  { obstack_free (this, NULL); }
>> +
>> +  /* Free all memory in the obstack but leave it valid for further
>> +     allocation.  */
>> +  void clear ()
>> +  { obstack_free (this, obstack_base (this)); }
>> +};
> 
> Hi Pedro,
> Did you consider "upstream" this to include/obstack.h? so that gcc can
> use it as well.
> 

A little, but not very seriously.  I did name the type as "auto_"
thinking that that's what GCC folks tend to name their
container/RAII types.

But OTOH, it feel a bit too early to propose that; it feels like
we could do more with extending the auto_obstack API; but OTOH I'm
not sure whether we'll continue to use obstacks directly that much;
I suspect that we may end up with allocators instead, thought
I haven't given that that much thought.

Also, I think that it couldn't be put in include/obstack.h,
since the obstack API requires that you define the
obstack_chunk_alloc/obstack_chunk_free macros (and GCC
defines those differently from us, which would be recipe
for ODR violations).

Also, unfortunately GCC is still C++03, and I didn't want to
paint myself into a corner.  :-)

Thanks,
Pedro Alves

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

* Re: [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack
  2017-06-28 14:39     ` Pedro Alves
@ 2017-06-28 21:33       ` Yao Qi
  0 siblings, 0 replies; 183+ messages in thread
From: Yao Qi @ 2017-06-28 21:33 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

On 17-06-28 15:39:28, Pedro Alves wrote:
> 
> A little, but not very seriously.  I did name the type as "auto_"
> thinking that that's what GCC folks tend to name their
> container/RAII types.
> 
> But OTOH, it feel a bit too early to propose that; it feels like
> we could do more with extending the auto_obstack API; but OTOH I'm

The reason I suggest to "upstream" it is its simplicity, and it doesn't
use any c++ 11 feature.

> not sure whether we'll continue to use obstacks directly that much;
> I suspect that we may end up with allocators instead, thought
> I haven't given that that much thought.

Agreed.  The reason I read your patch again is that I want to class-fy
some structures (frame_unwind for example) which are allocated on
obstack.

> 
> Also, I think that it couldn't be put in include/obstack.h,
> since the obstack API requires that you define the
> obstack_chunk_alloc/obstack_chunk_free macros (and GCC
> defines those differently from us, which would be recipe
> for ODR violations).
> 
> Also, unfortunately GCC is still C++03, and I didn't want to
> paint myself into a corner.  :-)
> 

No problem, that is fine to keep it in GDB.

-- 
Yao 

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

* Re: [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer
  2017-06-02 12:23 ` [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer Pedro Alves
@ 2017-06-28 21:40   ` Yao Qi
  2017-07-13 20:46   ` Keith Seitz
  1 sibling, 0 replies; 183+ messages in thread
From: Yao Qi @ 2017-06-28 21:40 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=unknown-8bit, Size: 774 bytes --]

On 17-06-02 13:22:07, Pedro Alves wrote:
> "make_symbol_completion_list_fn" is odly named when you look at a list
> of "standard" completers, like the Python/Guild completer lists
> adjusted by this patch.  Rename / move it to completers.h/c, for
> consistency.
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* completer.c (symbol_completer): New function, based on
> 	make_symbol_completion_list_fn.
> 	* completer.h (symbol_completer): New declaration.
> 	* guile/scm-cmd.c (cmdscm_completers): Adjust.
> 	* python/py-cmd.c (completers): Adjust.
> 	* symtab.c (make_symbol_completion_list_fn): Delete.
> 	* symtab.h  (make_symbol_completion_list_fn): Delete.
> 	* cli/cli-decode.c (add_cmd): Adjust.

Patch is good to me.

-- 
Yao (齐尧)

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

* Re: [PATCH 06/40] Expression completer should not match explicit location options
  2017-06-02 12:22 ` [PATCH 06/40] Expression completer should not match explicit location options Pedro Alves
@ 2017-06-29  8:29   ` Yao Qi
  2017-06-29 10:56     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-29  8:29 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> Currently, the expression completer matches explicit location options,
> which would only make sense for commands that work with linespecs, not
> expressions.
>
> I.e., currently, this:
>  "p -functi"
>
> Completes to:
>  "p -function "

I don't know much about completer, expression and explicit location, so
I don't know what do you fix here, anything wrong in the completion?

>
> This patch fixes that, and adds regression tests.

-- 
Yao (齐尧)

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

* Re: [PATCH 13/40] Introduce strncmp_iw
  2017-06-02 12:23 ` [PATCH 13/40] Introduce strncmp_iw Pedro Alves
@ 2017-06-29  8:42   ` Yao Qi
  2017-07-17 19:16     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-29  8:42 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> +/* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
> +   differences in whitespace.  STRING2_LEN is STRING2's length.
> +   Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
> +   non-zero otherwise (slightly different than strncmp()'s range of
> +   return values).  */
> +extern int strncmp_iw (const char *, const char *, size_t);

Use parameter name in the declaration, otherwise, STRING1, STRING2 and
STRING2_LEN in comments are pointless.

> +
> +/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
> +   differences in whitespace.  Returns 0 if they match, non-zero if
> +   they don't (slightly different than strcmp()'s range of return
> +   values).
> +
> +   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
> +   This "feature" is useful when searching for matching C++ function
> +   names (such as if the user types 'break FOO', where FOO is a
> +   mangled C++ function).  */
>  extern int strcmp_iw (const char *, const char *);

Otherwise, patch is good to me.

-- 
Yao (齐尧)

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

* Re: [PATCH 06/40] Expression completer should not match explicit location options
  2017-06-29  8:29   ` Yao Qi
@ 2017-06-29 10:56     ` Pedro Alves
  2017-06-29 11:08       ` Pedro Alves
  2017-06-29 11:24       ` Yao Qi
  0 siblings, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-29 10:56 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/29/2017 09:29 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> Currently, the expression completer matches explicit location options,
>> which would only make sense for commands that work with linespecs, not
>> expressions.
>>
>> I.e., currently, this:
>>  "p -functi"
>>
>> Completes to:
>>  "p -function "
> 
> I don't know much about completer, expression and explicit location, so
> I don't know what do you fix here, anything wrong in the completion?
> 

This is fixing a mismatch between what the completer thinks the
print command understands, and what the command really understands.

"-function" is an explicit location option that is understood by
commands that take (linespecs and) explicit locations as argument.
I.e, breakpoint commands, and "list".  For example:

 (gdb) b -source file.c -function my_func

So for those commands, it makes sense that the completer
completes 

 "b -sour[TAB]" -> "b -source "
 "b -functi[TAB]" -> "b -function "

etc.

(
A bit orthogonal, but for clarity: currently, the explicit locations
completer doesn't help much with discovering the supported options,
because "b -[TAB]" doesn't show you the list of options.
It shows the _whole_ list of symbols in the program, which is
useless...  After the series, specifically after the
"A smarter linespec completer" patch, it will, though:

 (gdb) b -[TAB]
 -function      -label         -line          -probe         -probe-dtrace  -probe-stap    -qualified     -source        
)

However, commands that take expressions (not linespecs/locations) as
arguments, such as the "print" command, do _not_ understand
the "-source", "-function" etc. switches.  Instead, "-foo" is
understood as an expression applying unary minus on a symbol
named "foo" (think "print -1").
So it does not make sense for the associated completer to
complete on those options:

 (gdb) p -func[TAB]
 (gdb) p -function [RET]
 No symbol "function" in current context.

The patch fixes this by having the expression_completer function
bypass the function that completes explicit locations:

    /* Not ideal but it is what we used to do before...  */
 -  return location_completer (ignore, p, word);
 +  return linespec_location_completer (ignore, text, word);

(Note how location_completer calls into linespec_location_completer.)

>> This patch fixes that, and adds regression tests.

Thanks,
Pedro Alves

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

* Re: [PATCH 06/40] Expression completer should not match explicit location options
  2017-06-29 10:56     ` Pedro Alves
@ 2017-06-29 11:08       ` Pedro Alves
  2017-06-29 15:23         ` Pedro Alves
  2017-06-29 11:24       ` Yao Qi
  1 sibling, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-06-29 11:08 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 06/29/2017 11:56 AM, Pedro Alves wrote:

> The patch fixes this by having the expression_completer function
> bypass the function that completes explicit locations:
> 
>     /* Not ideal but it is what we used to do before...  */
>  -  return location_completer (ignore, p, word);
>  +  return linespec_location_completer (ignore, text, word);
> 

Hmm, reading this back made my realize that I'm dropping "p"
here, which is computed just above.  That doesn't make a difference
to the test added by 37cd5d19fecc (2008), which was the commit
that added that code.  Hmm, looks like that's a bit of dead code
we can remove.

Thanks,
Pedro Alves

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

* Re: [PATCH 06/40] Expression completer should not match explicit location options
  2017-06-29 10:56     ` Pedro Alves
  2017-06-29 11:08       ` Pedro Alves
@ 2017-06-29 11:24       ` Yao Qi
  2017-06-29 15:25         ` Pedro Alves
  1 sibling, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-06-29 11:24 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

Pedro Alves <palves@redhat.com> writes:

> commands that take expressions (not linespecs/locations) as
> arguments, such as the "print" command, do _not_ understand
> the "-source", "-function" etc. switches.  Instead, "-foo" is
> understood as an expression applying unary minus on a symbol
> named "foo" (think "print -1").

This paragraph is useful.  Thanks for the explanation.  Patch is good to
me.

-- 
Yao (齐尧)

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

* Re: [PATCH 06/40] Expression completer should not match explicit location options
  2017-06-29 11:08       ` Pedro Alves
@ 2017-06-29 15:23         ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-29 15:23 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 06/29/2017 12:07 PM, Pedro Alves wrote:
> On 06/29/2017 11:56 AM, Pedro Alves wrote:
> 
>> The patch fixes this by having the expression_completer function
>> bypass the function that completes explicit locations:
>>
>>     /* Not ideal but it is what we used to do before...  */
>>  -  return location_completer (ignore, p, word);
>>  +  return linespec_location_completer (ignore, text, word);
>>
> 
> Hmm, reading this back made my realize that I'm dropping "p"
> here, which is computed just above.  That doesn't make a difference
> to the test added by 37cd5d19fecc (2008), which was the commit
> that added that code.  Hmm, looks like that's a bit of dead code
> we can remove.
> 


I've split that to a separate preparatory patch and pushed it in,
as below, after doing some archaeology.

From 195bcdd5183f2c137399db23a68a26a4e4193f8f Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 29 Jun 2017 15:52:37 +0100
Subject: [PATCH] Remove old stale expression_completer hack

The code in question was introduced by:

 https://sourceware.com/ml/gdb-patches/2008-06/msg00143.html

"The fix is to make sure that the entire expression is passed to
expression_completer, then duplicate some logic there in the case
where location_completer is called."

The logic that was duplicated was much later on removed by the
original explicit locations patch:

 commit 87f0e7204722a986f79f245eee716f0870832d47
 Author:     Keith Seitz <keiths@redhat.com>
 AuthorDate: Tue Aug 11 17:09:36 2015 -0700
 Commit:     Keith Seitz <keiths@redhat.com>
 CommitDate: Tue Aug 11 17:09:36 2015 -0700

     Explicit locations: add UI features for CLI

 @@ -688,16 +880,6 @@ complete_line_internal (const char *text,
		       rl_completer_word_break_characters =
			 gdb_completer_file_name_break_characters;
		     }
 -                 else if (c->completer == location_completer)
 -                   {
 -                     /* Commands which complete on locations want to
 -                        see the entire argument.  */
 -                     for (p = word;
 -                          p > tmp_command
 -                            && p[-1] != ' ' && p[-1] != '\t';
 -                          p--)
 -                       ;
 -                   }

However this case in expression_completer was left behind.

I couldn't come up with a test where this currently makes any
difference.

gdb/ChangeLog:
2017-06-29  Pedro Alves  <palves@redhat.com>

	* completer.c (expression_completer): Remove code that recomputes
	'text' from 'word'.
---
 gdb/ChangeLog   |  5 +++++
 gdb/completer.c | 10 +---------
 2 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9be08dd..c76158f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2017-06-29  Pedro Alves  <palves@redhat.com>
+
+	* completer.c (expression_completer): Remove code that recomputes
+	'text' from 'word'.
+
 2017-06-29  Yao Qi  <yao.qi@linaro.org>
 
 	* regformats/regdat.sh: Generate code with
diff --git a/gdb/completer.c b/gdb/completer.c
index 6acf115..f152dd5 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -587,7 +587,6 @@ expression_completer (struct cmd_list_element *ignore,
 {
   struct type *type = NULL;
   char *fieldname;
-  const char *p;
   enum type_code code = TYPE_CODE_UNDEF;
 
   /* Perform a tentative parse of the expression, to see whether a
@@ -635,15 +634,8 @@ expression_completer (struct cmd_list_element *ignore,
     }
   xfree (fieldname);
 
-  /* Commands which complete on locations want to see the entire
-     argument.  */
-  for (p = word;
-       p > text && p[-1] != ' ' && p[-1] != '\t';
-       p--)
-    ;
-
   /* Not ideal but it is what we used to do before...  */
-  return location_completer (ignore, p, word);
+  return location_completer (ignore, text, word);
 }
 
 /* See definition in completer.h.  */
-- 
2.5.5


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

* Re: [PATCH 06/40] Expression completer should not match explicit location options
  2017-06-29 11:24       ` Yao Qi
@ 2017-06-29 15:25         ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-06-29 15:25 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/29/2017 12:24 PM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> commands that take expressions (not linespecs/locations) as
>> arguments, such as the "print" command, do _not_ understand
>> the "-source", "-function" etc. switches.  Instead, "-foo" is
>> understood as an expression applying unary minus on a symbol
>> named "foo" (think "print -1").
> 
> This paragraph is useful.  Thanks for the explanation.  Patch is good to
> me.

Rewrote the commit log in that direction, and pushed it in, then.

From eb17d4137dc83a373b8968cd20b256fa8073a4ca Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 29 Jun 2017 15:52:38 +0100
Subject: [PATCH] Expression completer should not match explicit location
 options

This commit fixes a mismatch between what "print" command completer
thinks the command understands, and what the command actually
understands.

The explicit location options are understood by commands that take
(linespecs and) explicit locations as argument.  I.e, breakpoint
commands, and "list".  For example:

 (gdb) b -source file.c -function my_func

So for those commands, it makes sense that the completer
completes:

 "b -sour[TAB]" -> "b -source "
 "b -functi[TAB]" -> "b -function "

etc.

However, completion for commands that take expressions (not
linespecs/locations) as arguments, such as the "print" command, also
completes the explicit location options, even though those switches
aren't really understood by these commands.  Instead, "-foo" is
understood as an expression applying unary minus on a symbol named
"foo" (think "print -1"):

 (gdb) p -func[TAB]
 (gdb) p -function [RET]
 No symbol "function" in current context.

The patch fixes this by having the expression_completer function
bypass the function that completes explicit locations.

New regression tests included.

gdb/ChangeLog:
2017-06-29  Pedro Alves  <palves@redhat.com>

	* completer.c (expression_completer): Call
	linespec_location_completer instead of location_completer.

gdb/testsuite/ChangeLog:
2017-06-29  Pedro Alves  <palves@redhat.com>

	* gdb.base/printcmds.exp: Add tests.
---
 gdb/ChangeLog                        | 5 +++++
 gdb/testsuite/ChangeLog              | 4 ++++
 gdb/completer.c                      | 2 +-
 gdb/testsuite/gdb.base/printcmds.exp | 6 ++++++
 4 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index c76158f..b47226bc 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,10 @@
 2017-06-29  Pedro Alves  <palves@redhat.com>
 
+	* completer.c (expression_completer): Call
+	linespec_location_completer instead of location_completer.
+
+2017-06-29  Pedro Alves  <palves@redhat.com>
+
 	* completer.c (expression_completer): Remove code that recomputes
 	'text' from 'word'.
 
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index b7462a5..41c5434 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2017-06-29  Pedro Alves  <palves@redhat.com>
+
+	* gdb.base/printcmds.exp: Add tests.
+
 2017-06-28  Doug Gilmore  <Doug.Gilmore@imgtec.com>
 
 	PR gdb/21337
diff --git a/gdb/completer.c b/gdb/completer.c
index f152dd5..68e68eb 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -635,7 +635,7 @@ expression_completer (struct cmd_list_element *ignore,
   xfree (fieldname);
 
   /* Not ideal but it is what we used to do before...  */
-  return location_completer (ignore, text, word);
+  return linespec_location_completer (ignore, text, word);
 }
 
 /* See definition in completer.h.  */
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index d949b30..323ca73 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -931,6 +931,12 @@ gdb_test "ptype \"abc\"" " = char \\\[4\\\]"
 gdb_test "print \$cvar = \"abc\"" " = \"abc\""
 gdb_test "print sizeof (\$cvar)" " = 4"
 
+# GDB used to complete the explicit location options even when
+# printing expressions.
+gdb_test_no_output "complete p -function"
+gdb_test_no_output "complete p -line"
+gdb_test_no_output "complete p -source"
+
 gdb_file_cmd ${binfile}
 
 gdb_test "print \$pc" "No registers\\." "print \$pc (with file)"
-- 
2.5.5

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

* Re: [PATCH 03/40] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index
  2017-06-02 12:22 ` [PATCH 03/40] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index Pedro Alves
@ 2017-07-13 20:28   ` Keith Seitz
  2017-07-14 16:02     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-13 20:28 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

I've looked over this patch, and it looks fine to me. One little request:

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 22d81fa..69f3bc2 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -5385,7 +5385,6 @@ static VEC (char_ptr) *
>  make_file_symbol_completion_list_1 (const char *text, const char *word,
>  				    const char *srcfile)
>  {
> -  struct symtab *s;
>    /* The symbol we are completing on.  Points in same buffer as text.  */
>    const char *sym_text;
>    /* Length of sym_text.  */

The comment for this function is (and also later when it is renamed to collect_file_symbol_completion_matches)

/* Like collect_symbol_completion_matches, but collects a list of
   symbols defined in a source file FILE.  */
                     ^^^^^^^^^^^^^^^^^^^^

I think that underlined bit should be updated to be a bit clearer. While it is pedantically correct, I think "in all source files named FILE" tells the reader of other important behavior/intent.

Keith

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

* Re: [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache)
  2017-06-02 12:31 ` [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache) Pedro Alves
@ 2017-07-13 20:41   ` Keith Seitz
  2017-07-14 19:40     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-13 20:41 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

I just have a little comment...

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> diff --git a/gdb/filename-seen-cache.h b/gdb/filename-seen-cache.h
> new file mode 100644
> index 0000000..c041d3e
> --- /dev/null
> +++ b/gdb/filename-seen-cache.h
> @@ -0,0 +1,64 @@
> +/* Filename-seen cache for the GNU debugger, GDB.
> +
> +   Copyright (C) 1986-2017 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "defs.h"
> +#include "common/function-view.h"
> +
> +/* Cache to watch for file names already seen.  */
> +
> +class filename_seen_cache
> +{
> +public:
> +  filename_seen_cache ();
> +  ~filename_seen_cache ();
> +
> +  /* Disable copy.  */
> +  filename_seen_cache (const filename_seen_cache &) = delete;
> +  void operator= (const filename_seen_cache &) = delete;
> +
> +  /* Empty the cache, but do not delete it.  */
> +  void clear ();
> +
> +  /* If FILE is not already in the table of files in CACHE, return
> +     false; otherwise return true.  Optionally add FILE to the table
> +     if ADD is true.
> +
> +     NOTE: We don't manage space for FILE, we assume FILE lives as
> +     long as the caller needs.  */
> +  bool filename_seen (const char *file, bool add);

I'm not really a fan of this style of interface. [I realize we've done this for many years.] When reading through code that uses this, e.g.,

   bool seen = cache.filename_seen (file, true);

that "true" doesn't really tell me what that means, and in the context of the method itself, it isn't obvious (to me, at least) what "true" might mean when asking if we've seen a filename. A reader could think that it somehow altered how the search was performed, e.g., case-insensitive or whatnot.

Now if this read

  bool seen = cache.filename_seen (file, ADD);

That reveals much more about what is going to happen.

But that's nitpicky style comment (aka there's no reason to change anything). I didn't otherwise see any issues with the patch.

Keith

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

* Re: [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer
  2017-06-02 12:23 ` [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer Pedro Alves
  2017-06-28 21:40   ` Yao Qi
@ 2017-07-13 20:46   ` Keith Seitz
  2017-07-17 11:00     ` Pedro Alves
  1 sibling, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-13 20:46 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> "make_symbol_completion_list_fn" is odly named when you look at a list
> of "standard" completers, like the Python/Guild completer lists
> adjusted by this patch.  Rename / move it to completers.h/c, for
> consistency.

Oh, man, I totally agree. make_symbol_completion_list_fn sounds more like a typedef name!

And who could possibly complain about shortening symtab.[ch]. Those files are already a little bloated with non-symtab stuff.

I see no issues with this patch.

Keith

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

* Re: [PATCH 10/40] Clean up "completer_handle_brkchars" callback handling
  2017-06-02 12:23 ` [PATCH 10/40] Clean up "completer_handle_brkchars" callback handling Pedro Alves
@ 2017-07-13 21:08   ` Keith Seitz
  2017-07-17 11:14     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-13 21:08 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> This patch cleans up "completer_handle_brkchars" callback handling:
> 
> - Renames the function typedef to better match its intent:
>   completer_ftype_void ->  completer_handle_brkchars_ftype
> 
> - Factors out common code in complete_line_internal handling the
>   "handle_brkchars" callback to a separate function.
> 
> - Centralizes all the "completer method" to "handle_brkchars method"
>   mapping in a single function.
> 

I don't see any issues.

Keith

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

* Re: [PATCH 03/40] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index
  2017-07-13 20:28   ` Keith Seitz
@ 2017-07-14 16:02     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-14 16:02 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/13/2017 09:28 PM, Keith Seitz wrote:

> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> diff --git a/gdb/symtab.c b/gdb/symtab.c
>> index 22d81fa..69f3bc2 100644
>> --- a/gdb/symtab.c
>> +++ b/gdb/symtab.c
>> @@ -5385,7 +5385,6 @@ static VEC (char_ptr) *
>>  make_file_symbol_completion_list_1 (const char *text, const char *word,
>>  				    const char *srcfile)
>>  {
>> -  struct symtab *s;
>>    /* The symbol we are completing on.  Points in same buffer as text.  */
>>    const char *sym_text;
>>    /* Length of sym_text.  */
> 
> The comment for this function is (and also later when it is renamed to collect_file_symbol_completion_matches)
> 
> /* Like collect_symbol_completion_matches, but collects a list of
>    symbols defined in a source file FILE.  */
>                      ^^^^^^^^^^^^^^^^^^^^
> 
> I think that underlined bit should be updated to be a bit clearer. While it is pedantically correct, I think "in all source files named FILE" tells the reader of other important behavior/intent.

Agreed.  I did that change and pushed it in (with s/FILE/SRCFILE/
to match the actual parameter name while at it.)

Thanks,
Pedro Alves

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

* Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction
  2017-06-02 12:23 ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Pedro Alves
@ 2017-07-14 17:23   ` Keith Seitz
  2017-07-17 13:56     ` Pedro Alves
  2018-03-05 21:43   ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Simon Marchi
  1 sibling, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-14 17:23 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:

> Adds a new "completion_tracker" class that is meant to hold everything
> about the state of the current completion operation.

This is quite close to the approach I attempted in the series I submitted (cough) in 2015.

Just have a few minor comments (about comments!).

> diff --git a/gdb/completer.c b/gdb/completer.c
> index fe69faa..c6e1e28 100644
> --- a/gdb/completer.c
> +++ b/gdb/completer.c
> @@ -429,10 +420,10 @@ backup_text_ptr (const char *p, const char *text)
>  /* A completer function for explicit locations.  This function
>     completes both options ("-source", "-line", etc) and values.  */
>  
> -static VEC (char_ptr) *
> -explicit_location_completer (struct cmd_list_element *ignore,
> -			     struct event_location *location,
> -			     const char *text, const char *word)
> +static void
> +complete_explicit_location (completion_tracker &tracker,
> +			    struct event_location *location,
> +			    const char *text, const char *word)
>  {
>    const char *p;
>    VEC (char_ptr) *matches = NULL;

`matches' is no longer used. [I realize this will disappear in a later patch.]

>  /* See completer.h.  */
>  
> -enum maybe_add_completion_enum
> -maybe_add_completion (completion_tracker_t tracker, char *name)
> +bool
> +completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
>  {
[snip]

>  
> -  return (htab_elements (tracker) < max_completions
> -	  ? MAYBE_ADD_COMPLETION_OK
> -	  : MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
> +void
> +completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
> +{
> +  if (!maybe_add_completion (std::move (name)))
> +    throw_max_completions_reached_error ();
>  }
>  
>  void
> @@ -1075,10 +1075,16 @@ throw_max_completions_reached_error (void)
>    throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
>  }
>  
> -/* Generate completions all at once.  Returns a vector of unique strings
> -   allocated with xmalloc.  Returns NULL if there are no completions
> -   or if max_completions is 0.  If max_completions is non-negative, this will
> -   return at most max_completions strings.
> +void
> +completion_tracker::add_completions (completion_list &&list)
> +{
> +  for (auto &candidate : list)
> +    add_completion (std::move (candidate));
> +}

Some of the above methods have comments (per convention, "See XYZ.h"), some do not.  [There are a bunch more methods with no comments below this, too.]

Is this requirement being relaxed for C++? I certainly wouldn't mind if we started assuming "See XYZ.h" for all methods, but I don't think convention has been discussed/codified yet.

> +
> +/* Build a new C string that is a copy or LCD with the whitespace of
> +   ORIG/ORIG_LEN preserved.

Is this supposed to be "a copy *of* LCD"?

> +
> +/* Helper for gdb_rl_attempted_completion_function, which does most of
> +   the work.  This is called by readline to build the match list
> +   array, and determining the lowest common denominator.  The real

This last sentence isn't right. At its simplest, it says, "This is called, and determining the lowest common denominator." Is that supposed to be, "This is called to build.. and to determine"?

> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 09c9411b..cd78a16 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
>  
>  /* Return a vector of all symbols (regardless of class) which begin by
>     matching TEXT.  If the answer is no symbols, then the return value
>     is NULL.  */

This comment needs updating ("Return a vector...").

>  
> -VEC (char_ptr) *
> -make_symbol_completion_list (const char *text, const char *word)
> +void
> +collect_symbol_completion_matches (completion_tracker &tracker,
> +				   const char *text, const char *word)
>  {
> -  return current_language->la_make_symbol_completion_list (text, word,
> -							   TYPE_CODE_UNDEF);
> +  current_language->la_collect_symbol_completion_matches (tracker,
> +							  text, word,
> +							  TYPE_CODE_UNDEF);
>  }
>  
> -/* Like make_symbol_completion_list, but only return STRUCT_DOMAIN
> -   symbols whose type code is CODE.  */
> +/* Like collect_symbol_completion_matches, but only return
> +   STRUCT_DOMAIN symbols whose type code is CODE.  */
>  

s/return/collect/ ?

> -VEC (char_ptr) *
> -make_symbol_completion_type (const char *text, const char *word,
> -			     enum type_code code)
> +void
> +collect_symbol_completion_matches_type (completion_tracker &tracker,
> +					const char *text, const char *word,
> +					enum type_code code)
>  {
>    gdb_assert (code == TYPE_CODE_UNION
>  	      || code == TYPE_CODE_STRUCT
>  	      || code == TYPE_CODE_ENUM);
> -  return current_language->la_make_symbol_completion_list (text, word, code);
> +  current_language->la_collect_symbol_completion_matches (tracker,
> +							  text, word, code);
>  }
>  
> @@ -5503,17 +5419,16 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
>  
>  /* Return a vector of all source files whose names begin with matching
>     TEXT.  The file names are looked up in the symbol tables of this
> -   program.  If the answer is no matchess, then the return value is
> -   NULL.  */
> +   program.  */

This comment also needs updating ("Return a vector...").

Keith

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

* Re: [PATCH 12/40] "complete" command and completion word break characters
  2017-06-02 12:29 ` [PATCH 12/40] "complete" command and completion word break characters Pedro Alves
@ 2017-07-14 17:50   ` Keith Seitz
  2017-07-17 14:36     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-14 17:50 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> First, the complete command has a too-simple approximation of what
> readline's TAB-completion code does to find the completion word point.
> Unfortunately, readline doesn't expose the functionality it uses
> internally, so to fix this this patch copies over the relevant code,
> and adjusts it a bit to better fit the use cases we need it for.
> (Specifically, our version avoids relying on the
> rl_word_break_characters, etc. globals, and instead takes those as
> arguments.)

re: "copies over the relevant code"
Is it possible to mention this in/around the copied code? That might make it easier to track differences in the future, e.g., if someone found a problem with the copied code, he could look upstream for a fix (or report a bug).

> 
> diff --git a/gdb/completer.h b/gdb/completer.h
> index e554bff..207781d 100644
> --- a/gdb/completer.h
> +++ b/gdb/completer.h
> @@ -203,6 +203,10 @@ extern void complete_line (completion_tracker &tracker,
>  			   const char *line_buffer,
>  			   int point);
>  
> +extern const char *completion_find_completion_word (completion_tracker &tracker,
> +						    const char *text,
> +						    int *quote_char);
> +

Missing comment?

Keith

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

* Re: [PATCH 14/40] Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout
  2017-06-02 12:22 ` [PATCH 14/40] Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout Pedro Alves
@ 2017-07-14 18:04   ` Keith Seitz
  2017-07-17 14:55     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-14 18:04 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> I ran into LENGTH_OF_OPERATOR in cp-support.c and wanted to use it
> elsewhere, so I moved it to cp-support.h.  Since there's already
> CP_ANONYMOUS_NAMESPACE_STR/CP_ANONYMOUS_NAMESPACE_LEN there, I
> followed the same naming pattern for the new symbols.

Looks pretty straightforward (even?) to me, but I do notice that there are still one or two places that could be updated:

c-exp.y:2281:    {"operator", OPERATOR, OP_NULL, FLAG_CXX},
cp-name-parser.y:1839:      if (strncmp (tokstart, "operator", 8) == 0)

Keith

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

* Re: [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache)
  2017-07-13 20:41   ` Keith Seitz
@ 2017-07-14 19:40     ` Pedro Alves
  2017-07-17 10:51       ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-14 19:40 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/13/2017 09:41 PM, Keith Seitz wrote:


>> +
>> +     NOTE: We don't manage space for FILE, we assume FILE lives as
>> +     long as the caller needs.  */
>> +  bool filename_seen (const char *file, bool add);
> 
> I'm not really a fan of this style of interface. [I realize we've done this for many years.] When reading through code that uses this, e.g.,

Yeah...  I'm not a fan either.  It's known as "the boolean trap".
This one was preexisting, and I should have probably tried
to fix it, because then I'd have realized then that _all_ callers
pass "true".  :-)  So we can just remove the parameter.
I did that now.  I also shortened the method name from "filename_seen" 
to just "seen", since the prefix wasn't really adding
anything.

> 
>    bool seen = cache.filename_seen (file, true);
> 
> that "true" doesn't really tell me what that means, and in the context of the method itself, it isn't obvious (to me, at least) what "true" might mean when asking if we've seen a filename. A reader could think that it somehow altered how the search was performed, e.g., case-insensitive or whatnot.
> 
> Now if this read
> 
>   bool seen = cache.filename_seen (file, ADD);
> 
> That reveals much more about what is going to happen.
> 
> But that's nitpicky style comment (aka there's no reason to change anything). I didn't otherwise see any issues with the patch.

I was about to push it when I realized that I missed releasing the
new heap-allocated dwarf2_per_objfile::filenames_cache...

To address that, I made it possible for dwarf2_per_objfile to be
a non-POD:

  https://sourceware.org/ml/gdb-patches/2017-07/msg00202.html

and made dwarf2_per_objfile::filenames_cache an optional, so the
cache object (though not its elements) is on the obstack too.

Here's the updated patch.

From b83f66dac43ec3458e0bd4b3883437aefe28d1b6 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 14 Jul 2017 20:24:02 +0100
Subject: [PATCH] Fix TAB-completion + .gdb_index slowness (generalize
 filename_seen_cache)

Tab completion when debugging a program binary that uses GDB index is
surprisingly much slower than when GDB uses psymtabs instead.  Around
1.5x/3x slower.  That's surprising, because the whole point of GDB
index is to speed things up...

For example, with:

 set pagination off
 set $count = 0
 while $count < 400
   complete b string_prin         # matches gdb's string_printf
   printf "count = %d\n", $count
   set $count = $count + 1
 end

 $ time ./gdb --batch -q  ./gdb-with-index -ex "source script.cmd"
 real    0m11.042s
 user    0m10.920s
 sys     0m0.042s

 $ time ./gdb --batch -q  ./gdb-without-index -ex "source script.cmd"
 real    0m4.635s
 user    0m4.590s
 sys     0m0.037s

Same but with:
 -   complete b string_prin
 +   complete b zzzzzz
to exercise the no-matches worst case, master currently gets you
something like:

 with index           without index
 real    0m11.971s    0m8.413s
 user    0m11.912s    0m8.355s
 sys     0m0.035s     0m0.035s

Running gdb under perf shows 80% spent inside
maybe_add_partial_symtab_filename, and 20% spent in the lbasename
inside that.

The problem that tab completion walks over all compunit symtabs, and
for each, walks the contained file symtabs.  And there a huge number
of file symtabs (each included system header, etc.) that appear in
each compunit symtab's file symtab list.  As in, when debugging GDB, I
have 367381 symtabs iterated, when of those only 5371 filenames are
unique...

This was a regression from the earlier (nice) split of symtabs in
compunit symtabs + file symtabs.

The fix here is to add a cache of unique filenames per objfile so that
the walk / uniquing is only done once.  There's already a abstraction
for this in symtab.c; this patch moves that code out to a separate
file and C++ifies it bit.

This makes the worst-case scenario above consistently drop to ~2.5s
(1.5s for the "string_prin" hit case), making it over 3.3x times
faster than psymtabs in this use case (7x in the "string_prin" hit
case).

gdb/ChangeLog:
2017-07-14  Pedro Alves  <palves@redhat.com>

	* Makefile.in (COMMON_OBS): Add filename-seen-cache.o.
	* dwarf2read.c: Include "filename-seen-cache.h".
	* dwarf2read.c (dwarf2_per_objfile) <filenames_cache>: New field.
	(dw2_map_symbol_filenames): Build and use a filenames_seen_cache.
	* filename-seen-cache.c: New file.
	* filename-seen-cache.h: New file.
	* symtab.c: Include "filename-seen-cache.h".
	(struct filename_seen_cache, INITIAL_FILENAME_SEEN_CACHE_SIZE)
	(create_filename_seen_cache, clear_filename_seen_cache)
	(delete_filename_seen_cache, filename_seen): Delete, parts moved
	to filename-seen-cache.h/filename-seen-cache.c.
	(output_source_filename, sources_info)
	(maybe_add_partial_symtab_filename)
	(make_source_files_completion_list): Adjust to use
	filename_seen_cache.
---
 gdb/ChangeLog             |  18 ++++++++
 gdb/Makefile.in           |   1 +
 gdb/dwarf2read.c          | 104 ++++++++++++++++++++++++++--------------------
 gdb/filename-seen-cache.c |  66 +++++++++++++++++++++++++++++
 gdb/filename-seen-cache.h |  63 ++++++++++++++++++++++++++++
 gdb/symtab.c              | 100 ++++++--------------------------------------
 6 files changed, 220 insertions(+), 132 deletions(-)
 create mode 100644 gdb/filename-seen-cache.c
 create mode 100644 gdb/filename-seen-cache.h

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8c6a4f4..c9eae0a 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,23 @@
 2017-07-14  Pedro Alves  <palves@redhat.com>
 
+	* Makefile.in (COMMON_OBS): Add filename-seen-cache.o.
+	* dwarf2read.c: Include "filename-seen-cache.h".
+	* dwarf2read.c (dwarf2_per_objfile) <filenames_cache>: New field.
+	(dw2_map_symbol_filenames): Build and use a filenames_seen_cache.
+	* filename-seen-cache.c: New file.
+	* filename-seen-cache.h: New file.
+	* symtab.c: Include "filename-seen-cache.h".
+	(struct filename_seen_cache, INITIAL_FILENAME_SEEN_CACHE_SIZE)
+	(create_filename_seen_cache, clear_filename_seen_cache)
+	(delete_filename_seen_cache, filename_seen): Delete, parts moved
+	to filename-seen-cache.h/filename-seen-cache.c.
+	(output_source_filename, sources_info)
+	(maybe_add_partial_symtab_filename)
+	(make_source_files_completion_list): Adjust to use
+	filename_seen_cache.
+
+2017-07-14  Pedro Alves  <palves@redhat.com>
+
 	* symtab.c (make_file_symbol_completion_list_1): Iterate over
 	symtabs matching all symtabs with SRCFILE as file name instead of
 	only considering the first hit, with lookup_symtab.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b27f698..c6e618a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1714,6 +1714,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	f-typeprint.o \
 	f-valprint.o \
 	fileio.o \
+	filename-seen-cache.o \
 	filestuff.o \
 	filesystem.o \
 	findcmd.o \
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 334adaf..beb3401 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -74,7 +74,7 @@
 #include "common/gdb_optional.h"
 #include "common/underlying.h"
 #include "common/byte-vector.h"
-
+#include "filename-seen-cache.h"
 #include <fcntl.h>
 #include <sys/types.h>
 #include <algorithm>
@@ -345,6 +345,9 @@ public:
 
   /* Table containing line_header indexed by offset and offset_in_dwz.  */
   htab_t line_header_hash {};
+
+  /* Table containing all filenames.  */
+  gdb::optional<filename_seen_cache> filenames_cache;
 };
 
 static struct dwarf2_per_objfile *dwarf2_per_objfile;
@@ -4311,64 +4314,75 @@ static void
 dw2_map_symbol_filenames (struct objfile *objfile, symbol_filename_ftype *fun,
 			  void *data, int need_fullname)
 {
-  int i;
-  htab_up visited (htab_create_alloc (10, htab_hash_pointer, htab_eq_pointer,
-				      NULL, xcalloc, xfree));
-
   dw2_setup (objfile);
 
-  /* The rule is CUs specify all the files, including those used by
-     any TU, so there's no need to scan TUs here.
-     We can ignore file names coming from already-expanded CUs.  */
-
-  for (i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
+  if (!dwarf2_per_objfile->filenames_cache)
     {
-      struct dwarf2_per_cu_data *per_cu = dw2_get_cutu (i);
+      dwarf2_per_objfile->filenames_cache.emplace ();
 
-      if (per_cu->v.quick->compunit_symtab)
-	{
-	  void **slot = htab_find_slot (visited.get (),
-					per_cu->v.quick->file_names,
-					INSERT);
+      htab_up visited (htab_create_alloc (10,
+					  htab_hash_pointer, htab_eq_pointer,
+					  NULL, xcalloc, xfree));
 
-	  *slot = per_cu->v.quick->file_names;
-	}
-    }
-
-  for (i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
-    {
-      int j;
-      struct dwarf2_per_cu_data *per_cu = dw2_get_cu (i);
-      struct quick_file_names *file_data;
-      void **slot;
+      /* The rule is CUs specify all the files, including those used
+	 by any TU, so there's no need to scan TUs here.  We can
+	 ignore file names coming from already-expanded CUs.  */
 
-      /* We only need to look at symtabs not already expanded.  */
-      if (per_cu->v.quick->compunit_symtab)
-	continue;
+      for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
+	{
+	  struct dwarf2_per_cu_data *per_cu = dw2_get_cutu (i);
 
-      file_data = dw2_get_file_names (per_cu);
-      if (file_data == NULL)
-	continue;
+	  if (per_cu->v.quick->compunit_symtab)
+	    {
+	      void **slot = htab_find_slot (visited.get (),
+					    per_cu->v.quick->file_names,
+					    INSERT);
 
-      slot = htab_find_slot (visited.get (), file_data, INSERT);
-      if (*slot)
-	{
-	  /* Already visited.  */
-	  continue;
+	      *slot = per_cu->v.quick->file_names;
+	    }
 	}
-      *slot = file_data;
 
-      for (j = 0; j < file_data->num_file_names; ++j)
+      for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
 	{
-	  const char *this_real_name;
+	  int j;
+	  struct dwarf2_per_cu_data *per_cu = dw2_get_cu (i);
+	  struct quick_file_names *file_data;
+	  void **slot;
 
-	  if (need_fullname)
-	    this_real_name = dw2_get_real_path (objfile, file_data, j);
-	  else
-	    this_real_name = NULL;
-	  (*fun) (file_data->file_names[j], this_real_name, data);
+	  /* We only need to look at symtabs not already expanded.  */
+	  if (per_cu->v.quick->compunit_symtab)
+	    continue;
+
+	  file_data = dw2_get_file_names (per_cu);
+	  if (file_data == NULL)
+	    continue;
+
+	  slot = htab_find_slot (visited.get (), file_data, INSERT);
+	  if (*slot)
+	    {
+	      /* Already visited.  */
+	      continue;
+	    }
+	  *slot = file_data;
+
+	  for (int j = 0; j < file_data->num_file_names; ++j)
+	    {
+	      const char *filename = file_data->file_names[j];
+	      dwarf2_per_objfile->filenames_cache->seen (filename);
+	    }
 	}
     }
+
+  dwarf2_per_objfile->filenames_cache->traverse ([&] (const char *filename)
+    {
+      const char *this_real_name;
+
+      if (need_fullname)
+	this_real_name = gdb_realpath (filename);
+      else
+	this_real_name = NULL;
+      (*fun) (filename, this_real_name, data);
+    });
 }
 
 static int
diff --git a/gdb/filename-seen-cache.c b/gdb/filename-seen-cache.c
new file mode 100644
index 0000000..6282105
--- /dev/null
+++ b/gdb/filename-seen-cache.c
@@ -0,0 +1,66 @@
+/* Filename-seen cache for the GNU debugger, GDB.
+
+   Copyright (C) 1986-2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "filename-seen-cache.h"
+#include "filenames.h"
+
+  /* Initial size of the table.  It automagically grows from here.  */
+#define INITIAL_FILENAME_SEEN_CACHE_SIZE 100
+
+/* filename_seen_cache constructor.  */
+
+filename_seen_cache::filename_seen_cache ()
+{
+  m_tab = htab_create_alloc (INITIAL_FILENAME_SEEN_CACHE_SIZE,
+			     filename_hash, filename_eq,
+			     NULL, xcalloc, xfree);
+}
+
+/* See filename-seen-cache.h.  */
+
+void
+filename_seen_cache::clear ()
+{
+  htab_empty (m_tab);
+}
+
+/* See filename-seen-cache.h.  */
+
+filename_seen_cache::~filename_seen_cache ()
+{
+  htab_delete (m_tab);
+}
+
+/* See filename-seen-cache.h.  */
+
+bool
+filename_seen_cache::seen (const char *file)
+{
+  void **slot;
+
+  /* Is FILE in tab?  */
+  slot = htab_find_slot (m_tab, file, INSERT);
+  if (*slot != NULL)
+    return true;
+
+  /* No; add it to tab.  */
+  *slot = (char *) file;
+  return false;
+}
diff --git a/gdb/filename-seen-cache.h b/gdb/filename-seen-cache.h
new file mode 100644
index 0000000..175234f
--- /dev/null
+++ b/gdb/filename-seen-cache.h
@@ -0,0 +1,63 @@
+/* Filename-seen cache for the GNU debugger, GDB.
+
+   Copyright (C) 1986-2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "common/function-view.h"
+
+/* Cache to watch for file names already seen.  */
+
+class filename_seen_cache
+{
+public:
+  filename_seen_cache ();
+  ~filename_seen_cache ();
+
+  /* Disable copy.  */
+  filename_seen_cache (const filename_seen_cache &) = delete;
+  void operator= (const filename_seen_cache &) = delete;
+
+  /* Empty the cache, but do not delete it.  */
+  void clear ();
+
+  /* If FILE is not already in the table of files in CACHE, add it and
+     return false; otherwise return true.
+
+     NOTE: We don't manage space for FILE, we assume FILE lives as
+     long as the caller needs.  */
+  bool seen (const char *file);
+
+  /* Traverse all cache entries, calling CALLBACK on each.  The
+     filename is passed as argument to CALLBACK.  */
+  void traverse (gdb::function_view<void (const char *)> callback)
+  {
+    htab_traverse_noresize (m_tab,
+			    [] (void **slot, void *info) -> int
+			    {
+			      auto filename = (const char *) *slot;
+			      auto cb = (gdb::function_view<void (const char *)> *) info;
+			      (*cb) (filename);
+			      return 1;
+			    },
+			    &callback);
+  }
+
+private:
+  /* Table of files seen so far.  */
+  htab_t m_tab;
+};
diff --git a/gdb/symtab.c b/gdb/symtab.c
index c7f1311..c80f637 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -63,6 +63,7 @@
 #include "completer.h"
 #include "progspace-and-thread.h"
 #include "common/gdb_optional.h"
+#include "filename-seen-cache.h"
 
 /* Forward declarations for local functions.  */
 
@@ -3967,74 +3968,6 @@ operator_chars (const char *p, const char **end)
 }
 \f
 
-/* Cache to watch for file names already seen by filename_seen.  */
-
-struct filename_seen_cache
-{
-  /* Table of files seen so far.  */
-  htab_t tab;
-  /* Initial size of the table.  It automagically grows from here.  */
-#define INITIAL_FILENAME_SEEN_CACHE_SIZE 100
-};
-
-/* filename_seen_cache constructor.  */
-
-static struct filename_seen_cache *
-create_filename_seen_cache (void)
-{
-  struct filename_seen_cache *cache = XNEW (struct filename_seen_cache);
-
-  cache->tab = htab_create_alloc (INITIAL_FILENAME_SEEN_CACHE_SIZE,
-				  filename_hash, filename_eq,
-				  NULL, xcalloc, xfree);
-
-  return cache;
-}
-
-/* Empty the cache, but do not delete it.  */
-
-static void
-clear_filename_seen_cache (struct filename_seen_cache *cache)
-{
-  htab_empty (cache->tab);
-}
-
-/* filename_seen_cache destructor.
-   This takes a void * argument as it is generally used as a cleanup.  */
-
-static void
-delete_filename_seen_cache (void *ptr)
-{
-  struct filename_seen_cache *cache = (struct filename_seen_cache *) ptr;
-
-  htab_delete (cache->tab);
-  xfree (cache);
-}
-
-/* If FILE is not already in the table of files in CACHE, return zero;
-   otherwise return non-zero.  Optionally add FILE to the table if ADD
-   is non-zero.
-
-   NOTE: We don't manage space for FILE, we assume FILE lives as long
-   as the caller needs.  */
-
-static int
-filename_seen (struct filename_seen_cache *cache, const char *file, int add)
-{
-  void **slot;
-
-  /* Is FILE in tab?  */
-  slot = htab_find_slot (cache->tab, file, add ? INSERT : NO_INSERT);
-  if (*slot != NULL)
-    return 1;
-
-  /* No; maybe add it to tab.  */
-  if (add)
-    *slot = (char *) file;
-
-  return 0;
-}
-
 /* Data structure to maintain printing state for output_source_filename.  */
 
 struct output_source_filename_data
@@ -4064,7 +3997,7 @@ output_source_filename (const char *name,
      symtabs; it doesn't hurt to check.  */
 
   /* Was NAME already seen?  */
-  if (filename_seen (data->filename_seen_cache, name, 1))
+  if (data->filename_seen_cache->seen (name))
     {
       /* Yes; don't print it again.  */
       return;
@@ -4096,16 +4029,15 @@ sources_info (char *ignore, int from_tty)
   struct symtab *s;
   struct objfile *objfile;
   struct output_source_filename_data data;
-  struct cleanup *cleanups;
 
   if (!have_full_symbols () && !have_partial_symbols ())
     {
       error (_("No symbol table is loaded.  Use the \"file\" command."));
     }
 
-  data.filename_seen_cache = create_filename_seen_cache ();
-  cleanups = make_cleanup (delete_filename_seen_cache,
-			   data.filename_seen_cache);
+  filename_seen_cache filenames_seen;
+
+  data.filename_seen_cache = &filenames_seen;
 
   printf_filtered ("Source files for which symbols have been read in:\n\n");
 
@@ -4121,13 +4053,11 @@ sources_info (char *ignore, int from_tty)
   printf_filtered ("Source files for which symbols "
 		   "will be read in on demand:\n\n");
 
-  clear_filename_seen_cache (data.filename_seen_cache);
+  filenames_seen.clear ();
   data.first = 1;
   map_symbol_filenames (output_partial_symbol_filename, &data,
 			1 /*need_fullname*/);
   printf_filtered ("\n");
-
-  do_cleanups (cleanups);
 }
 
 /* Compare FILE against all the NFILES entries of FILES.  If BASENAMES is
@@ -5551,7 +5481,7 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
 
   if (not_interesting_fname (filename))
     return;
-  if (!filename_seen (data->filename_seen_cache, filename, 1)
+  if (!data->filename_seen_cache->seen (filename)
       && filename_ncmp (filename, data->text, data->text_len) == 0)
     {
       /* This file matches for a completion; add it to the
@@ -5563,7 +5493,7 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
       const char *base_name = lbasename (filename);
 
       if (base_name != filename
-	  && !filename_seen (data->filename_seen_cache, base_name, 1)
+	  && !data->filename_seen_cache->seen (base_name)
 	  && filename_ncmp (base_name, data->text, data->text_len) == 0)
 	add_filename_to_list (base_name, data->text, data->word, data->list);
     }
@@ -5584,23 +5514,20 @@ make_source_files_completion_list (const char *text, const char *word)
   VEC (char_ptr) *list = NULL;
   const char *base_name;
   struct add_partial_filename_data datum;
-  struct filename_seen_cache *filename_seen_cache;
-  struct cleanup *back_to, *cache_cleanup;
+  struct cleanup *back_to;
 
   if (!have_full_symbols () && !have_partial_symbols ())
     return list;
 
   back_to = make_cleanup (do_free_completion_list, &list);
 
-  filename_seen_cache = create_filename_seen_cache ();
-  cache_cleanup = make_cleanup (delete_filename_seen_cache,
-				filename_seen_cache);
+  filename_seen_cache filenames_seen;
 
   ALL_FILETABS (objfile, cu, s)
     {
       if (not_interesting_fname (s->filename))
 	continue;
-      if (!filename_seen (filename_seen_cache, s->filename, 1)
+      if (!filenames_seen.seen (s->filename)
 	  && filename_ncmp (s->filename, text, text_len) == 0)
 	{
 	  /* This file matches for a completion; add it to the current
@@ -5615,13 +5542,13 @@ make_source_files_completion_list (const char *text, const char *word)
 	     command do when they parse file names.  */
 	  base_name = lbasename (s->filename);
 	  if (base_name != s->filename
-	      && !filename_seen (filename_seen_cache, base_name, 1)
+	      && !filenames_seen.seen (base_name)
 	      && filename_ncmp (base_name, text, text_len) == 0)
 	    add_filename_to_list (base_name, text, word, &list);
 	}
     }
 
-  datum.filename_seen_cache = filename_seen_cache;
+  datum.filename_seen_cache = &filenames_seen;
   datum.text = text;
   datum.word = word;
   datum.text_len = text_len;
@@ -5629,7 +5556,6 @@ make_source_files_completion_list (const char *text, const char *word)
   map_symbol_filenames (maybe_add_partial_symtab_filename, &datum,
 			0 /*need_fullname*/);
 
-  do_cleanups (cache_cleanup);
   discard_cleanups (back_to);
 
   return list;
-- 
2.5.5

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

* Re: [PATCH 15/40] Rewrite/enhance explicit locations completer, parse left->right
  2017-06-02 12:23 ` [PATCH 15/40] Rewrite/enhance explicit locations completer, parse left->right Pedro Alves
@ 2017-07-14 20:55   ` Keith Seitz
  2017-07-17 19:24     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-14 20:55 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:

> So... this patch gets rid of the need for quoting.

Yippie! /me really dislikes all the quoting nonsense that users have to deal with

> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.base/completion.exp: Adjust expected output.
> 	* gdb.linespec/ls-errs.exp (do_test): Adjust expected output.

> diff --git a/gdb/completer.c b/gdb/completer.c
> index a1d3a43..0473d8c 100644
> --- a/gdb/completer.c
> +++ b/gdb/completer.c

> @@ -1189,6 +1452,22 @@ completion_tracker::completion_tracker ()
>  				      NULL, xcalloc, xfree);
>  }
>  
> +void
> +completion_tracker::discard_completions ()

Implicit or missing comment?

> @@ -1517,6 +1839,12 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
>      }
>  }
>  
> +void
> +completion_tracker::advance_custom_word_point_by (size_t len)

Implicit or missing comment?

> +{
> +  m_custom_word_point += len;
> +}
> +
>  /* Build a new C string that is a copy or LCD with the whitespace of
>     ORIG/ORIG_LEN preserved.
>  
> @@ -1720,14 +2055,20 @@ completion_result::reset_match_list ()
>  static char **
>  gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
>  {
> -  /* Completers must be called twice.  If rl_point (i.e., END) is at
> -     column 0, then readline skips the the handle_brkchars phase, and
> -     so we create a tracker now in that case too.  */
> -  delete current_completion.tracker;
> -  current_completion.tracker = new completion_tracker ();
> +  /* Completers that provide a custom word point in the
> +     handle_brkchars phase also compute their completions then.
> +     Completers that leave the completion word handling to readline
> +     must be called twice.  If rl_point (i.e., END) is at column 0,
> +     then readline skips the the handle_brkchars phase, and so we
                            ^^^^^^^
"the the"

> +     create a tracker now in that case too.  */
> +  if (end == 0 || !current_completion.tracker->use_custom_word_point ())
> +    {
> +      delete current_completion.tracker;
> +      current_completion.tracker = new completion_tracker ();
>  
> -  complete_line (*current_completion.tracker, text,
> -		 rl_line_buffer, rl_point);
> +      complete_line (*current_completion.tracker, text,
> +		     rl_line_buffer, rl_point);
> +    }
>  
>    completion_tracker &tracker = *current_completion.tracker;
>  
> diff --git a/gdb/location.c b/gdb/location.c
> index d711d7b..19d3232 100644
> --- a/gdb/location.c
> +++ b/gdb/location.c
> +static bool
> +is_cp_operator (const char *start, const char *comma)
> +{
> +  if (comma != NULL
> +      && (comma - start) >= CP_OPERATOR_LEN)
> +    {
> +      const char *p = comma;
> +
> +      while (p > start && isspace (p[-1]))
> +	p--;
> +      if (p - start >= CP_OPERATOR_LEN)
> +	{
> +	  p-= CP_OPERATOR_LEN;
          ^^^

Missing a space there.

> +	  if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
> +	      && (p == start
> +		  || !(isalnum (p[-1]) || p[-1] == '_')))
> +	    {


> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 91e8b90..7c7ff7f 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -4995,6 +4995,7 @@ add_symtab_completions (struct compunit_symtab *cust,
>  void
>  default_collect_symbol_completion_matches_break_on
>    (completion_tracker &tracker,
> +   complete_symbol_mode mode,
>     const char *text, const char *word,
>     const char *break_on, enum type_code code)
>  {
> @@ -5013,8 +5014,12 @@ default_collect_symbol_completion_matches_break_on
>    const char *sym_text;
>    /* Length of sym_text.  */
>    int sym_text_len;
> +  struct cleanup *cleanups;

`cleanups' is unused.

> diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
> index 6597ea7..f03bfc3 100644
> --- a/gdb/testsuite/gdb.base/completion.exp
> +++ b/gdb/testsuite/gdb.base/completion.exp
> @@ -790,7 +790,7 @@ gdb_test_multiple "" $test {
>      -re "break\.c.*break1\.c.*$gdb_prompt " {
>  	send_gdb "1\t\n"
>  	gdb_test_multiple "" $test {
> -	    -re ".*Function \"$srcfile2\" not defined\..*$gdb_prompt " {
> +	    -re "malformed linespec error: unexpected end of input\r\n$gdb_prompt " {
>  		pass $test
>  	    }
>  	    -re "$gdb_prompt p$" {

I don't understand this change, and it FAILs for me. In the log, it is still saying "Function \"break1.c\" not found". [NOTE: the completion is still "break break1.c ".]

I see this will eventually PASS (when the trailing ':' is output). Perhaps this hunk is in the wrong patch?

Keith

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

* Re: [PATCH 16/40] Explicit locations -label completer
  2017-06-02 12:29 ` [PATCH 16/40] Explicit locations -label completer Pedro Alves
@ 2017-07-14 21:32   ` Keith Seitz
  0 siblings, 0 replies; 183+ messages in thread
From: Keith Seitz @ 2017-07-14 21:32 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> We're missing a completer for
> 
>   (gdb) break -function func -label [TAB]
> 
> This patch adds one.  Tests will be added later in the series.

Nice! I didn't notice any issues.

Keith

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

* Re: [PATCH 17/40] Linespec lexing and C++ operators
  2017-06-02 12:29 ` [PATCH 17/40] Linespec lexing and C++ operators Pedro Alves
@ 2017-07-14 21:45   ` Keith Seitz
  2017-07-17 19:34     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-14 21:45 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> There's some lexing code in linespec that isn't handling C++ operators
> correctly.  It's the usual confusion with operator< / operator<<, in
> code that wants to skip past template parameters.
> 
> The linespec_lexer_lex_string change is necessary otherwise we get
> this (with current master):
> 
>  (gdb) break 'operator<'
>  unmatched quote
> 
> The need for the find_toplevel_char change was exposed by the use of
> that function in the explicit location completer.  Without the fix,
> that completer is not able to "see" past operator< symbols, without
> quoting, like:
> 
>  (gdb) b -function operator<(int, int) -labe[TAB]    # nothing happens
> 
> gdb incorrectly thinks "-labe" is part of the "unclosed" template
> parameter list started with "<".

Ouch. The best laid plans...

Just a few trivial things.

> diff --git a/gdb/linespec.c b/gdb/linespec.c
> index 0216bf1..f24cca2 100644
> --- a/gdb/linespec.c
> +++ b/gdb/linespec.c
> @@ -674,14 +674,49 @@ linespec_lexer_lex_string (linespec_parser *parser)
>  	  else if (*PARSER_STREAM (parser) == '<'
>  		   || *PARSER_STREAM (parser) == '(')
>  	    {
> -	      const char *p;
> +	      /* Don't interpret 'operator<' / 'operator<<' as a
> +		 template parameter list though.  */
> +	      if (*PARSER_STREAM (parser) == '<'
> +		  && (PARSER_STATE (parser)->language->la_language
> +		      == language_cplus)
> +		  && (PARSER_STREAM (parser) - start) >= CP_OPERATOR_LEN)
> +		{
> +		  const char *p = PARSER_STREAM (parser);
> +
> +		  while (p > start && isspace (p[-1]))
> +		    p--;
> +		  if (p - start >= CP_OPERATOR_LEN)
> +		    {
> +		      p-= CP_OPERATOR_LEN;

This is a cut-n-paste-o (that's a typo propagated via cut-n-paste). Missing a space before the operator.

> +		      if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
> +			  && (p == start
> +			      || !(isalnum (p[-1]) || p[-1] == '_')))
> +			{
> +			  /* This is an operator name.  Keep going.  */
> +			  ++(PARSER_STREAM (parser));
> +			  if (*PARSER_STREAM (parser) == '<')
> +			    ++(PARSER_STREAM (parser));
> +			  continue;
> +			}
> +		    }
> +		}
>  
> -	      p = find_parameter_list_end (PARSER_STREAM (parser));
> -	      if (p != NULL)
> +	      const char *p = find_parameter_list_end (PARSER_STREAM (parser));
> +	      PARSER_STREAM (parser) = p;
> +
> +	      /* Don't loop around to the normal \0 case above because
> +		 we don't want to misinterpret a potential keyword at
> +		 the end of the token when the string isn't
> +		 "()<>"-balanced.  This handles "b
> +		 function(thread<tab>" in completion mode.  */
> +	      if (*p == '\0')
>  		{
> -		  PARSER_STREAM (parser) = p;
> -		  continue;
> +		  LS_TOKEN_STOKEN (token).ptr = start;
> +		  LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;

Line length > 80?

> +		  return token;
>  		}
> +	      else
> +		continue;
>  	    }
>  	  /* Commas are terminators, but not if they are part of an
>  	     operator name.  */

Keith

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

* Re: [PATCH 18/40] A smarter linespec completer
  2017-06-02 12:23 ` [PATCH 18/40] A smarter linespec completer Pedro Alves
@ 2017-07-15  0:07   ` Keith Seitz
  2017-07-17 18:21     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-15  0:07 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

This is awesome. Just some minor nits.

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
>         * gdb.linespec/ls-errs.exp: Don't sent tab characters, now that
>         the completer works.

Typo "sent"

> diff --git a/gdb/completer.h b/gdb/completer.h
> index eab9c69..fbfe4d5 100644
> --- a/gdb/completer.h
> +++ b/gdb/completer.h
> @@ -255,6 +265,14 @@ private:
>       completable words.  */
>    int m_custom_word_point = 0;
>  
> +  /* If true, tell readline to skip appending a whitespace after the
> +     completion.  Automatically set if we have a unique completion
> +     that already has a space at the end.  Completer may also
> +     explicitly set this.  E.g., the linespec completer sets when when

Typos: "... completer sets [this] when *when* .."

> +     the completion ends with the ":" separator between filename and
> +     function name.  */
> +  bool m_suppress_append_ws = false;
> +
>    /* Our idea of lowest common denominator to hand over to readline.
>       See intro.  */
>    char *m_lowest_common_denominator = NULL;
> @@ -347,6 +365,11 @@ extern completer_handle_brkchars_ftype *
>  
>  /* Exported to linespec.c */
>  
> +extern completion_list complete_source_filenames (const char *text);
> +
> +extern void complete_expression (completion_tracker &tracker,
> +				 const char *text, const char *word);
> +
>  extern const char *skip_quoted_chars (const char *, const char *,
>  				      const char *);

Should the explanatory comments in completer.c be moved here?

> diff --git a/gdb/linespec.c b/gdb/linespec.c
> index f24cca2..c993c67 100644
> --- a/gdb/linespec.c
> +++ b/gdb/linespec.c
> @@ -271,6 +293,29 @@ struct ls_parser
>    /* The result of the parse.  */
>    struct linespec result;
>  #define PARSER_RESULT(PPTR) (&(PPTR)->result)
> +
> +  /* What the parser believes the current word point should complete
> +     to.  */
> +  linespec_complete_what complete_what;
> +
> +  /* The completion word point.  The parser advances this as is skips

Typo "as i[t] skips"

> +     tokens.  At some point the input string will end or parsing will
> +     fail, and then we attempt completion at the captured completion
> +     word point, interpreting the string at completion_word as
> +     COMPLETE_WHAT.  */
> +  const char *completion_word;
> +
> +  /* If the current token was a quoted string, then this is the
> +     quoting character (either " or '.).  */

Typo {either " or '.).} (extra period inside parenthesis)

> +  int completion_quote_char;

Why int?

> @@ -543,6 +588,30 @@ find_parameter_list_end (const char *input)
>    return p;
>  }
>  
> +/* If the [STRING, STRING_LEN) string ends with what looks like a
> +   keyword, return the keyword start offset in STRING.  Return -1
> +   otherwise.  */
> +
> +size_t
> +string_find_incomplete_keyword_at_end (const char * const *keywords,
> +				       const char *string, size_t string_len)

Should this be static?

> +  else if (component == linespec_complete_what::FUNCTION)
> +    {
> +      completion_list fn_list;
> +
> +      linespec_complete_function (tracker, text, source_filename);
> +      if (source_filename == NULL)
> +	fn_list = complete_source_filenames (text);
> +

Maybe I took the description of FUNCTION too literally, but it was not obvious to me why we search for source files here.

When parse_linespec returns (after, e.g., "break foo"), PARSER_EXPLICIT->function_name == "foo", but here we search for both functions *and* source files starting with "foo". Given the ambiguity in the grammar, this is a necessary evil. The meaning of FUNCTION is overloaded (it is not just "functions/methods").

Can a comment be added to clarify this (probably at linespec_complete_what::FUNCTION)?

> +  else if (parser.complete_what == linespec_complete_what::FUNCTION)
> +    {
> +      /* While parsing/lexing, we didn't know whether the completion
> +	 word completes to a unique function name already or not.  E.g.:

Similarly here, I would like to see "unique function or source file name". A casual reader may really easily overlook this.

> +	   "b function() <tab>"
> +	 may need to complete either to:
> +	   "b function() const"
> +	 or to:
> +	   "b function() if/thread/task"
> +
> +	 Or, this:
> +	   "b foo t"
> +	 may need to complete either to:
> +	   "b foo template_fun<T>()"
> +	 with "foo" being the template function's return type, or to:
> +	   "b foo thread/task"
> +
> +	 Address that by completing assuming function, and seeing if
> +	 we find a function completion that matches exactly
> +	 "function()". If so, then we advance the completion word past
> +	 the function and switch to KEYWORD completion mode.  */
> +
> +      const char *text = parser.completion_word;
> +      const char *word = parser.completion_word;
> +
> +      complete_linespec_component (&parser, tracker,
> +				   parser.completion_word,
> +				   linespec_complete_what::FUNCTION,
> +				   PARSER_EXPLICIT (&parser)->source_filename);
> +
> +      parser.complete_what = linespec_complete_what::NOTHING;
> +
> +      if (tracker.quote_char ())
> +	{
> +	  /* The function/file name was not close-quoted, so this
> +	     can't be a keyword.  */

quote_char could be ':' (from complete_linespec_component). Please add a comment reminding the reader. [Well, that could easily be me, and I'm gettin' old!]

Keith

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

* Re: [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache)
  2017-07-14 19:40     ` Pedro Alves
@ 2017-07-17 10:51       ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 10:51 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/14/2017 08:40 PM, Pedro Alves wrote:

> I was about to push it when I realized that I missed releasing the
> new heap-allocated dwarf2_per_objfile::filenames_cache...
> 
> To address that, I made it possible for dwarf2_per_objfile to be
> a non-POD:
> 
>   https://sourceware.org/ml/gdb-patches/2017-07/msg00202.html
> 
> and made dwarf2_per_objfile::filenames_cache an optional, so the
> cache object (though not its elements) is on the obstack too.
> 
> Here's the updated patch.

The dwarf2_per_objfile patch is in, so I pushed this one in too,
with a tiny comment update to mention the reason for the optional:

  /* Table containing all filenames.  This is an optional because the
     table is lazily constructed on first access.  */
  gdb::optional<filename_seen_cache> filenames_cache;

Thanks much for the review, Keith.

-- 
Pedro Alves

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

* Re: [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer
  2017-07-13 20:46   ` Keith Seitz
@ 2017-07-17 11:00     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 11:00 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches, Yao Qi

On 07/13/2017 09:46 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> "make_symbol_completion_list_fn" is odly named when you look at a list
>> of "standard" completers, like the Python/Guild completer lists
>> adjusted by this patch.  Rename / move it to completers.h/c, for
>> consistency.
> 
> Oh, man, I totally agree. make_symbol_completion_list_fn sounds more like a typedef name!
> 
> And who could possibly complain about shortening symtab.[ch]. Those files are already a little bloated with non-symtab stuff.
> 
> I see no issues with this patch.

Thanks guys.  I pushed this one in.

-- 
Pedro Alves

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

* Re: [PATCH 10/40] Clean up "completer_handle_brkchars" callback handling
  2017-07-13 21:08   ` Keith Seitz
@ 2017-07-17 11:14     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 11:14 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/13/2017 10:08 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> This patch cleans up "completer_handle_brkchars" callback handling:
>>
>> - Renames the function typedef to better match its intent:
>>   completer_ftype_void ->  completer_handle_brkchars_ftype
>>
>> - Factors out common code in complete_line_internal handling the
>>   "handle_brkchars" callback to a separate function.
>>
>> - Centralizes all the "completer method" to "handle_brkchars method"
>>   mapping in a single function.
>>
> 
> I don't see any issues.

Thanks much Keith.  I pushed it in.

-- 
Pedro Alves

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

* Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction
  2017-07-14 17:23   ` Keith Seitz
@ 2017-07-17 13:56     ` Pedro Alves
  2017-07-18  8:23       ` Christophe Lyon
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 13:56 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/14/2017 06:23 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
>> Adds a new "completion_tracker" class that is meant to hold everything
>> about the state of the current completion operation.
> 
> This is quite close to the approach I attempted in the series I submitted (cough) in 2015.

Yeah, I'm sorry about that.  As you know, I wasn't involved
in that review back then, and I had assumed master already contained
all the patches you had had back then...  After you pointed me at them,
I considered rebasing on top of them.   But since your patches (naturally)
were still using VEC (since they predated C++), we'd end touching/redoing
the same exact same code throughout twice, to work with the
completion_tracker_t methods.  :-/

> 
> Just have a few minor comments (about comments!).
> 
>> diff --git a/gdb/completer.c b/gdb/completer.c
>> index fe69faa..c6e1e28 100644
>> --- a/gdb/completer.c
>> +++ b/gdb/completer.c
>> @@ -429,10 +420,10 @@ backup_text_ptr (const char *p, const char *text)
>>  /* A completer function for explicit locations.  This function
>>     completes both options ("-source", "-line", etc) and values.  */
>>  
>> -static VEC (char_ptr) *
>> -explicit_location_completer (struct cmd_list_element *ignore,
>> -			     struct event_location *location,
>> -			     const char *text, const char *word)
>> +static void
>> +complete_explicit_location (completion_tracker &tracker,
>> +			    struct event_location *location,
>> +			    const char *text, const char *word)
>>  {
>>    const char *p;
>>    VEC (char_ptr) *matches = NULL;
> 
> `matches' is no longer used. [I realize this will disappear in a later patch.]
> 
>>  /* See completer.h.  */
>>  
>> -enum maybe_add_completion_enum
>> -maybe_add_completion (completion_tracker_t tracker, char *name)
>> +bool
>> +completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
>>  {
> [snip]
> 
>>  
>> -  return (htab_elements (tracker) < max_completions
>> -	  ? MAYBE_ADD_COMPLETION_OK
>> -	  : MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
>> +void
>> +completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
>> +{
>> +  if (!maybe_add_completion (std::move (name)))
>> +    throw_max_completions_reached_error ();
>>  }
>>  
>>  void
>> @@ -1075,10 +1075,16 @@ throw_max_completions_reached_error (void)
>>    throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
>>  }
>>  
>> -/* Generate completions all at once.  Returns a vector of unique strings
>> -   allocated with xmalloc.  Returns NULL if there are no completions
>> -   or if max_completions is 0.  If max_completions is non-negative, this will
>> -   return at most max_completions strings.
>> +void
>> +completion_tracker::add_completions (completion_list &&list)
>> +{
>> +  for (auto &candidate : list)
>> +    add_completion (std::move (candidate));
>> +}
> 
> Some of the above methods have comments (per convention, "See XYZ.h"), some do not.  [There are a bunch more methods with no comments below this, too.]
> 
> Is this requirement being relaxed for C++? I certainly wouldn't mind if we started assuming "See XYZ.h" for all methods, but I don't think convention has been discussed/codified yet.

Indeed, I don't think it's been discussed.  For now, I added the
comments, but I wouldn't mind relaxing either.

> 
>> +
>> +/* Build a new C string that is a copy or LCD with the whitespace of
>> +   ORIG/ORIG_LEN preserved.
> 
> Is this supposed to be "a copy *of* LCD"?

Indeed.  an "inline line" in "we want to end up with an input line
like" was supposed to be "input line".  Gah.

> 
>> +
>> +/* Helper for gdb_rl_attempted_completion_function, which does most of
>> +   the work.  This is called by readline to build the match list
>> +   array, and determining the lowest common denominator.  The real
> 
> This last sentence isn't right. At its simplest, it says, "This is called, and determining the lowest common denominator." Is that supposed to be, "This is called to build.. and to determine"?

Yup, thanks.

> 
>> diff --git a/gdb/symtab.c b/gdb/symtab.c
>> index 09c9411b..cd78a16 100644
>> --- a/gdb/symtab.c
>> +++ b/gdb/symtab.c
>>  
>>  /* Return a vector of all symbols (regardless of class) which begin by
>>     matching TEXT.  If the answer is no symbols, then the return value
>>     is NULL.  */
> 
> This comment needs updating ("Return a vector...").

Did that now.

> 
>>  
>> -VEC (char_ptr) *
>> -make_symbol_completion_list (const char *text, const char *word)
>> +void
>> +collect_symbol_completion_matches (completion_tracker &tracker,
>> +				   const char *text, const char *word)
>>  {
>> -  return current_language->la_make_symbol_completion_list (text, word,
>> -							   TYPE_CODE_UNDEF);
>> +  current_language->la_collect_symbol_completion_matches (tracker,
>> +							  text, word,
>> +							  TYPE_CODE_UNDEF);
>>  }
>>  
>> -/* Like make_symbol_completion_list, but only return STRUCT_DOMAIN
>> -   symbols whose type code is CODE.  */
>> +/* Like collect_symbol_completion_matches, but only return
>> +   STRUCT_DOMAIN symbols whose type code is CODE.  */
>>  
> 
> s/return/collect/ ?

Fixed.

> 
>> -VEC (char_ptr) *
>> -make_symbol_completion_type (const char *text, const char *word,
>> -			     enum type_code code)
>> +void
>> +collect_symbol_completion_matches_type (completion_tracker &tracker,
>> +					const char *text, const char *word,
>> +					enum type_code code)
>>  {
>>    gdb_assert (code == TYPE_CODE_UNION
>>  	      || code == TYPE_CODE_STRUCT
>>  	      || code == TYPE_CODE_ENUM);
>> -  return current_language->la_make_symbol_completion_list (text, word, code);
>> +  current_language->la_collect_symbol_completion_matches (tracker,
>> +							  text, word, code);
>>  }
>>  
>> @@ -5503,17 +5419,16 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
>>  
>>  /* Return a vector of all source files whose names begin with matching
>>     TEXT.  The file names are looked up in the symbol tables of this
>> -   program.  If the answer is no matchess, then the return value is
>> -   NULL.  */
>> +   program.  */
> 
> This comment also needs updating ("Return a vector...").
> 

Thanks much Keith.  I pushed the patch with the below squashed in.

(While adding the missing comments I noticed that
throw_max_completions_reached_error could be static, and then
since there's only one caller, I inlined it.)

Thanks,
Pedro Alves

diff --git i/gdb/completer.c w/gdb/completer.c
index c6e1e28..85e6d88 100644
--- i/gdb/completer.c
+++ w/gdb/completer.c
@@ -426,7 +426,6 @@ complete_explicit_location (completion_tracker &tracker,
 			    const char *text, const char *word)
 {
   const char *p;
-  VEC (char_ptr) *matches = NULL;
 
   /* Find the beginning of the word.  This is necessary because
      we need to know if we are completing an option name or value.  We
@@ -1029,6 +1028,8 @@ completion_tracker::completion_tracker ()
 				      NULL, xcalloc, xfree);
 }
 
+/* See completer.h.  */
+
 completion_tracker::~completion_tracker ()
 {
   xfree (m_lowest_common_denominator);
@@ -1062,18 +1063,16 @@ completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
   return true;
 }
 
+/* See completer.h.  */
+
 void
 completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
 {
   if (!maybe_add_completion (std::move (name)))
-    throw_max_completions_reached_error ();
+    throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
 }
 
-void
-throw_max_completions_reached_error (void)
-{
-  throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
-}
+/* See completer.h.  */
 
 void
 completion_tracker::add_completions (completion_list &&list)
@@ -1337,7 +1336,7 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
     }
 }
 
-/* Build a new C string that is a copy or LCD with the whitespace of
+/* Build a new C string that is a copy of LCD with the whitespace of
    ORIG/ORIG_LEN preserved.
 
    Say the user is completing a symbol name, with spaces, like:
@@ -1348,7 +1347,7 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
 
      "foo(int)"
 
-   we want to end up with an inline line like:
+   we want to end up with an input line like:
 
      "foo ( int)"
       ^^^^^^^      => text from LCD [1], whitespace from ORIG preserved.
@@ -1402,6 +1401,8 @@ expand_preserving_ws (const char *orig, size_t orig_len,
   return xstrdup (res.c_str ());
 }
 
+/* See completer.h.  */
+
 completion_result
 completion_tracker::build_completion_result (const char *text,
 					     int start, int end)
@@ -1443,11 +1444,15 @@ completion_tracker::build_completion_result (const char *text,
     }
 }
 
+/* See completer.h  */
+
 completion_result::completion_result ()
   : match_list (NULL), number_matches (0),
     completion_suppress_append (false)
 {}
 
+/* See completer.h  */
+
 completion_result::completion_result (char **match_list_,
 				      size_t number_matches_,
 				      bool completion_suppress_append_)
@@ -1456,11 +1461,15 @@ completion_result::completion_result (char **match_list_,
     completion_suppress_append (completion_suppress_append_)
 {}
 
+/* See completer.h  */
+
 completion_result::~completion_result ()
 {
   reset_match_list ();
 }
 
+/* See completer.h  */
+
 completion_result::completion_result (completion_result &&rhs)
 {
   if (this == &rhs)
@@ -1473,6 +1482,8 @@ completion_result::completion_result (completion_result &&rhs)
   rhs.number_matches = 0;
 }
 
+/* See completer.h  */
+
 char **
 completion_result::release_match_list ()
 {
@@ -1489,6 +1500,8 @@ compare_cstrings (const char *str1, const char *str2)
   return strcmp (str1, str2) < 0;
 }
 
+/* See completer.h  */
+
 void
 completion_result::sort_match_list ()
 {
@@ -1502,6 +1515,8 @@ completion_result::sort_match_list ()
     }
 }
 
+/* See completer.h  */
+
 void
 completion_result::reset_match_list ()
 {
@@ -1515,9 +1530,9 @@ completion_result::reset_match_list ()
 }
 
 /* Helper for gdb_rl_attempted_completion_function, which does most of
-   the work.  This is called by readline to build the match list
-   array, and determining the lowest common denominator.  The real
-   matches list starts at match[1], while match[0] is the slot holding
+   the work.  This is called by readline to build the match list array
+   and to determine the lowest common denominator.  The real matches
+   list starts at match[1], while match[0] is the slot holding
    readline's idea of the lowest common denominator of all matches,
    which is what readline replaces the completion "word" with.
 
diff --git i/gdb/completer.h w/gdb/completer.h
index e554bff..4b3b188 100644
--- i/gdb/completer.h
+++ w/gdb/completer.h
@@ -274,9 +274,4 @@ extern const char *skip_quoted (const char *);
 
 extern int max_completions;
 
-
-/* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR.  */ 
-
-extern void throw_max_completions_reached_error (void);
-
 #endif /* defined (COMPLETER_H) */
diff --git i/gdb/symtab.c w/gdb/symtab.c
index b70a818..57fb355 100644
--- i/gdb/symtab.c
+++ w/gdb/symtab.c
@@ -5217,9 +5217,8 @@ default_collect_symbol_completion_matches (completion_tracker &tracker,
 							     code);
 }
 
-/* Return a vector of all symbols (regardless of class) which begin by
-   matching TEXT.  If the answer is no symbols, then the return value
-   is NULL.  */
+/* Collect all symbols (regardless of class) which begin by matching
+   TEXT.  */
 
 void
 collect_symbol_completion_matches (completion_tracker &tracker,
@@ -5230,7 +5229,7 @@ collect_symbol_completion_matches (completion_tracker &tracker,
 							  TYPE_CODE_UNDEF);
 }
 
-/* Like collect_symbol_completion_matches, but only return
+/* Like collect_symbol_completion_matches, but only collect
    STRUCT_DOMAIN symbols whose type code is CODE.  */
 
 void
@@ -5406,7 +5405,7 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
     }
 }
 
-/* Return a vector of all source files whose names begin with matching
+/* Return a list of all source files whose names begin with matching
    TEXT.  The file names are looked up in the symbol tables of this
    program.  */
 

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

* Re: [PATCH 12/40] "complete" command and completion word break characters
  2017-07-14 17:50   ` Keith Seitz
@ 2017-07-17 14:36     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 14:36 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/14/2017 06:49 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> First, the complete command has a too-simple approximation of what
>> readline's TAB-completion code does to find the completion word point.
>> Unfortunately, readline doesn't expose the functionality it uses
>> internally, so to fix this this patch copies over the relevant code,
>> and adjusts it a bit to better fit the use cases we need it for.
>> (Specifically, our version avoids relying on the
>> rl_word_break_characters, etc. globals, and instead takes those as
>> arguments.)
> 
> re: "copies over the relevant code"
> Is it possible to mention this in/around the copied code? That might make it easier to track differences in the future, e.g., if someone found a problem with the copied code, he could look upstream for a fix (or report a bug).

Indeed.  See delta diff below.

> 
>>
>> diff --git a/gdb/completer.h b/gdb/completer.h
>> index e554bff..207781d 100644
>> --- a/gdb/completer.h
>> +++ b/gdb/completer.h
>> @@ -203,6 +203,10 @@ extern void complete_line (completion_tracker &tracker,
>>  			   const char *line_buffer,
>>  			   int point);
>>  
>> +extern const char *completion_find_completion_word (completion_tracker &tracker,
>> +						    const char *text,
>> +						    int *quote_char);
>> +
> 
> Missing comment?

Good catch.  I've pushed in the patch with the below squashed in.
There was another comment that was not relevant for gdb's copy.

diff --git i/gdb/completer.c w/gdb/completer.c
index 3f25261..196610d 100644
--- i/gdb/completer.c
+++ w/gdb/completer.c
@@ -220,20 +220,19 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
 #define RL_QF_BACKSLASH         0x04
 #define RL_QF_OTHER_QUOTE       0x08
 
-/* Find the bounds of the current word for completion purposes, and leave
-   rl_point set to the end of the word.  This function skips quoted
-   substrings (characters between matched pairs of characters in
-   rl_completer_quote_characters).  First we try to find an unclosed
-   quoted substring on which to do matching.  If one is not found, we use
-   the word break characters to find the boundaries of the current word.
-   We call an application-specific function to decide whether or not a
-   particular word break character is quoted; if that function returns a
-   non-zero result, the character does not break a word.  This function
-   returns the opening quote character if we found an unclosed quoted
-   substring, '\0' otherwise.  FP, if non-null, is set to a value saying
-   which (shell-like) quote characters we found (single quote, double
-   quote, or backslash) anywhere in the string.  DP, if non-null, is set to
-   the value of the delimiter character that caused a word break. */
+/* Find the bounds of the current word for completion purposes, and
+   return a pointer to the end of the word.  This mimics (and is a
+   modified version of) readline's _rl_find_completion_word internal
+   function.
+
+   This function skips quoted substrings (characters between matched
+   pairs of characters in rl_completer_quote_characters).  We try to
+   find an unclosed quoted substring on which to do matching.  If one
+   is not found, we use the word break characters to find the
+   boundaries of the current word.  QC, if non-null, is set to the
+   opening quote character if we found an unclosed quoted substring,
+   '\0' otherwise.  DP, if non-null, is set to the value of the
+   delimiter character that caused a word break.  */
 
 struct gdb_rl_completion_word_info
 {
@@ -342,11 +341,6 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
   /* If we are at an unquoted word break, then advance past it.  */
   scan = line_buffer[point];
 
-  /* If there is an application-specific function to say whether or
-     not a character is quoted and we found a quote character, let
-     that function decide whether or not a character is a word break,
-     even if it is found in rl_completer_word_break_characters.  Don't
-     bother if we're at the end of the line, though.  */
   if (scan)
     {
       isbrk = strchr (brkchars, scan) != 0;
@@ -1464,8 +1458,7 @@ gdb_completion_word_break_characters ()
   return NULL;
 }
 
-/* Get the list of chars that are considered as word breaks
-   for the current command.  */
+/* See completer.h.  */
 
 const char *
 completion_find_completion_word (completion_tracker &tracker, const char *text,
diff --git i/gdb/completer.h w/gdb/completer.h
index 1f375f8..cf93cf0 100644
--- i/gdb/completer.h
+++ w/gdb/completer.h
@@ -203,6 +203,12 @@ extern void complete_line (completion_tracker &tracker,
 			   const char *line_buffer,
 			   int point);
 
+/* Find the bounds of the word in TEXT for completion purposes, and
+   return a pointer to the end of the word.  Calls the completion
+   machinery for a handle_brkchars phase (using TRACKER) to figure out
+   the right work break characters for the command in TEXT.
+   QUOTE_CHAR, if non-null, is set to the opening quote character if
+   we found an unclosed quoted substring, '\0' otherwise.  */
 extern const char *completion_find_completion_word (completion_tracker &tracker,
 						    const char *text,
 						    int *quote_char);

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

* Re: [PATCH 14/40] Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout
  2017-07-14 18:04   ` Keith Seitz
@ 2017-07-17 14:55     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 14:55 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/14/2017 07:04 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> I ran into LENGTH_OF_OPERATOR in cp-support.c and wanted to use it
>> elsewhere, so I moved it to cp-support.h.  Since there's already
>> CP_ANONYMOUS_NAMESPACE_STR/CP_ANONYMOUS_NAMESPACE_LEN there, I
>> followed the same naming pattern for the new symbols.
> 
> Looks pretty straightforward (even?) to me, but I do notice that there are still one or two places that could be updated:
> 
> c-exp.y:2281:    {"operator", OPERATOR, OP_NULL, FLAG_CXX},
> cp-name-parser.y:1839:      if (strncmp (tokstart, "operator", 8) == 0)

I had left these two because looking at the context around them, I'm
thinking that using the macro would obfuscate more than help.  c-exp.y has:

...
    {"new", NEW, OP_NULL, FLAG_CXX},
    {"delete", DELETE, OP_NULL, FLAG_CXX},
    {"operator", OPERATOR, OP_NULL, FLAG_CXX},

    {"and", ANDAND, BINOP_END, FLAG_CXX},
    {"and_eq", ASSIGN_MODIFY, BINOP_BITWISE_AND, FLAG_CXX},
    {"bitand", '&', OP_NULL, FLAG_CXX},
...

And cp-name-parser.y has:

...
    case 8:
      HANDLE_SPECIAL ("typeinfo for ", DEMANGLE_COMPONENT_TYPEINFO);
      HANDLE_SPECIAL ("typeinfo fn for ", DEMANGLE_COMPONENT_TYPEINFO_FN);
      HANDLE_SPECIAL ("typeinfo name for ", DEMANGLE_COMPONENT_TYPEINFO_NAME);
      if (strncmp (tokstart, "operator", 8) == 0)
	return OPERATOR;
      if (strncmp (tokstart, "restrict", 8) == 0)
	return RESTRICT;
      if (strncmp (tokstart, "unsigned", 8) == 0)
	return UNSIGNED;
      if (strncmp (tokstart, "template", 8) == 0)
	return TEMPLATE;
...

Note all those "8"s match the size in the switch case.  (Likewise
the other switch cases.)

Given that nowadays compilers optimize strlen("string literal") to a
constant, this cp-name-parser.y case might be tweaked as:

      if (startswith (tokstart, "operator"))
	return OPERATOR;
      if (startswith (tokstart, "restrict"))
	return RESTRICT;
      if (startswith (tokstart, "unsigned"))
	return UNSIGNED;
      if (startswith (tokstart, "template"))
	return TEMPLATE;

etc.

So for now, I've pushed the patch as it was.

Thanks,
Pedro Alves

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

* Re: [PATCH 18/40] A smarter linespec completer
  2017-07-15  0:07   ` Keith Seitz
@ 2017-07-17 18:21     ` Pedro Alves
  2017-07-17 19:02       ` Keith Seitz
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 18:21 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

Hi Keith!

On 07/15/2017 01:07 AM, Keith Seitz wrote:
> This is awesome. Just some minor nits.
> 
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> gdb/testsuite/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>>         * gdb.linespec/ls-errs.exp: Don't sent tab characters, now that
>>         the completer works.
> 
> Typo "sent"

Fixed.

> 
>> diff --git a/gdb/completer.h b/gdb/completer.h
>> index eab9c69..fbfe4d5 100644
>> --- a/gdb/completer.h
>> +++ b/gdb/completer.h
>> @@ -255,6 +265,14 @@ private:
>>       completable words.  */
>>    int m_custom_word_point = 0;
>>  
>> +  /* If true, tell readline to skip appending a whitespace after the
>> +     completion.  Automatically set if we have a unique completion
>> +     that already has a space at the end.  Completer may also
>> +     explicitly set this.  E.g., the linespec completer sets when when
> 
> Typos: "... completer sets [this] when *when* .."

Thanks, fixed.

> 
>> +     the completion ends with the ":" separator between filename and
>> +     function name.  */
>> +  bool m_suppress_append_ws = false;
>> +
>>    /* Our idea of lowest common denominator to hand over to readline.
>>       See intro.  */
>>    char *m_lowest_common_denominator = NULL;
>> @@ -347,6 +365,11 @@ extern completer_handle_brkchars_ftype *
>>  
>>  /* Exported to linespec.c */
>>  
>> +extern completion_list complete_source_filenames (const char *text);
>> +
>> +extern void complete_expression (completion_tracker &tracker,
>> +				 const char *text, const char *word);
>> +
>>  extern const char *skip_quoted_chars (const char *, const char *,
>>  				      const char *);
> 
> Should the explanatory comments in completer.c be moved here?

Moved.

> 
>> diff --git a/gdb/linespec.c b/gdb/linespec.c
>> index f24cca2..c993c67 100644
>> --- a/gdb/linespec.c
>> +++ b/gdb/linespec.c
>> @@ -271,6 +293,29 @@ struct ls_parser
>>    /* The result of the parse.  */
>>    struct linespec result;
>>  #define PARSER_RESULT(PPTR) (&(PPTR)->result)
>> +
>> +  /* What the parser believes the current word point should complete
>> +     to.  */
>> +  linespec_complete_what complete_what;
>> +
>> +  /* The completion word point.  The parser advances this as is skips
> 
> Typo "as i[t] skips"

Fixed.

> 
>> +     tokens.  At some point the input string will end or parsing will
>> +     fail, and then we attempt completion at the captured completion
>> +     word point, interpreting the string at completion_word as
>> +     COMPLETE_WHAT.  */
>> +  const char *completion_word;
>> +
>> +  /* If the current token was a quoted string, then this is the
>> +     quoting character (either " or '.).  */
> 
> Typo {either " or '.).} (extra period inside parenthesis)

Fixed.

> 
>> +  int completion_quote_char;
> 
> Why int?

Mainly because the related readline code uses int too.
E.g., gdb_rl_find_completion_word -> completion_find_completion_word,
and then the tracker uses int for the quote char too.
I suspect that the reason readline uses int it to avoid issues
with having to remember to cast a char to unsigned char when calling
functions like isdigit, etc. everywhere.  We have some functions
in GDB that take a quote char and use int too:

extern void generic_emit_char (int c, struct type *type, struct ui_file *stream,
			       int quoter, const char *encoding);

extern void generic_printstr (struct ui_file *stream, struct type *type, 
			      const gdb_byte *string, unsigned int length, 
			      const char *encoding, int force_ellipses,
			      int quote_char, int c_style_terminator,
			      const struct value_print_options *options);

might be related, not sure.

I can make this a char, and for consistency try to make completer_tracker
use char too, though I'd prefer to do that as a patch on top of this one
that touches every path in a single go, than update the few related
completer_tracker patches, mainly because gdb_rl_find_completion_word
is already in master.  Would you mind that?

> 
>> @@ -543,6 +588,30 @@ find_parameter_list_end (const char *input)
>>    return p;
>>  }
>>  
>> +/* If the [STRING, STRING_LEN) string ends with what looks like a
>> +   keyword, return the keyword start offset in STRING.  Return -1
>> +   otherwise.  */
>> +
>> +size_t
>> +string_find_incomplete_keyword_at_end (const char * const *keywords,
>> +				       const char *string, size_t string_len)
> 
> Should this be static?

Indeed.

> 
>> +  else if (component == linespec_complete_what::FUNCTION)
>> +    {
>> +      completion_list fn_list;
>> +
>> +      linespec_complete_function (tracker, text, source_filename);
>> +      if (source_filename == NULL)
>> +	fn_list = complete_source_filenames (text);
>> +
> 
> Maybe I took the description of FUNCTION too literally, but it was not obvious to me why we search for source files here.
> 
> When parse_linespec returns (after, e.g., "break foo"), PARSER_EXPLICIT->function_name == "foo", but here we search for both functions *and* source files starting with "foo". Given the ambiguity in the grammar, this is a necessary evil. The meaning of FUNCTION is overloaded (it is not just "functions/methods").
> 
> Can a comment be added to clarify this (probably at linespec_complete_what::FUNCTION)?

Sure thing.

> 
>> +  else if (parser.complete_what == linespec_complete_what::FUNCTION)
>> +    {
>> +      /* While parsing/lexing, we didn't know whether the completion
>> +	 word completes to a unique function name already or not.  E.g.:
> 
> Similarly here, I would like to see "unique function or source file name". A casual reader may really easily overlook this.
> 
>> +	   "b function() <tab>"
>> +	 may need to complete either to:
>> +	   "b function() const"
>> +	 or to:
>> +	   "b function() if/thread/task"
>> +
>> +	 Or, this:
>> +	   "b foo t"
>> +	 may need to complete either to:
>> +	   "b foo template_fun<T>()"
>> +	 with "foo" being the template function's return type, or to:
>> +	   "b foo thread/task"
>> +
>> +	 Address that by completing assuming function, and seeing if
>> +	 we find a function completion that matches exactly
>> +	 "function()". If so, then we advance the completion word past
>> +	 the function and switch to KEYWORD completion mode.  */
>> +
>> +      const char *text = parser.completion_word;
>> +      const char *word = parser.completion_word;
>> +
>> +      complete_linespec_component (&parser, tracker,
>> +				   parser.completion_word,
>> +				   linespec_complete_what::FUNCTION,
>> +				   PARSER_EXPLICIT (&parser)->source_filename);
>> +
>> +      parser.complete_what = linespec_complete_what::NOTHING;
>> +
>> +      if (tracker.quote_char ())
>> +	{
>> +	  /* The function/file name was not close-quoted, so this
>> +	     can't be a keyword.  */
> 
> quote_char could be ':' (from complete_linespec_component). Please add a comment reminding the reader. [Well, that could easily be me, and I'm gettin' old!]

Here are the comments that I came up with.  WDYT?

From ac03503acbb0796899d51b42c6ac7d9560794bd9 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 17 Jul 2017 18:45:57 +0100
Subject: [PATCH] address review

---
 gdb/completer.c |  7 ++-----
 gdb/completer.h |  9 +++++++--
 gdb/linespec.c  | 55 ++++++++++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 53 insertions(+), 18 deletions(-)

diff --git a/gdb/completer.c b/gdb/completer.c
index b23baed..eb80a37 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -555,8 +555,7 @@ complete_files_symbols (completion_tracker &tracker,
     }
 }
 
-/* Return a list of all source files whose names begin with matching
-   TEXT.  */
+/* See completer.h.  */
 
 completion_list
 complete_source_filenames (const char *text)
@@ -1000,9 +999,7 @@ add_struct_fields (struct type *type, completion_list &output,
     }
 }
 
-/* Complete on expressions.  Often this means completing on symbol
-   names, but some language parsers also have support for completing
-   field names.  */
+/* See completer.h.  */
 
 void
 complete_expression (completion_tracker &tracker,
diff --git a/gdb/completer.h b/gdb/completer.h
index ba66137..f68c6dc 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -267,8 +267,8 @@ private:
 
   /* If true, tell readline to skip appending a whitespace after the
      completion.  Automatically set if we have a unique completion
-     that already has a space at the end.  Completer may also
-     explicitly set this.  E.g., the linespec completer sets when when
+     that already has a space at the end.  A completer may also
+     explicitly set this.  E.g., the linespec completer sets this when
      the completion ends with the ":" separator between filename and
      function name.  */
   bool m_suppress_append_ws = false;
@@ -371,8 +371,13 @@ extern completer_handle_brkchars_ftype *
 
 /* Exported to linespec.c */
 
+/* Return a list of all source files whose names begin with matching
+   TEXT.  */
 extern completion_list complete_source_filenames (const char *text);
 
+/* Complete on expressions.  Often this means completing on symbol
+   names, but some language parsers also have support for completing
+   field names.  */
 extern void complete_expression (completion_tracker &tracker,
 				 const char *text, const char *word);
 
diff --git a/gdb/linespec.c b/gdb/linespec.c
index f2da551..136cb65 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -54,7 +54,14 @@ enum class linespec_complete_what
   /* Nothing, no possible completion.  */
   NOTHING,
 
-  /* A function/method name.  */
+  /* A function/method name.  Due to ambiguity between
+
+       (gdb) b source[TAB]
+       source_file.c
+       source_function
+
+     this can also indicate a source filename, iff we haven't seen a
+     separate source filename component, as in "b source.c:function".  */
   FUNCTION,
 
   /* A label symbol.  E.g., break file.c:function:LABEL.  */
@@ -298,7 +305,7 @@ struct ls_parser
      to.  */
   linespec_complete_what complete_what;
 
-  /* The completion word point.  The parser advances this as is skips
+  /* The completion word point.  The parser advances this as it skips
      tokens.  At some point the input string will end or parsing will
      fail, and then we attempt completion at the captured completion
      word point, interpreting the string at completion_word as
@@ -306,7 +313,7 @@ struct ls_parser
   const char *completion_word;
 
   /* If the current token was a quoted string, then this is the
-     quoting character (either " or '.).  */
+     quoting character (either " or ').  */
   int completion_quote_char;
 
   /* If the current token was a quoted string, then this points at the
@@ -592,7 +599,7 @@ find_parameter_list_end (const char *input)
    keyword, return the keyword start offset in STRING.  Return -1
    otherwise.  */
 
-size_t
+static size_t
 string_find_incomplete_keyword_at_end (const char * const *keywords,
 				       const char *string, size_t string_len)
 {
@@ -2893,7 +2900,12 @@ complete_linespec_component (linespec_parser *parser,
 
       linespec_complete_function (tracker, text, source_filename);
       if (source_filename == NULL)
-	fn_list = complete_source_filenames (text);
+	{
+	  /* Haven't seen a source component, like in "b
+	     file.c:function[TAB]".  Maybe this wasn't a function, but
+	     a filename instead, like "b file.[TAB]".  */
+	  fn_list = complete_source_filenames (text);
+	}
 
       /* If we only have a single filename completion, append a ':' for
 	 the user, since that's the only thing that can usefully follow
@@ -3066,7 +3078,10 @@ linespec_complete (completion_tracker &tracker, const char *text)
   else if (parser.complete_what == linespec_complete_what::FUNCTION)
     {
       /* While parsing/lexing, we didn't know whether the completion
-	 word completes to a unique function name already or not.  E.g.:
+	 word completes to a unique function/source name already or
+	 not.
+
+	 E.g.:
 	   "b function() <tab>"
 	 may need to complete either to:
 	   "b function() const"
@@ -3080,10 +3095,26 @@ linespec_complete (completion_tracker &tracker, const char *text)
 	 with "foo" being the template function's return type, or to:
 	   "b foo thread/task"
 
-	 Address that by completing assuming function, and seeing if
-	 we find a function completion that matches exactly
-	 "function()". If so, then we advance the completion word past
-	 the function and switch to KEYWORD completion mode.  */
+	 Or, this:
+	   "b file<TAB>"
+	 may need to complete either to a source file name:
+	   "b file.c"
+	 or this, also a filename, but a unique completion:
+	   "b file.c:"
+	 or to a function name:
+	   "b file_function"
+
+	 Address that by completing assuming source or function, and
+	 seeing if we find a completion that matches exactly the
+	 completion word.  If so, then it must be a function (see note
+	 below) and we advance the completion word to the end of input
+	 and switch to KEYWORD completion mode.
+
+	 Note: if we find a unique completion for a source filename,
+	 then it won't match the completion word, because the LCD will
+	 contain a trailing ':'.  And if we're completing at or after
+	 the ':', then complete_linespec_component won't try to
+	 complete on source filenames.  */
 
       const char *text = parser.completion_word;
       const char *word = parser.completion_word;
@@ -3098,7 +3129,9 @@ linespec_complete (completion_tracker &tracker, const char *text)
       if (tracker.quote_char ())
 	{
 	  /* The function/file name was not close-quoted, so this
-	     can't be a keyword.  */
+	     can't be a keyword.  Note: complete_linespec_component
+	     may have swapped the original quote char for ':' when we
+	     get here, but that still indicates the same.  */
 	}
       else if (!tracker.have_completions ())
 	{
-- 
2.5.5


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

* Re: [PATCH 18/40] A smarter linespec completer
  2017-07-17 18:21     ` Pedro Alves
@ 2017-07-17 19:02       ` Keith Seitz
  2017-07-17 19:33         ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-17 19:02 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 07/17/2017 11:21 AM, Pedro Alves wrote:
>>
>>> +  int completion_quote_char;
>>
>> Why int?
> 
> Mainly because the related readline code uses int too.

Well, that's definitely a good reason! That's all I really wanted to know.

> I can make this a char, and for consistency try to make completer_tracker
> use char too, though I'd prefer to do that as a patch on top of this one
> that touches every path in a single go, than update the few related
> completer_tracker patches, mainly because gdb_rl_find_completion_word
> is already in master.  Would you mind that?

It's up to you, really. IMO, I don't see any overwhelming reason to change this. In fact, a good case could be made for not changing it (now that I know what said reason is).

> Here are the comments that I came up with.  WDYT?

Perfect! Thanks!

Keith

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

* Re: [PATCH 13/40] Introduce strncmp_iw
  2017-06-29  8:42   ` Yao Qi
@ 2017-07-17 19:16     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 19:16 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches


On 06/29/2017 09:41 AM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> +/* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
>> +   differences in whitespace.  STRING2_LEN is STRING2's length.
>> +   Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
>> +   non-zero otherwise (slightly different than strncmp()'s range of
>> +   return values).  */
>> +extern int strncmp_iw (const char *, const char *, size_t);
> 
> Use parameter name in the declaration, otherwise, STRING1, STRING2 and
> STRING2_LEN in comments are pointless.

Indeed, fixed.

> Otherwise, patch is good to me.
> 

Pushed as below.

Thanks much,
Pedro Alves

From 1d550c828c00978860de9ba35b9ab5b182b968bc Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 17 Jul 2017 20:08:48 +0100
Subject: [PATCH] Introduce strncmp_iw

The explicit locations completer patch will need a strncmp_iw
function, that to strcmp_iw like strncmp is to strcmp.  This patch
implements it.

(Unit tests added a bit further down in this series will exercise
this.)

gdb/ChangeLog:
2017-07-17  Pedro Alves  <palves@redhat.com>

	* utils.c (enum class strncmp_iw_mode): New.
	(strcmp_iw): Rename to ...
	(strncmp_iw_with_mode): ... this.  Add string2_len and mode
	parameters.  Handle them.
	(strncmp_iw): New.
	(strcmp_iw): Reimplement as wrapper around strncmp_iw_with_mode.
	* utils.h (strncmp_iw): Declare.
	(strcmp_iw): Move describing comments here.
---
 gdb/ChangeLog | 11 +++++++++
 gdb/utils.c   | 77 +++++++++++++++++++++++++++++++++++++++++------------------
 gdb/utils.h   | 19 ++++++++++++++-
 3 files changed, 83 insertions(+), 24 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8778cae..f1e21f1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,16 @@
 2017-07-17  Pedro Alves  <palves@redhat.com>
 
+	* utils.c (enum class strncmp_iw_mode): New.
+	(strcmp_iw): Rename to ...
+	(strncmp_iw_with_mode): ... this.  Add string2_len and mode
+	parameters.  Handle them.
+	(strncmp_iw): New.
+	(strcmp_iw): Reimplement as wrapper around strncmp_iw_with_mode.
+	* utils.h (strncmp_iw): Declare.
+	(strcmp_iw): Move describing comments here.
+
+2017-07-17  Pedro Alves  <palves@redhat.com>
+
 	* c-exp.y (operator_stoken): Use CP_OPERATOR_LEN and
 	CP_OPERATOR_STR.
 	* c-typeprint.c (is_type_conversion_operator): Use
diff --git a/gdb/utils.c b/gdb/utils.c
index b66132a..43e1827 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2363,41 +2363,72 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
     }
 }
 
-/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
-   differences in whitespace.  Returns 0 if they match, non-zero if they
-   don't (slightly different than strcmp()'s range of return values).
+/* Modes of operation for strncmp_iw_with_mode.  */
 
-   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
-   This "feature" is useful when searching for matching C++ function names
-   (such as if the user types 'break FOO', where FOO is a mangled C++
-   function).  */
+enum class strncmp_iw_mode
+{
+  /* Work like strncmp, while ignoring whitespace.  */
+  NORMAL,
 
-int
-strcmp_iw (const char *string1, const char *string2)
+  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
+     string1=="FOO(PARAMS)" matches string2=="FOO".  */
+  MATCH_PARAMS,
+};
+
+/* Helper for strncmp_iw and strcmp_iw.  */
+
+static int
+strncmp_iw_with_mode (const char *string1, const char *string2,
+		      size_t string2_len, strncmp_iw_mode mode)
 {
-  while ((*string1 != '\0') && (*string2 != '\0'))
+  const char *end_str2 = string2 + string2_len;
+
+  while (1)
     {
       while (isspace (*string1))
-	{
-	  string1++;
-	}
-      while (isspace (*string2))
-	{
-	  string2++;
-	}
+	string1++;
+      while (string2 < end_str2 && isspace (*string2))
+	string2++;
+      if (*string1 == '\0' || string2 == end_str2)
+	break;
       if (case_sensitivity == case_sensitive_on && *string1 != *string2)
 	break;
       if (case_sensitivity == case_sensitive_off
 	  && (tolower ((unsigned char) *string1)
 	      != tolower ((unsigned char) *string2)))
 	break;
-      if (*string1 != '\0')
-	{
-	  string1++;
-	  string2++;
-	}
+
+      string1++;
+      string2++;
     }
-  return (*string1 != '\0' && *string1 != '(') || (*string2 != '\0');
+
+  if (string2 == end_str2)
+    {
+      if (mode == strncmp_iw_mode::NORMAL)
+	return 0;
+      else
+	return (*string1 != '\0' && *string1 != '(');
+    }
+  else
+    return 1;
+}
+
+/* See utils.h.  */
+
+int
+strncmp_iw (const char *string1, const char *string2, size_t string2_len)
+{
+  return strncmp_iw_with_mode (string1, string2, string2_len,
+			       strncmp_iw_mode::NORMAL);
+}
+
+/* See utils.h.  */
+
+int
+strcmp_iw (const char *string1, const char *string2)
+{
+  return strncmp_iw_with_mode (string1, string2, strlen (string2),
+			       strncmp_iw_mode::MATCH_PARAMS);
 }
 
 /* This is like strcmp except that it ignores whitespace and treats
diff --git a/gdb/utils.h b/gdb/utils.h
index 3347c23..6df752f 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -31,7 +31,24 @@ extern void initialize_utils (void);
 
 extern int sevenbit_strings;
 
-extern int strcmp_iw (const char *, const char *);
+/* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
+   differences in whitespace.  STRING2_LEN is STRING2's length.
+   Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
+   non-zero otherwise (slightly different than strncmp()'s range of
+   return values).  */
+extern int strncmp_iw (const char *string1, const char *string2,
+		       size_t string2_len);
+
+/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
+   differences in whitespace.  Returns 0 if they match, non-zero if
+   they don't (slightly different than strcmp()'s range of return
+   values).
+
+   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
+   This "feature" is useful when searching for matching C++ function
+   names (such as if the user types 'break FOO', where FOO is a
+   mangled C++ function).  */
+extern int strcmp_iw (const char *string1, const char *string2);
 
 extern int strcmp_iw_ordered (const char *, const char *);
 
-- 
2.5.5

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

* Re: [PATCH 19/40] Fix cp_find_first_component_aux bug
  2017-06-02 12:23 ` [PATCH 19/40] Fix cp_find_first_component_aux bug Pedro Alves
@ 2017-07-17 19:17   ` Keith Seitz
  2017-07-17 19:50     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-17 19:17 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* cp-support.c (cp_find_first_component_aux): Add missing case for
> 	end of string.

That's pretty obvious, even to me. Do we a test case would add anything? /me not so sure [but then it would be rather trivial to write, eh?]

Keith

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

* Re: [PATCH 15/40] Rewrite/enhance explicit locations completer, parse left->right
  2017-07-14 20:55   ` Keith Seitz
@ 2017-07-17 19:24     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 19:24 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/14/2017 09:55 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
>> So... this patch gets rid of the need for quoting.
> 
> Yippie! /me really dislikes all the quoting nonsense that users have to deal with

:-)

> 
>> gdb/testsuite/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* gdb.base/completion.exp: Adjust expected output.
>> 	* gdb.linespec/ls-errs.exp (do_test): Adjust expected output.
> 
>> diff --git a/gdb/completer.c b/gdb/completer.c
>> index a1d3a43..0473d8c 100644
>> --- a/gdb/completer.c
>> +++ b/gdb/completer.c
> 
>> @@ -1189,6 +1452,22 @@ completion_tracker::completion_tracker ()
>>  				      NULL, xcalloc, xfree);
>>  }
>>  
>> +void
>> +completion_tracker::discard_completions ()
> 
> Implicit or missing comment?

Missing.

> 
>> @@ -1517,6 +1839,12 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
>>      }
>>  }
>>  
>> +void
>> +completion_tracker::advance_custom_word_point_by (size_t len)
> 
> Implicit or missing comment?

Fixed.

> 
>> +{
>> +  m_custom_word_point += len;
>> +}
>> +
>>  /* Build a new C string that is a copy or LCD with the whitespace of
>>     ORIG/ORIG_LEN preserved.
>>  
>> @@ -1720,14 +2055,20 @@ completion_result::reset_match_list ()
>>  static char **
>>  gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
>>  {
>> -  /* Completers must be called twice.  If rl_point (i.e., END) is at
>> -     column 0, then readline skips the the handle_brkchars phase, and
>> -     so we create a tracker now in that case too.  */
>> -  delete current_completion.tracker;
>> -  current_completion.tracker = new completion_tracker ();
>> +  /* Completers that provide a custom word point in the
>> +     handle_brkchars phase also compute their completions then.
>> +     Completers that leave the completion word handling to readline
>> +     must be called twice.  If rl_point (i.e., END) is at column 0,
>> +     then readline skips the the handle_brkchars phase, and so we
>                             ^^^^^^^
> "the the"

Fixed.

> 
>> +     create a tracker now in that case too.  */
>> +  if (end == 0 || !current_completion.tracker->use_custom_word_point ())
>> +    {
>> +      delete current_completion.tracker;
>> +      current_completion.tracker = new completion_tracker ();
>>  
>> -  complete_line (*current_completion.tracker, text,
>> -		 rl_line_buffer, rl_point);
>> +      complete_line (*current_completion.tracker, text,
>> +		     rl_line_buffer, rl_point);
>> +    }
>>  
>>    completion_tracker &tracker = *current_completion.tracker;
>>  
>> diff --git a/gdb/location.c b/gdb/location.c
>> index d711d7b..19d3232 100644
>> --- a/gdb/location.c
>> +++ b/gdb/location.c
>> +static bool
>> +is_cp_operator (const char *start, const char *comma)
>> +{
>> +  if (comma != NULL
>> +      && (comma - start) >= CP_OPERATOR_LEN)
>> +    {
>> +      const char *p = comma;
>> +
>> +      while (p > start && isspace (p[-1]))
>> +	p--;
>> +      if (p - start >= CP_OPERATOR_LEN)
>> +	{
>> +	  p-= CP_OPERATOR_LEN;
>           ^^^
> 
> Missing a space there.

Fixed.

> 
>> +	  if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
>> +	      && (p == start
>> +		  || !(isalnum (p[-1]) || p[-1] == '_')))
>> +	    {
> 
> 
>> diff --git a/gdb/symtab.c b/gdb/symtab.c
>> index 91e8b90..7c7ff7f 100644
>> --- a/gdb/symtab.c
>> +++ b/gdb/symtab.c
>> @@ -4995,6 +4995,7 @@ add_symtab_completions (struct compunit_symtab *cust,
>>  void
>>  default_collect_symbol_completion_matches_break_on
>>    (completion_tracker &tracker,
>> +   complete_symbol_mode mode,
>>     const char *text, const char *word,
>>     const char *break_on, enum type_code code)
>>  {
>> @@ -5013,8 +5014,12 @@ default_collect_symbol_completion_matches_break_on
>>    const char *sym_text;
>>    /* Length of sym_text.  */
>>    int sym_text_len;
>> +  struct cleanup *cleanups;
> 
> `cleanups' is unused.

Eh, curious.  Thanks, removed.

> 
>> diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
>> index 6597ea7..f03bfc3 100644
>> --- a/gdb/testsuite/gdb.base/completion.exp
>> +++ b/gdb/testsuite/gdb.base/completion.exp
>> @@ -790,7 +790,7 @@ gdb_test_multiple "" $test {
>>      -re "break\.c.*break1\.c.*$gdb_prompt " {
>>  	send_gdb "1\t\n"
>>  	gdb_test_multiple "" $test {
>> -	    -re ".*Function \"$srcfile2\" not defined\..*$gdb_prompt " {
>> +	    -re "malformed linespec error: unexpected end of input\r\n$gdb_prompt " {
>>  		pass $test
>>  	    }
>>  	    -re "$gdb_prompt p$" {
> 
> I don't understand this change, and it FAILs for me. In the log, it is still saying "Function \"break1.c\" not found". [NOTE: the completion is still "break break1.c ".]
> 
> I see this will eventually PASS (when the trailing ':' is output). Perhaps this hunk is in the wrong patch?

Indeed, that's exactly it.  This hunk should be a part of the
linespec completer patch.  I've moved it there.  Thanks for noticing that.

Before that other patch:

 (gdb) break break1[TAB]
 (gdb) break break1.c [RET]
 Function "break1.c" not defined.
 Make breakpoint pending on future shared library load? (y or [n]) n

After:

 (gdb) break break1[TAB]
 (gdb) break break1.c:[RET]
 malformed linespec error: unexpected end of input

Here's what I pushed (sorry, lost the delta...)

From c6756f62e04846d68c24ee922ddb0377d4bd17f2 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 17 Jul 2017 20:21:33 +0100
Subject: [PATCH] Rewrite/enhance explicit locations completer, parse
 left->right

One of the most annoying (to me) things about GDB's completion is when
you have overloads in your program, and you want to set a breakpoint
in one of them:

 void function(int);  // set breakpoint here.
 void function(long);

 (gdb) b -f func[TAB]
 (gdb) b -f function(       # ok, gdb completed as much as possible.
 (gdb) b -f function([TAB]  # show me the overloads, please.
 <_all_ symbols in the program are shown...>

E.g., when debugging GDB, that'd be:

 (gdb) b -f function([TAB]
 (anonymous namespace)::get_global()::global  pt_insn_get_offset@plt                       scm_new_port_table_entry
 asprintf                                     pt_pkt_alloc_decoder                         scm_new_port_table_entry@plt
 asprintf@plt                                 pt_pkt_alloc_decoder@plt                     scm_out_of_range
 bt_ctf_get_char_array                        pt_pkt_sync_forward                          scm_out_of_range@plt
 bt_ctf_get_char_array@plt                    pt_pkt_sync_forward@plt                      scm_putc
 bt_ctf_get_uint64                            pwrite                                       scm_putc@plt
 bt_ctf_get_uint64@plt                        pwrite@plt                                   scm_reverse_x
 bt_ctf_iter_read_event                       PyErr_Restore                                scm_reverse_x@plt
 bt_ctf_iter_read_event@plt                   PyErr_Restore@plt                            scm_set_port_filename_x
 <snip...>

Now that's a load of completely useless completions.

The reason GDB offers those is that the completer relies on readline
figuring out the completion word point in the input line based on the
language's word break characters, which include "(".  So readline
tells the completer to complete on "", the string that is after '('.
Likewise, if you type "function(i[TAB]" to try to complete to "int",
you're out of luck.  GDB shows you all the symbols in the program that
start with "i"...  This makes sense for the expression completer, as
what you'd want to type is e.g., a global variable, say:

(gdb) print function(i[TAB]

but, it makes no sense when specifying a function name for a
breakpoint location.

To get around that limitation, users need to quote the function name,
like:

 (gdb) b -f 'function([TAB]
 function(int)      function(long)
 (gdb) b 'function(i[TAB]
 (gdb) b 'function(int)' # now completes correctly!

Note that the quoting is only necessary for completion.  Creating the
breakpoint does not require the quoting:

 (gdb) b -f function(int) [RET]
 Breakpoint 1 at ....

This patch removes this limitation.

(
Actually, it's a necessary patch, though not sufficient.  That'll
start working correctly by the end of the series.  With this patch, if try it,
you'll see:

 (gdb) b -f function(i[TAB]
 (gdb) b -f function

i.e., gdb strips everything after the "(".  That's caused by some code
in symtab.c that'll be eliminated further down the series.  These
patches are all unfortunately interrelated, which is also the reason
new tests only appear much later in the series.
But let's ignore that reality for the remainder of the description.
)

So... this patch gets rid of the need for quoting.

It does that by adding a way for a completer to control the exact
completion word point that readline should start the completion
request for, instead of letting readline try to figure it out using
the current language's word break chars array, and often failing.

In the case above, we want the completer to figure out that it's
completing a function name that starts with "function(i".  It now
does.

It took me a while to figure out a way to ask readline to "use this
exact word point", and for a while I feared that it'd be impossible
with current readline (and having to rely on master readline for core
functionality is something I'd like to avoid very much).  Eventually,
after several different attempts, I came up with what is described in
the comment above gdb_custom_word_point_brkchars in the patch.

With this patch, the handle_brkchars phase of the explicit location
completer advances the expected word point as it parses the input line
left to right, until it figures out exactly what we're completing,
instead of expecting readline to break the string using the word break
characters, and then having the completer heuristically fix up a bad
decision by parsing the input string backwards.  This allows correctly
knowning that we're completing a symbol name after -function, complete
functions without quoting, etc.

Later, we'll make use of this same mechanims to implement a proper
linespec completer that avoids need for quoting too.

gdb/ChangeLog:
2017-07-17  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_collect_symbol_completion_matches): Add
	complete_symbol_mode parameter.
	* cli/cli-cmds.c (complete_command): Get the completion result out
	of the handle_brkchars tracker if used a custom word point.
	* completer.c: Include "linespec.h".
	(enum explicit_location_match_type) <MATCH_LINE>: New enumerator.
	(advance_to_expression_complete_word_point): New.
	(completion_tracker::completes_to_completion_word): New.
	(complete_files_symbols): Pass down
	complete_symbol_mode::EXPRESSION.
	(explicit_options, probe_options): New.
	(collect_explicit_location_matches): Complete on the
	explictit_loc->foo instead of word.  Use
	linespec_complete_function.  Handle MATCH_LINE.  Handle offering
	keyword and options completions.
	(backup_text_ptr): Delete.
	(skip_keyword): New.
	(complete_explicit_location): Remove 'word' parameter.  Add
	language, quoted_arg_start and quoted_arg_end parameters.
	Rewrite, parsing left to right.
	(location_completer): Rewrite.
	(location_completer_handle_brkchars): New function.
	(symbol_completer): Pass down complete_symbol_mode::EXPRESSION.
	(enum complete_line_internal_reason): Adjust comments.
	(completion_tracker::discard_completions): New.
	(completer_handle_brkchars_func_for_completer): Handle
	location_completer.
	(gdb_custom_word_point_brkchars)
	(gdb_org_rl_basic_quote_characters): New.
	(gdb_completion_word_break_characters_throw)
	(completion_find_completion_word): Handle trackers that use a
	custom word point.
	(completion_tracker::advance_custom_word_point_by): New.
	(completion_tracker::build_completion_result): Don't rely on
	readline appending the quote char.
	(gdb_rl_attempted_completion_function_throw): Handle trackers that
	use a custom word point.
	(gdb_rl_attempted_completion_function): Restore
	rl_basic_quote_characters.
	* completer.h (class completion_tracker): Extend intro comment.
	(completion_tracker::set_quote_char)
	(completion_tracker::quote_char)
	(completion_tracker::set_use_custom_word_point)
	(completion_tracker::use_custom_word_point)
	(completion_tracker::custom_word_point)
	(completion_tracker::set_custom_word_point)
	(completion_tracker::advance_custom_word_point_by)
	(completion_tracker::completes_to_completion_word)
	(completion_tracker::discard_completions): New methods.
	(completion_tracker::m_quote_char)
	(completion_tracker::m_use_custom_word_point)
	(completion_tracker::m_custom_word_point): New fields.
	(advance_to_expression_complete_word_point): Declare.
	* f-lang.c (f_collect_symbol_completion_matches): Add
	complete_symbol_mode parameter.
	* language.h (struct language_defn)
	<la_collect_symbol_completion_matches>: Add complete_symbol_mode
	parameter.
	* linespec.c (linespec_keywords): Add NULL terminator.  Make extern.
	(linespec_complete_function): New function.
	(linespec_lexer_lex_keyword): Adjust.
	* linespec.h (linespec_keywords, linespec_complete_function): New
	declarations.
	* location.c (find_end_quote): New function.
	(explicit_location_lex_one): Add explicit_completion_info
	parameter.  Save quoting info.  Don't throw if being called for
	completion.  Don't handle Ada operators here.
	(is_cp_operator, skip_op_false_positives, first_of)
	(explicit_location_lex_one_function): New function.
	(string_to_explicit_location): Replace 'dont_throw' parameter with
	an explicit_completion_info pointer parameter.  Handle it.  Don't
	use explicit_location_lex_one to lex function names.  Use
	explicit_location_lex_one_function instead.
	* location.h (struct explicit_completion_info): New.
	(string_to_explicit_location): Replace 'dont_throw' parameter with
	an explicit_completion_info pointer parameter.
	* symtab.c (default_collect_symbol_completion_matches_break_on):
	Add complete_symbol_mode parameter.  Handle LINESPEC mode.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches): Add complete_symbol_mode
	parameter.
	(collect_symbol_completion_matches_type): Pass down
	complete_symbol_mode::EXPRESSION.
	(collect_file_symbol_completion_matches): Add complete_symbol_mode
	parameter.  Handle LINESPEC mode.
	* symtab.h (complete_symbol_mode): New.
	(default_collect_symbol_completion_matches_break_on)
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches)
	(collect_file_symbol_completion_matches): Add complete_symbol_mode
	parameter.

gdb/testsuite/ChangeLog:
2017-07-17  Pedro Alves  <palves@redhat.com>

	* gdb.linespec/ls-errs.exp (do_test): Adjust expected output.
---
 gdb/ChangeLog                          |  94 ++++++
 gdb/testsuite/ChangeLog                |   4 +
 gdb/ada-lang.c                         |   1 +
 gdb/cli/cli-cmds.c                     |  17 +-
 gdb/completer.c                        | 568 ++++++++++++++++++++++++++-------
 gdb/completer.h                        |  82 ++++-
 gdb/f-lang.c                           |   3 +-
 gdb/language.h                         |   1 +
 gdb/linespec.c                         |  31 +-
 gdb/linespec.h                         |  10 +
 gdb/location.c                         | 303 ++++++++++++++++--
 gdb/location.h                         |  29 +-
 gdb/symtab.c                           |  18 +-
 gdb/symtab.h                           |  15 +
 gdb/testsuite/gdb.linespec/ls-errs.exp |  18 +-
 15 files changed, 1029 insertions(+), 165 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index f1e21f1..b341eb1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,99 @@
 2017-07-17  Pedro Alves  <palves@redhat.com>
 
+	* ada-lang.c (ada_collect_symbol_completion_matches): Add
+	complete_symbol_mode parameter.
+	* cli/cli-cmds.c (complete_command): Get the completion result out
+	of the handle_brkchars tracker if used a custom word point.
+	* completer.c: Include "linespec.h".
+	(enum explicit_location_match_type) <MATCH_LINE>: New enumerator.
+	(advance_to_expression_complete_word_point): New.
+	(completion_tracker::completes_to_completion_word): New.
+	(complete_files_symbols): Pass down
+	complete_symbol_mode::EXPRESSION.
+	(explicit_options, probe_options): New.
+	(collect_explicit_location_matches): Complete on the
+	explictit_loc->foo instead of word.  Use
+	linespec_complete_function.  Handle MATCH_LINE.  Handle offering
+	keyword and options completions.
+	(backup_text_ptr): Delete.
+	(skip_keyword): New.
+	(complete_explicit_location): Remove 'word' parameter.  Add
+	language, quoted_arg_start and quoted_arg_end parameters.
+	Rewrite, parsing left to right.
+	(location_completer): Rewrite.
+	(location_completer_handle_brkchars): New function.
+	(symbol_completer): Pass down complete_symbol_mode::EXPRESSION.
+	(enum complete_line_internal_reason): Adjust comments.
+	(completion_tracker::discard_completions): New.
+	(completer_handle_brkchars_func_for_completer): Handle
+	location_completer.
+	(gdb_custom_word_point_brkchars)
+	(gdb_org_rl_basic_quote_characters): New.
+	(gdb_completion_word_break_characters_throw)
+	(completion_find_completion_word): Handle trackers that use a
+	custom word point.
+	(completion_tracker::advance_custom_word_point_by): New.
+	(completion_tracker::build_completion_result): Don't rely on
+	readline appending the quote char.
+	(gdb_rl_attempted_completion_function_throw): Handle trackers that
+	use a custom word point.
+	(gdb_rl_attempted_completion_function): Restore
+	rl_basic_quote_characters.
+	* completer.h (class completion_tracker): Extend intro comment.
+	(completion_tracker::set_quote_char)
+	(completion_tracker::quote_char)
+	(completion_tracker::set_use_custom_word_point)
+	(completion_tracker::use_custom_word_point)
+	(completion_tracker::custom_word_point)
+	(completion_tracker::set_custom_word_point)
+	(completion_tracker::advance_custom_word_point_by)
+	(completion_tracker::completes_to_completion_word)
+	(completion_tracker::discard_completions): New methods.
+	(completion_tracker::m_quote_char)
+	(completion_tracker::m_use_custom_word_point)
+	(completion_tracker::m_custom_word_point): New fields.
+	(advance_to_expression_complete_word_point): Declare.
+	* f-lang.c (f_collect_symbol_completion_matches): Add
+	complete_symbol_mode parameter.
+	* language.h (struct language_defn)
+	<la_collect_symbol_completion_matches>: Add complete_symbol_mode
+	parameter.
+	* linespec.c (linespec_keywords): Add NULL terminator.  Make extern.
+	(linespec_complete_function): New function.
+	(linespec_lexer_lex_keyword): Adjust.
+	* linespec.h (linespec_keywords, linespec_complete_function): New
+	declarations.
+	* location.c (find_end_quote): New function.
+	(explicit_location_lex_one): Add explicit_completion_info
+	parameter.  Save quoting info.  Don't throw if being called for
+	completion.  Don't handle Ada operators here.
+	(is_cp_operator, skip_op_false_positives, first_of)
+	(explicit_location_lex_one_function): New function.
+	(string_to_explicit_location): Replace 'dont_throw' parameter with
+	an explicit_completion_info pointer parameter.  Handle it.  Don't
+	use explicit_location_lex_one to lex function names.  Use
+	explicit_location_lex_one_function instead.
+	* location.h (struct explicit_completion_info): New.
+	(string_to_explicit_location): Replace 'dont_throw' parameter with
+	an explicit_completion_info pointer parameter.
+	* symtab.c (default_collect_symbol_completion_matches_break_on):
+	Add complete_symbol_mode parameter.  Handle LINESPEC mode.
+	(default_collect_symbol_completion_matches)
+	(collect_symbol_completion_matches): Add complete_symbol_mode
+	parameter.
+	(collect_symbol_completion_matches_type): Pass down
+	complete_symbol_mode::EXPRESSION.
+	(collect_file_symbol_completion_matches): Add complete_symbol_mode
+	parameter.  Handle LINESPEC mode.
+	* symtab.h (complete_symbol_mode): New.
+	(default_collect_symbol_completion_matches_break_on)
+	(default_collect_symbol_completion_matches)
+	(collect_symbol_completion_matches)
+	(collect_file_symbol_completion_matches): Add complete_symbol_mode
+	parameter.
+
+2017-07-17  Pedro Alves  <palves@redhat.com>
+
 	* utils.c (enum class strncmp_iw_mode): New.
 	(strcmp_iw): Rename to ...
 	(strncmp_iw_with_mode): ... this.  Add string2_len and mode
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index bd68b63..f1f1ecb 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2017-07-17  Pedro Alves  <palves@redhat.com>
+
+	* gdb.linespec/ls-errs.exp (do_test): Adjust expected output.
+
 2017-07-15  Andrew Burgess  <andrew.burgess@embecosm.com>
 
 	* gdb.mi/mi-vla-fortran.exp: Make test names unique.
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index e629902..9b5dfab 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6500,6 +6500,7 @@ symbol_completion_add (completion_tracker &tracker,
 
 static void
 ada_collect_symbol_completion_matches (completion_tracker &tracker,
+				       complete_symbol_mode mode,
 				       const char *text0, const char *word,
 				       enum type_code code)
 {
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index fb41e24..af750d3 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -266,6 +266,7 @@ complete_command (char *arg_entry, int from_tty)
 
   completion_tracker tracker_handle_brkchars;
   completion_tracker tracker_handle_completions;
+  completion_tracker *tracker;
 
   int quote_char = '\0';
   const char *word;
@@ -275,8 +276,17 @@ complete_command (char *arg_entry, int from_tty)
       word = completion_find_completion_word (tracker_handle_brkchars,
 					      arg, &quote_char);
 
-      /* Completers must be called twice.  */
-      complete_line (tracker_handle_completions, word, arg, strlen (arg));
+      /* Completers that provide a custom word point in the
+	 handle_brkchars phase also compute their completions then.
+	 Completers that leave the completion word handling to readline
+	 must be called twice.  */
+      if (tracker_handle_brkchars.use_custom_word_point ())
+	tracker = &tracker_handle_brkchars;
+      else
+	{
+	  complete_line (tracker_handle_completions, word, arg, strlen (arg));
+	  tracker = &tracker_handle_completions;
+	}
     }
   CATCH (ex, RETURN_MASK_ALL)
     {
@@ -286,8 +296,7 @@ complete_command (char *arg_entry, int from_tty)
   std::string arg_prefix (arg, word - arg);
 
   completion_result result
-    = (tracker_handle_completions.build_completion_result
-       (word, word - arg, strlen (arg)));
+    = tracker->build_completion_result (word, word - arg, strlen (arg));
 
   if (result.number_matches != 0)
     {
diff --git a/gdb/completer.c b/gdb/completer.c
index 196610d..1e4fe18 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -29,6 +29,7 @@
 #include "arch-utils.h"
 #include "location.h"
 #include <algorithm>
+#include "linespec.h"
 #include "cli/cli-decode.h"
 
 /* FIXME: This is needed because of lookup_cmd_1 ().  We should be
@@ -44,6 +45,9 @@
 
 #include "completer.h"
 
+static void complete_expression (completion_tracker &tracker,
+				 const char *text, const char *word);
+
 /* Misc state that needs to be tracked across several different
    readline completer entry point calls, all related to a single
    completion invocation.  */
@@ -63,8 +67,9 @@ struct gdb_completer_state
 /* The current completion state.  */
 static gdb_completer_state current_completion;
 
-/* An enumeration of the various things a user might
-   attempt to complete for a location.  */
+/* An enumeration of the various things a user might attempt to
+   complete for a location.  If you change this, remember to update
+   the explicit_options array below too.  */
 
 enum explicit_location_match_type
 {
@@ -74,6 +79,9 @@ enum explicit_location_match_type
     /* The name of a function or method.  */
     MATCH_FUNCTION,
 
+    /* A line number.  */
+    MATCH_LINE,
+
     /* The name of a label.  */
     MATCH_LABEL
 };
@@ -366,6 +374,48 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
   return line_buffer + point;
 }
 
+/* See completer.h.  */
+
+const char *
+advance_to_expression_complete_word_point (completion_tracker &tracker,
+					   const char *text)
+{
+  gdb_rl_completion_word_info info;
+
+  info.word_break_characters
+    = current_language->la_word_break_characters ();
+  info.quote_characters = gdb_completer_quote_characters;
+  info.basic_quote_characters = rl_basic_quote_characters;
+
+  const char *start
+    = gdb_rl_find_completion_word (&info, NULL, NULL, text);
+
+  tracker.advance_custom_word_point_by (start - text);
+
+  return start;
+}
+
+/* See completer.h.  */
+
+bool
+completion_tracker::completes_to_completion_word (const char *word)
+{
+  if (m_lowest_common_denominator_unique)
+    {
+      const char *lcd = m_lowest_common_denominator;
+
+      if (strncmp_iw (word, lcd, strlen (lcd)) == 0)
+	{
+	  /* Maybe skip the function and complete on keywords.  */
+	  size_t wordlen = strlen (word);
+	  if (word[wordlen - 1] == ' ')
+	    return true;
+	}
+    }
+
+  return false;
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -450,7 +500,9 @@ complete_files_symbols (completion_tracker &tracker,
      symbols as well as on files.  */
   if (colon)
     {
-      collect_file_symbol_completion_matches (tracker, symbol_start, word,
+      collect_file_symbol_completion_matches (tracker,
+					      complete_symbol_mode::EXPRESSION,
+					      symbol_start, word,
 					      file_to_match);
       xfree (file_to_match);
     }
@@ -458,7 +510,9 @@ complete_files_symbols (completion_tracker &tracker,
     {
       size_t text_len = strlen (text);
 
-      collect_symbol_completion_matches (tracker, symbol_start, word);
+      collect_symbol_completion_matches (tracker,
+					 complete_symbol_mode::EXPRESSION,
+					 symbol_start, word);
       /* If text includes characters which cannot appear in a file
 	 name, they cannot be asking for completion on files.  */
       if (strcspn (text,
@@ -499,10 +553,32 @@ complete_files_symbols (completion_tracker &tracker,
       /* No completions at all.  As the final resort, try completing
 	 on the entire text as a symbol.  */
       collect_symbol_completion_matches (tracker,
+					 complete_symbol_mode::EXPRESSION,
 					 orig_text, word);
     }
 }
 
+/* The explicit location options.  Note that indexes into this array
+   must match the explicit_location_match_type enumerators.  */
+static const char *const explicit_options[] =
+  {
+    "-source",
+    "-function",
+    "-line",
+    "-label",
+    NULL
+  };
+
+/* The probe modifier options.  These can appear before a location in
+   breakpoint commands.  */
+static const char *const probe_options[] =
+  {
+    "-probe",
+    "-probe-stap",
+    "-probe-dtrace",
+    NULL
+  };
+
 /* Returns STRING if not NULL, the empty string otherwise.  */
 
 static const char *
@@ -518,18 +594,22 @@ static void
 collect_explicit_location_matches (completion_tracker &tracker,
 				   struct event_location *location,
 				   enum explicit_location_match_type what,
-				   const char *word)
+				   const char *word,
+				   const struct language_defn *language)
 {
   const struct explicit_location *explicit_loc
     = get_explicit_location (location);
 
+  /* Note, in the various MATCH_* below, we complete on
+     explicit_loc->foo instead of WORD, because only the former will
+     have already skipped past any quote char.  */
   switch (what)
     {
     case MATCH_SOURCE:
       {
 	const char *source = string_or_empty (explicit_loc->source_filename);
 	completion_list matches
-	  = make_source_files_completion_list (source, word);
+	  = make_source_files_completion_list (source, source);
 	tracker.add_completions (std::move (matches));
       }
       break;
@@ -537,18 +617,15 @@ collect_explicit_location_matches (completion_tracker &tracker,
     case MATCH_FUNCTION:
       {
 	const char *function = string_or_empty (explicit_loc->function_name);
-	if (explicit_loc->source_filename != NULL)
-	  {
-	    const char *filename = explicit_loc->source_filename;
-
-	    collect_file_symbol_completion_matches (tracker,
-						    function, word, filename);
-	  }
-       else
-	 collect_symbol_completion_matches (tracker, function, word);
+	linespec_complete_function (tracker, function,
+				    explicit_loc->source_filename);
       }
       break;
 
+    case MATCH_LINE:
+      /* Nothing to offer.  */
+      break;
+
     case MATCH_LABEL:
       /* Not supported.  */
       break;
@@ -556,126 +633,296 @@ collect_explicit_location_matches (completion_tracker &tracker,
     default:
       gdb_assert_not_reached ("unhandled explicit_location_match_type");
     }
+
+  if (tracker.completes_to_completion_word (word))
+    {
+      tracker.discard_completions ();
+      tracker.advance_custom_word_point_by (strlen (word));
+      complete_on_enum (tracker, explicit_options, "", "");
+      complete_on_enum (tracker, linespec_keywords, "", "");
+    }
+  else if (!tracker.have_completions ())
+    {
+      /* Maybe we have an unterminated linespec keyword at the tail of
+	 the string.  Try completing on that.  */
+      size_t wordlen = strlen (word);
+      const char *keyword = word + wordlen;
+
+      if (wordlen > 0 && keyword[-1] != ' ')
+	{
+	  while (keyword > word && *keyword != ' ')
+	    keyword--;
+	  /* Don't complete on keywords if we'd be completing on the
+	     whole explicit linespec option.  E.g., "b -function
+	     thr<tab>" should not complete to the "thread"
+	     keyword.  */
+	  if (keyword != word)
+	    {
+	      keyword = skip_spaces_const (keyword);
+
+	      tracker.advance_custom_word_point_by (keyword - word);
+	      complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+	    }
+	}
+      else if (wordlen > 0 && keyword[-1] == ' ')
+	{
+	  /* Assume that we're maybe past the explicit location
+	     argument, and we didn't manage to find any match because
+	     the user wants to create a pending breakpoint.  Offer the
+	     keyword and explicit location options as possible
+	     completions.  */
+	  tracker.advance_custom_word_point_by (keyword - word);
+	  complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+	  complete_on_enum (tracker, explicit_options, keyword, keyword);
+	}
+    }
 }
 
-/* A convenience macro to (safely) back up P to the previous word.  */
+/* If the next word in *TEXT_P is any of the keywords in KEYWORDS,
+   then advance both TEXT_P and the word point in the tracker past the
+   keyword and return the (0-based) index in the KEYWORDS array that
+   matched.  Otherwise, return -1.  */
 
-static const char *
-backup_text_ptr (const char *p, const char *text)
+static int
+skip_keyword (completion_tracker &tracker,
+	      const char * const *keywords, const char **text_p)
 {
-  while (p > text && isspace (*p))
-    --p;
-  for (; p > text && !isspace (p[-1]); --p)
-    ;
+  const char *text = *text_p;
+  const char *after = skip_to_space_const (text);
+  size_t len = after - text;
+
+  if (text[len] != ' ')
+    return -1;
+
+  int found = -1;
+  for (int i = 0; keywords[i] != NULL; i++)
+    {
+      if (strncmp (keywords[i], text, len) == 0)
+	{
+	  if (found == -1)
+	    found = i;
+	  else
+	    return -1;
+	}
+    }
+
+  if (found != -1)
+    {
+      tracker.advance_custom_word_point_by (len + 1);
+      text += len + 1;
+      *text_p = text;
+      return found;
+    }
 
-  return p;
+  return -1;
 }
 
 /* A completer function for explicit locations.  This function
-   completes both options ("-source", "-line", etc) and values.  */
+   completes both options ("-source", "-line", etc) and values.  If
+   completing a quoted string, then QUOTED_ARG_START and
+   QUOTED_ARG_END point to the quote characters.  LANGUAGE is the
+   current language.  */
 
 static void
 complete_explicit_location (completion_tracker &tracker,
 			    struct event_location *location,
-			    const char *text, const char *word)
+			    const char *text,
+			    const language_defn *language,
+			    const char *quoted_arg_start,
+			    const char *quoted_arg_end)
 {
-  const char *p;
+  if (*text != '-')
+    return;
 
-  /* Find the beginning of the word.  This is necessary because
-     we need to know if we are completing an option name or value.  We
-     don't get the leading '-' from the completer.  */
-  p = backup_text_ptr (word, text);
+  int keyword = skip_keyword (tracker, explicit_options, &text);
 
-  if (*p == '-')
+  if (keyword == -1)
+    complete_on_enum (tracker, explicit_options, text, text);
+  else
     {
-      /* Completing on option name.  */
-      static const char *const keywords[] =
+      /* Completing on value.  */
+      enum explicit_location_match_type what
+	= (explicit_location_match_type) keyword;
+
+      if (quoted_arg_start != NULL && quoted_arg_end != NULL)
 	{
-	  "source",
-	  "function",
-	  "line",
-	  "label",
-	  NULL
-	};
+	  if (quoted_arg_end[1] == '\0')
+	    {
+	      /* If completing a quoted string with the cursor right
+		 at the terminating quote char, complete the
+		 completion word without interpretation, so that
+		 readline advances the cursor one whitespace past the
+		 quote, even if there's no match.  This makes these
+		 cases behave the same:
+
+		   before: "b -function function()"
+		   after:  "b -function function() "
+
+		   before: "b -function 'function()'"
+		   after:  "b -function 'function()' "
+
+		 and trusts the user in this case:
+
+		   before: "b -function 'not_loaded_function_yet()'"
+		   after:  "b -function 'not_loaded_function_yet()' "
+	      */
+	      gdb::unique_xmalloc_ptr<char> text_copy
+		(xstrdup (text));
+	      tracker.add_completion (std::move (text_copy));
+	    }
+	  else if (quoted_arg_end[1] == ' ')
+	    {
+	      /* We're maybe past the explicit location argument.
+		 Skip the argument without interpretion, assuming the
+		 user may want to create pending breakpoint.  Offer
+		 the keyword and explicit location options as possible
+		 completions.  */
+	      tracker.advance_custom_word_point_by (strlen (text));
+	      complete_on_enum (tracker, linespec_keywords, "", "");
+	      complete_on_enum (tracker, explicit_options, "", "");
+	    }
+	  return;
+	}
 
-      /* Skip over the '-'.  */
-      ++p;
+      /* Now gather matches  */
+      collect_explicit_location_matches (tracker, location, what, text,
+					 language);
+    }
+}
 
-      complete_on_enum (tracker, keywords, p, p);
-      return;
+/* A completer for locations.  */
+
+void
+location_completer (struct cmd_list_element *ignore,
+		    completion_tracker &tracker,
+		    const char *text, const char *word_entry)
+{
+  int found_probe_option = -1;
+
+  /* If we have a probe modifier, skip it.  This can only appear as
+     first argument.  Until we have a specific completer for probes,
+     falling back to the linespec completer for the remainder of the
+     line is better than nothing.  */
+  if (text[0] == '-' && text[1] == 'p')
+    found_probe_option = skip_keyword (tracker, probe_options, &text);
+
+  const char *option_text = text;
+  int saved_word_point = tracker.custom_word_point ();
+
+  const char *copy = text;
+
+  explicit_completion_info completion_info;
+  event_location_up location
+    = string_to_explicit_location (&copy, current_language,
+				   &completion_info);
+  if (completion_info.quoted_arg_start != NULL
+      && completion_info.quoted_arg_end == NULL)
+    {
+      /* Found an unbalanced quote.  */
+      tracker.set_quote_char (*completion_info.quoted_arg_start);
+      tracker.advance_custom_word_point_by (1);
     }
-  else
+
+  if (location != NULL)
     {
-      /* Completing on value (or unknown).  Get the previous word to see what
-	 the user is completing on.  */
-      size_t len, offset;
-      const char *new_word, *end;
-      enum explicit_location_match_type what;
-      struct explicit_location *explicit_loc
-	= get_explicit_location (location);
-
-      /* Backup P to the previous word, which should be the option
-	 the user is attempting to complete.  */
-      offset = word - p;
-      end = --p;
-      p = backup_text_ptr (p, text);
-      len = end - p;
-
-      if (strncmp (p, "-source", len) == 0)
+      if (*copy != '\0')
 	{
-	  what = MATCH_SOURCE;
-	  new_word = explicit_loc->source_filename + offset;
+	  tracker.advance_custom_word_point_by (copy - text);
+	  text = copy;
+
+	  /* We found a terminator at the tail end of the string,
+	     which means we're past the explicit location options.  We
+	     may have a keyword to complete on.  If we have a whole
+	     keyword, then complete whatever comes after as an
+	     expression.  This is mainly for the "if" keyword.  If the
+	     "thread" and "task" keywords gain their own completers,
+	     they should be used here.  */
+	  int keyword = skip_keyword (tracker, linespec_keywords, &text);
+
+	  if (keyword == -1)
+	    {
+	      complete_on_enum (tracker, linespec_keywords, text, text);
+	    }
+	  else
+	    {
+	      const char *word
+		= advance_to_expression_complete_word_point (tracker, text);
+	      complete_expression (tracker, text, word);
+	    }
 	}
-      else if (strncmp (p, "-function", len) == 0)
+      else
 	{
-	  what = MATCH_FUNCTION;
-	  new_word = explicit_loc->function_name + offset;
+	  tracker.advance_custom_word_point_by (completion_info.last_option
+						- text);
+	  text = completion_info.last_option;
+
+	  complete_explicit_location (tracker, location.get (), text,
+				      current_language,
+				      completion_info.quoted_arg_start,
+				      completion_info.quoted_arg_end);
+
 	}
-      else if (strncmp (p, "-label", len) == 0)
+    }
+  else
+    {
+      /* This is an address or linespec location.  */
+      if (*text == '*')
 	{
-	  what = MATCH_LABEL;
-	  new_word = explicit_loc->label_name + offset;
+	  tracker.advance_custom_word_point_by (1);
+	  text++;
+	  const char *word
+	    = advance_to_expression_complete_word_point (tracker, text);
+	  complete_expression (tracker, text, word);
 	}
       else
 	{
-	  /* The user isn't completing on any valid option name,
-	     e.g., "break -source foo.c [tab]".  */
-	  return;
+	  /* Fall back to the old linespec completer, for now.  */
+
+	  if (word_entry == NULL)
+	    {
+	     /* We're in the handle_brkchars phase.  */
+	      tracker.set_use_custom_word_point (false);
+	      return;
+	    }
+
+	  complete_files_symbols (tracker, text, word_entry);
 	}
+    }
 
-      /* If the user hasn't entered a search expression, e.g.,
-	 "break -function <TAB><TAB>", new_word will be NULL, but
-	 search routines require non-NULL search words.  */
-      if (new_word == NULL)
-	new_word = "";
+  /* Add matches for option names, if either:
 
-      /* Now gather matches  */
-      collect_explicit_location_matches (tracker, location, what, new_word);
+     - Some completer above found some matches, but the word point did
+       not advance (e.g., "b <tab>" finds all functions, or "b -<tab>"
+       matches all objc selectors), or;
+
+     - Some completer above advanced the word point, but found no
+       matches.
+  */
+  if ((text[0] == '-' || text[0] == '\0')
+      && (!tracker.have_completions ()
+	  || tracker.custom_word_point () == saved_word_point))
+    {
+      tracker.set_custom_word_point (saved_word_point);
+      text = option_text;
+
+      if (found_probe_option == -1)
+	complete_on_enum (tracker, probe_options, text, text);
+      complete_on_enum (tracker, explicit_options, text, text);
     }
 }
 
-/* A completer for locations.  */
+/* The corresponding completer_handle_brkchars
+   implementation.  */
 
-void
-location_completer (struct cmd_list_element *ignore,
-		    completion_tracker &tracker,
-		    const char *text, const char *word)
+static void
+location_completer_handle_brkchars (struct cmd_list_element *ignore,
+				    completion_tracker &tracker,
+				    const char *text,
+				    const char *word_ignored)
 {
-  const char *copy = text;
+  tracker.set_use_custom_word_point (true);
 
-  event_location_up location = string_to_explicit_location (&copy,
-							    current_language,
-							    1);
-  if (location != NULL)
-    complete_explicit_location (tracker, location.get (),
-				text, word);
-  else
-    {
-      /* This is an address or linespec location.
-	 Right now both of these are handled by the (old) linespec
-	 completer.  */
-      complete_files_symbols (tracker, text, word);
-    }
+  location_completer (ignore, tracker, text, NULL);
 }
 
 /* Helper for expression_completer which recursively adds field and
@@ -837,7 +1084,8 @@ symbol_completer (struct cmd_list_element *ignore,
 		  completion_tracker &tracker,
 		  const char *text, const char *word)
 {
-  collect_symbol_completion_matches (tracker, text, word);
+  collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+				     text, word);
 }
 
 /* Here are some useful test cases for completion.  FIXME: These
@@ -866,10 +1114,24 @@ symbol_completer (struct cmd_list_element *ignore,
 enum complete_line_internal_reason
 {
   /* Preliminary phase, called by gdb_completion_word_break_characters
-     function, is used to determine the correct set of chars that are
-     word delimiters depending on the current command in line_buffer.
-     No completion list should be generated; the return value should
-     be NULL.  This is checked by an assertion.  */
+     function, is used to either:
+
+     #1 - Determine the set of chars that are word delimiters
+	  depending on the current command in line_buffer.
+
+     #2 - Manually advance RL_POINT to the "word break" point instead
+	  of letting readline do it (based on too-simple character
+	  matching).
+
+     Simpler completers that just pass a brkchars array to readline
+     (#1 above) must defer generating the completions to the main
+     phase (below).  No completion list should be generated in this
+     phase.
+
+     OTOH, completers that manually advance the word point(#2 above)
+     must set "use_custom_word_point" in the tracker and generate
+     their completion in this phase.  Note that this is the convenient
+     thing to do since they'll be parsing the input line anyway.  */
   handle_brkchars,
 
   /* Main phase, called by complete_line function, is used to get the
@@ -1003,6 +1265,8 @@ complete_line_internal_1 (completion_tracker &tracker,
       p++;
     }
 
+  tracker.advance_custom_word_point_by (p - tmp_command);
+
   if (!c)
     {
       /* It is an unrecognized command.  So there are no
@@ -1184,6 +1448,24 @@ completion_tracker::completion_tracker ()
 
 /* See completer.h.  */
 
+void
+completion_tracker::discard_completions ()
+{
+  xfree (m_lowest_common_denominator);
+  m_lowest_common_denominator = NULL;
+
+  m_lowest_common_denominator_unique = false;
+
+  m_entries_vec.clear ();
+
+  htab_delete (m_entries_hash);
+  m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+				      htab_hash_string, (htab_eq) streq,
+				      NULL, xcalloc, xfree);
+}
+
+/* See completer.h.  */
+
 completion_tracker::~completion_tracker ()
 {
   xfree (m_lowest_common_denominator);
@@ -1412,12 +1694,27 @@ completer_handle_brkchars_func_for_completer (completer_ftype *fn)
   if (fn == filename_completer)
     return filename_completer_handle_brkchars;
 
+  if (fn == location_completer)
+    return location_completer_handle_brkchars;
+
   if (fn == command_completer)
     return command_completer_handle_brkchars;
 
   return default_completer_handle_brkchars;
 }
 
+/* Used as brkchars when we want to tell readline we have a custom
+   word point.  We do that by making our rl_completion_word_break_hook
+   set RL_POINT to the desired word point, and return the character at
+   the word break point as the break char.  This is two bytes in order
+   to fit one break character plus the terminating null.  */
+static char gdb_custom_word_point_brkchars[2];
+
+/* Since rl_basic_quote_characters is not completer-specific, we save
+   its original value here, in order to be able to restore it in
+   gdb_rl_attempted_completion_function.  */
+static const char *gdb_org_rl_basic_quote_characters = rl_basic_quote_characters;
+
 /* Get the list of chars that are considered as word breaks
    for the current command.  */
 
@@ -1434,6 +1731,27 @@ gdb_completion_word_break_characters_throw ()
   complete_line_internal (tracker, NULL, rl_line_buffer,
 			  rl_point, handle_brkchars);
 
+  if (tracker.use_custom_word_point ())
+    {
+      gdb_assert (tracker.custom_word_point () > 0);
+      rl_point = tracker.custom_word_point () - 1;
+      gdb_custom_word_point_brkchars[0] = rl_line_buffer[rl_point];
+      rl_completer_word_break_characters = gdb_custom_word_point_brkchars;
+      rl_completer_quote_characters = NULL;
+
+      /* Clear this too, so that if we're completing a quoted string,
+	 readline doesn't consider the quote character a delimiter.
+	 If we didn't do this, readline would auto-complete {b
+	 'fun<tab>} to {'b 'function()'}, i.e., add the terminating
+	 \', but, it wouldn't append the separator space either, which
+	 is not desirable.  So instead we take care of appending the
+	 quote character to the LCD ourselves, in
+	 gdb_rl_attempted_completion_function.  Since this global is
+	 not just completer-specific, we'll restore it back to the
+	 default in gdb_rl_attempted_completion_function.  */
+      rl_basic_quote_characters = NULL;
+    }
+
   return rl_completer_word_break_characters;
 }
 
@@ -1468,6 +1786,13 @@ completion_find_completion_word (completion_tracker &tracker, const char *text,
 
   complete_line_internal (tracker, NULL, text, point, handle_brkchars);
 
+  if (tracker.use_custom_word_point ())
+    {
+      gdb_assert (tracker.custom_word_point () > 0);
+      *quote_char = tracker.quote_char ();
+      return text + tracker.custom_word_point ();
+    }
+
   gdb_rl_completion_word_info info;
 
   info.word_break_characters = rl_completer_word_break_characters;
@@ -1509,6 +1834,14 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
     }
 }
 
+/* See completer.h.  */
+
+void
+completion_tracker::advance_custom_word_point_by (size_t len)
+{
+  m_custom_word_point += len;
+}
+
 /* Build a new C string that is a copy of LCD with the whitespace of
    ORIG/ORIG_LEN preserved.
 
@@ -1596,6 +1929,13 @@ completion_tracker::build_completion_result (const char *text,
 
   if (m_lowest_common_denominator_unique)
     {
+      /* We don't rely on readline appending the quote char as
+	 delimiter as then readline wouldn't append the ' ' after the
+	 completion.  */
+      char buf[2] = { quote_char () };
+
+      match_list[0] = reconcat (match_list[0], match_list[0],
+				buf, (char *) NULL);
       match_list[1] = NULL;
 
       /* If we already have a space at the end of the match, tell
@@ -1728,14 +2068,20 @@ completion_result::reset_match_list ()
 static char **
 gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
 {
-  /* Completers must be called twice.  If rl_point (i.e., END) is at
-     column 0, then readline skips the the handle_brkchars phase, and
-     so we create a tracker now in that case too.  */
-  delete current_completion.tracker;
-  current_completion.tracker = new completion_tracker ();
+  /* Completers that provide a custom word point in the
+     handle_brkchars phase also compute their completions then.
+     Completers that leave the completion word handling to readline
+     must be called twice.  If rl_point (i.e., END) is at column 0,
+     then readline skips the handle_brkchars phase, and so we create a
+     tracker now in that case too.  */
+  if (end == 0 || !current_completion.tracker->use_custom_word_point ())
+    {
+      delete current_completion.tracker;
+      current_completion.tracker = new completion_tracker ();
 
-  complete_line (*current_completion.tracker, text,
-		 rl_line_buffer, rl_point);
+      complete_line (*current_completion.tracker, text,
+		     rl_line_buffer, rl_point);
+    }
 
   completion_tracker &tracker = *current_completion.tracker;
 
@@ -1753,6 +2099,10 @@ gdb_rl_attempted_completion_function_throw (const char *text, int start, int end
 char **
 gdb_rl_attempted_completion_function (const char *text, int start, int end)
 {
+  /* Restore globals that might have been tweaked in
+     gdb_completion_word_break_characters.  */
+  rl_basic_quote_characters = gdb_org_rl_basic_quote_characters;
+
   /* If we end up returning NULL, either on error, or simple because
      there are no matches, inhibit readline's default filename
      completer.  */
diff --git a/gdb/completer.h b/gdb/completer.h
index cf93cf0..40d2f58 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -133,6 +133,12 @@ public:
      up in the event the user requests to complete on something vague
      that necessitates the time consuming expansion of many symbol
      tables.
+
+   - The custom word point to hand over to readline, for completers
+     that parse the input string in order to dynamically adjust
+     themselves depending on exactly what they're completing.  E.g.,
+     the linespec completer needs to bypass readline's too-simple word
+     breaking algorithm.
 */
 class completion_tracker
 {
@@ -153,10 +159,52 @@ public:
      LIST.  */
   void add_completions (completion_list &&list);
 
+  /* Set the quote char to be appended after a unique completion is
+     added to the input line.  Set to '\0' to clear.  See
+     m_quote_char's description.  */
+  void set_quote_char (int quote_char)
+  { m_quote_char = quote_char; }
+
+  /* The quote char to be appended after a unique completion is added
+     to the input line.  Returns '\0' if no quote char has been set.
+     See m_quote_char's description.  */
+  int quote_char () { return m_quote_char; }
+
+  /* Tell the tracker that the current completer wants to provide a
+     custom word point instead of a list of a break chars, in the
+     handle_brkchars phase.  Such completers must also compute their
+     completions then.  */
+  void set_use_custom_word_point (bool enable)
+  { m_use_custom_word_point = enable; }
+
+  /* Whether the current completer computes a custom word point.  */
+  bool use_custom_word_point () const
+  { return m_use_custom_word_point; }
+
+  /* The custom word point.  */
+  int custom_word_point () const
+  { return m_custom_word_point; }
+
+  /* Set the custom word point to POINT.  */
+  void set_custom_word_point (int point)
+  { m_custom_word_point = point; }
+
+  /* Advance the custom word point by LEN.  */
+  void advance_custom_word_point_by (size_t len);
+
+  /* Return true if we only have one completion, and it matches
+     exactly the completion word.  I.e., completing results in what we
+     already have.  */
+  bool completes_to_completion_word (const char *word);
+
   /* True if we have any completion match recorded.  */
   bool have_completions () const
   { return !m_entries_vec.empty (); }
 
+  /* Discard the current completion match list and the current
+     LCD.  */
+  void discard_completions ();
+
   /* Build a completion_result containing the list of completion
      matches to hand over to readline.  The parameters are as in
      rl_attempted_completion_function.  */
@@ -185,7 +233,30 @@ private:
      searching too early.  */
   htab_t m_entries_hash;
 
-  /* Our idea of lowest common denominator to hand over to readline.  */
+  /* If non-zero, then this is the quote char that needs to be
+     appended after completion (iff we have a unique completion).  We
+     don't rely on readline appending the quote char as delimiter as
+     then readline wouldn't append the ' ' after the completion.
+     I.e., we want this:
+
+      before tab: "b 'function("
+      after tab:  "b 'function()' "
+  */
+  int m_quote_char = '\0';
+
+  /* If true, the completer has its own idea of "word" point, and
+     doesn't want to rely on readline computing it based on brkchars.
+     Set in the handle_brkchars phase.  */
+  bool m_use_custom_word_point = false;
+
+  /* The completer's idea of where the "word" we were looking at is
+     relative to RL_LINE_BUFFER.  This is advanced in the
+     handle_brkchars phase as the completer discovers potential
+     completable words.  */
+  int m_custom_word_point = 0;
+
+  /* Our idea of lowest common denominator to hand over to readline.
+     See intro.  */
   char *m_lowest_common_denominator = NULL;
 
   /* If true, the LCD is unique.  I.e., all completion candidates had
@@ -213,6 +284,15 @@ extern const char *completion_find_completion_word (completion_tracker &tracker,
 						    const char *text,
 						    int *quote_char);
 
+
+/* Assuming TEXT is an expression in the current language, find the
+   completion word point for TEXT, emulating the algorithm readline
+   uses to find the word point, using the current language's word
+   break characters.  */
+
+const char *advance_to_expression_complete_word_point
+  (completion_tracker &tracker, const char *text);
+
 extern char **gdb_rl_attempted_completion_function (const char *text,
 						    int start, int end);
 
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index e1184ee..937ebff 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -230,10 +230,11 @@ f_word_break_characters (void)
 
 static void
 f_collect_symbol_completion_matches (completion_tracker &tracker,
+				     complete_symbol_mode mode,
 				     const char *text, const char *word,
 				     enum type_code code)
 {
-  default_collect_symbol_completion_matches_break_on (tracker,
+  default_collect_symbol_completion_matches_break_on (tracker, mode,
 						      text, word, ":", code);
 }
 
diff --git a/gdb/language.h b/gdb/language.h
index 7ce4f7f..75b9438 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -330,6 +330,7 @@ struct language_defn
        symbols whose type has a code of CODE should be matched.  */
     void (*la_collect_symbol_completion_matches)
       (completion_tracker &tracker,
+       complete_symbol_mode mode,
        const char *text,
        const char *word,
        enum type_code code);
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 25ebdca..20ac71d 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -216,9 +216,9 @@ enum ls_token_type
 };
 typedef enum ls_token_type linespec_token_type;
 
-/* List of keywords  */
-
-static const char * const linespec_keywords[] = { "if", "thread", "task" };
+/* List of keywords.  This is NULL-terminated so that it can be used
+   as enum completer.  */
+const char * const linespec_keywords[] = { "if", "thread", "task", NULL };
 #define IF_KEYWORD_INDEX 0
 
 /* A token of the linespec lexer  */
@@ -400,7 +400,7 @@ linespec_lexer_lex_keyword (const char *p)
 
   if (p != NULL)
     {
-      for (i = 0; i < ARRAY_SIZE (linespec_keywords); ++i)
+      for (i = 0; linespec_keywords[i] != NULL; ++i)
 	{
 	  int len = strlen (linespec_keywords[i]);
 
@@ -421,7 +421,7 @@ linespec_lexer_lex_keyword (const char *p)
 		{
 		  p += len;
 		  p = skip_spaces_const (p);
-		  for (j = 0; j < ARRAY_SIZE (linespec_keywords); ++j)
+		  for (j = 0; linespec_keywords[j] != NULL; ++j)
 		    {
 		      int nextlen = strlen (linespec_keywords[j]);
 
@@ -1714,7 +1714,7 @@ linespec_parse_basic (linespec_parser *parser)
 	      if (token.type != LSTOKEN_NUMBER)
 		unexpected_linespec_error (parser);
 
-	      /* Record the lione offset and get the next token.  */
+	      /* Record the line offset and get the next token.  */
 	      name = copy_token_string (token);
 	      cleanup = make_cleanup (xfree, name);
 
@@ -2451,6 +2451,25 @@ linespec_lex_to_end (char **stringp)
   do_cleanups (cleanup);
 }
 
+/* See linespec.h.  */
+
+void
+linespec_complete_function (completion_tracker &tracker,
+			    const char *function,
+			    const char *source_filename)
+{
+  complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
+
+  if (source_filename != NULL)
+    {
+      collect_file_symbol_completion_matches (tracker, mode,
+					      function, function,
+					      source_filename);
+    }
+  else
+    collect_symbol_completion_matches (tracker, mode, function, function);
+}
+
 /* A helper function for decode_line_full and decode_line_1 to
    turn LOCATION into symtabs_and_lines.  */
 
diff --git a/gdb/linespec.h b/gdb/linespec.h
index f1244c4..d55ba12 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -184,6 +184,16 @@ extern const char *find_toplevel_char (const char *s, char c);
 
 extern void linespec_lex_to_end (char **stringp);
 
+extern const char * const linespec_keywords[];
+
+/* Complete a function symbol, in linespec mode.  If SOURCE_FILENAME
+   is non-NULL, limits completion to the list of functions defined in
+   source files that match SOURCE_FILENAME.  */
+
+extern void linespec_complete_function (completion_tracker &tracker,
+					const char *function,
+					const char *source_filename);
+
 /* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
    advancing EXP_PTR past any parsed text.  */
 
diff --git a/gdb/location.c b/gdb/location.c
index d711d7b..3238c9a 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -415,13 +415,44 @@ event_location_to_string (struct event_location *location)
   return EL_STRING (location);
 }
 
+/* Find an instance of the quote character C in the string S that is
+   outside of all single- and double-quoted strings (i.e., any quoting
+   other than C).  */
+
+static const char *
+find_end_quote (const char *s, char end_quote_char)
+{
+  /* zero if we're not in quotes;
+     '"' if we're in a double-quoted string;
+     '\'' if we're in a single-quoted string.  */
+  char nested_quote_char = '\0';
+
+  for (const char *scan = s; *scan != '\0'; scan++)
+    {
+      if (nested_quote_char != '\0')
+	{
+	  if (*scan == nested_quote_char)
+	    nested_quote_char = '\0';
+	  else if (scan[0] == '\\' && *(scan + 1) != '\0')
+	    scan++;
+	}
+      else if (*scan == end_quote_char && nested_quote_char == '\0')
+	return scan;
+      else if (*scan == '"' || *scan == '\'')
+	nested_quote_char = *scan;
+    }
+
+  return 0;
+}
+
 /* A lexer for explicit locations.  This function will advance INP
    past any strings that it lexes.  Returns a malloc'd copy of the
    lexed string or NULL if no lexing was done.  */
 
 static gdb::unique_xmalloc_ptr<char>
 explicit_location_lex_one (const char **inp,
-			   const struct language_defn *language)
+			   const struct language_defn *language,
+			   explicit_completion_info *completion_info)
 {
   const char *start = *inp;
 
@@ -431,21 +462,27 @@ explicit_location_lex_one (const char **inp,
   /* If quoted, skip to the ending quote.  */
   if (strchr (get_gdb_linespec_parser_quote_characters (), *start))
     {
-      char quote_char = *start;
+      if (completion_info != NULL)
+	completion_info->quoted_arg_start = start;
 
-      /* If the input is not an Ada operator, skip to the matching
-	 closing quote and return the string.  */
-      if (!(language->la_language == language_ada
-	    && quote_char == '\"' && is_ada_operator (start)))
-	{
-	  const char *end = find_toplevel_char (start + 1, quote_char);
+      const char *end = find_end_quote (start + 1, *start);
 
-	  if (end == NULL)
+      if (end == NULL)
+	{
+	  if (completion_info == NULL)
 	    error (_("Unmatched quote, %s."), start);
-	  *inp = end + 1;
+
+	  end = start + strlen (start);
+	  *inp = end;
 	  return gdb::unique_xmalloc_ptr<char> (savestring (start + 1,
-							    *inp - start - 2));
+							    *inp - start - 1));
 	}
+
+      if (completion_info != NULL)
+	completion_info->quoted_arg_end = end;
+      *inp = end + 1;
+      return gdb::unique_xmalloc_ptr<char> (savestring (start + 1,
+							*inp - start - 2));
     }
 
   /* If the input starts with '-' or '+', the string ends with the next
@@ -486,12 +523,180 @@ explicit_location_lex_one (const char **inp,
   return NULL;
 }
 
+/* Return true if COMMA points past "operator".  START is the start of
+   the line that COMMAND points to, hence when reading backwards, we
+   must not read any character before START.  */
+
+static bool
+is_cp_operator (const char *start, const char *comma)
+{
+  if (comma != NULL
+      && (comma - start) >= CP_OPERATOR_LEN)
+    {
+      const char *p = comma;
+
+      while (p > start && isspace (p[-1]))
+	p--;
+      if (p - start >= CP_OPERATOR_LEN)
+	{
+	  p -= CP_OPERATOR_LEN;
+	  if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+	      && (p == start
+		  || !(isalnum (p[-1]) || p[-1] == '_')))
+	    {
+	      return true;
+	    }
+	}
+    }
+  return false;
+}
+
+/* When scanning the input string looking for the next explicit
+   location option/delimiter, we jump to the next option by looking
+   for ",", and "-".  Such a character can also appear in C++ symbols
+   like "operator," and "operator-".  So when we find such a
+   character, we call this function to check if we found such a
+   symbol, meaning we had a false positive for an option string.  In
+   that case, we keep looking for the next delimiter, until we find
+   one that is not a false positive, or we reach end of string.  FOUND
+   is the character that scanning found (either '-' or ','), and START
+   is the start of the line that FOUND points to, hence when reading
+   backwards, we must not read any character before START.  Returns a
+   pointer to the next non-false-positive delimiter character, or NULL
+   if none was found.  */
+
+static const char *
+skip_op_false_positives (const char *start, const char *found)
+{
+  while (found != NULL && is_cp_operator (start, found))
+    {
+      if (found[0] == '-' && found[1] == '-')
+	start = found + 2;
+      else
+	start = found + 1;
+      found = find_toplevel_char (start, *found);
+    }
+
+  return found;
+}
+
+/* Assuming both FIRST and NEW_TOK point into the same string, return
+   the pointer that is closer to the start of the string.  If FIRST is
+   NULL, returns NEW_TOK.  If NEW_TOK is NULL, returns FIRST.  */
+
+static const char *
+first_of (const char *first, const char *new_tok)
+{
+  if (first == NULL)
+    return new_tok;
+  else if (new_tok != NULL && new_tok < first)
+    return new_tok;
+  else
+    return first;
+}
+
+/* A lexer for functions in explicit locations.  This function will
+   advance INP past a function until the next option, or until end of
+   string.  Returns a malloc'd copy of the lexed string or NULL if no
+   lexing was done.  */
+
+static gdb::unique_xmalloc_ptr<char>
+explicit_location_lex_one_function (const char **inp,
+				    const struct language_defn *language,
+				    explicit_completion_info *completion_info)
+{
+  const char *start = *inp;
+
+  if (*start == '\0')
+    return NULL;
+
+  /* If quoted, skip to the ending quote.  */
+  if (strchr (get_gdb_linespec_parser_quote_characters (), *start))
+    {
+      char quote_char = *start;
+
+      /* If the input is not an Ada operator, skip to the matching
+	 closing quote and return the string.  */
+      if (!(language->la_language == language_ada
+	    && quote_char == '\"' && is_ada_operator (start)))
+	{
+	  if (completion_info != NULL)
+	    completion_info->quoted_arg_start = start;
+
+	  const char *end = find_toplevel_char (start + 1, quote_char);
+
+	  if (end == NULL)
+	    {
+	      if (completion_info == NULL)
+		error (_("Unmatched quote, %s."), start);
+
+	      end = start + strlen (start);
+	      *inp = end;
+	      char *saved = savestring (start + 1, *inp - start - 1);
+	      return gdb::unique_xmalloc_ptr<char> (saved);
+	    }
+
+	  if (completion_info != NULL)
+	    completion_info->quoted_arg_end = end;
+	  *inp = end + 1;
+	  char *saved = savestring (start + 1, *inp - start - 2);
+	  return gdb::unique_xmalloc_ptr<char> (saved);
+	}
+    }
+
+  const char *comma = find_toplevel_char (start, ',');
+
+  /* If we have "-function -myfunction", or perhaps better example,
+     "-function -[BasicClass doIt]" (objc selector), treat
+     "-myfunction" as the function name.  I.e., skip the first char if
+     it is an hyphen.  Don't skip the first char always, because we
+     may have C++ "operator<", and find_toplevel_char needs to see the
+     'o' in that case.  */
+  const char *hyphen
+    = (*start == '-'
+       ? find_toplevel_char (start + 1, '-')
+       : find_toplevel_char (start, '-'));
+
+  /* Check for C++ "operator," and "operator-".  */
+  comma = skip_op_false_positives (start, comma);
+  hyphen = skip_op_false_positives (start, hyphen);
+
+  /* Pick the one that appears first.  */
+  const char *end = first_of (hyphen, comma);
+
+  /* See if a linespec keyword appears first.  */
+  const char *s = start;
+  const char *ws = find_toplevel_char (start, ' ');
+  while (ws != NULL && linespec_lexer_lex_keyword (ws + 1) == NULL)
+    {
+      s = ws + 1;
+      ws = find_toplevel_char (s, ' ');
+    }
+  if (ws != NULL)
+    end = first_of (end, ws + 1);
+
+  /* If we don't have any terminator, then take the whole string.  */
+  if (end == NULL)
+    end = start + strlen (start);
+
+  /* Trim whitespace at the end.  */
+  while (end > start && end[-1] == ' ')
+    end--;
+
+  *inp = end;
+
+  if (*inp - start > 0)
+    return gdb::unique_xmalloc_ptr<char> (savestring (start, *inp - start));
+
+  return NULL;
+}
+
 /* See description in location.h.  */
 
 event_location_up
 string_to_explicit_location (const char **argp,
 			     const struct language_defn *language,
-			     int dont_throw)
+			     explicit_completion_info *completion_info)
 {
   event_location_up location;
 
@@ -514,6 +719,14 @@ string_to_explicit_location (const char **argp,
       int len;
       const char *start;
 
+      /* Clear these on each iteration, since they should be filled
+	 with info about the last option.  */
+      if (completion_info != NULL)
+	{
+	  completion_info->quoted_arg_start = NULL;
+	  completion_info->quoted_arg_end = NULL;
+	}
+
       /* If *ARGP starts with a keyword, stop processing
 	 options.  */
       if (linespec_lexer_lex_keyword (*argp) != NULL)
@@ -522,40 +735,68 @@ string_to_explicit_location (const char **argp,
       /* Mark the start of the string in case we need to rewind.  */
       start = *argp;
 
+      if (completion_info != NULL)
+	completion_info->last_option = start;
+
       /* Get the option string.  */
       gdb::unique_xmalloc_ptr<char> opt
-	= explicit_location_lex_one (argp, language);
+	= explicit_location_lex_one (argp, language, NULL);
 
-      *argp = skip_spaces_const (*argp);
+      /* Use the length of the option to allow abbreviations.  */
+      len = strlen (opt.get ());
 
       /* Get the argument string.  */
-      gdb::unique_xmalloc_ptr<char> oarg
-	= explicit_location_lex_one (argp, language);
-      bool have_oarg = oarg != NULL;
       *argp = skip_spaces_const (*argp);
 
-      /* Use the length of the option to allow abbreviations.  */
-      len = strlen (opt.get ());
+      /* All options have a required argument.  Checking for this
+	 required argument is deferred until later.  */
+      gdb::unique_xmalloc_ptr<char> oarg;
+      /* True if we have an argument.  This is required because we'll
+	 move from OARG before checking whether we have an
+	 argument.  */
+      bool have_oarg = false;
+
+      /* Convenience to consistently set both OARG/HAVE_OARG from
+	 ARG.  */
+      auto set_oarg = [&] (gdb::unique_xmalloc_ptr<char> arg)
+	{
+	  oarg = std::move (arg);
+	  have_oarg = oarg != NULL;
+	};
 
-      /* All options have a required argument.  Checking for this required
-	 argument is deferred until later.  */
       if (strncmp (opt.get (), "-source", len) == 0)
-	EL_EXPLICIT (location)->source_filename = oarg.release ();
+	{
+	  set_oarg (explicit_location_lex_one (argp, language,
+					       completion_info));
+	  EL_EXPLICIT (location)->source_filename = oarg.release ();
+	}
       else if (strncmp (opt.get (), "-function", len) == 0)
-	EL_EXPLICIT (location)->function_name = oarg.release ();
+	{
+	  set_oarg (explicit_location_lex_one_function (argp, language,
+							completion_info));
+	  EL_EXPLICIT (location)->function_name = oarg.release ();
+	}
       else if (strncmp (opt.get (), "-line", len) == 0)
 	{
+	  set_oarg (explicit_location_lex_one (argp, language, NULL));
+	  *argp = skip_spaces_const (*argp);
 	  if (have_oarg)
-	    EL_EXPLICIT (location)->line_offset
-	      = linespec_parse_line_offset (oarg.get ());
+	    {
+	      EL_EXPLICIT (location)->line_offset
+		= linespec_parse_line_offset (oarg.get ());
+	      continue;
+	    }
 	}
       else if (strncmp (opt.get (), "-label", len) == 0)
-	EL_EXPLICIT (location)->label_name = oarg.release ();
+	{
+	  set_oarg (explicit_location_lex_one (argp, language, completion_info));
+	  EL_EXPLICIT (location)->label_name = oarg.release ();
+	}
       /* Only emit an "invalid argument" error for options
 	 that look like option strings.  */
       else if (opt.get ()[0] == '-' && !isdigit (opt.get ()[1]))
 	{
-	  if (!dont_throw)
+	  if (completion_info == NULL)
 	    error (_("invalid explicit location argument, \"%s\""), opt.get ());
 	}
       else
@@ -567,11 +808,13 @@ string_to_explicit_location (const char **argp,
 	  return location;
 	}
 
+      *argp = skip_spaces_const (*argp);
+
       /* It's a little lame to error after the fact, but in this
 	 case, it provides a much better user experience to issue
 	 the "invalid argument" error before any missing
 	 argument error.  */
-      if (!have_oarg && !dont_throw)
+      if (!have_oarg && completion_info == NULL)
 	error (_("missing argument for \"%s\""), opt.get ());
     }
 
@@ -581,7 +824,7 @@ string_to_explicit_location (const char **argp,
       && EL_EXPLICIT (location)->function_name == NULL
       && EL_EXPLICIT (location)->label_name == NULL
       && (EL_EXPLICIT (location)->line_offset.sign == LINE_OFFSET_UNKNOWN)
-      && !dont_throw)
+      && completion_info == NULL)
     {
       error (_("Source filename requires function, label, or "
 	       "line offset."));
@@ -639,7 +882,7 @@ string_to_event_location (char **stringp,
 
   /* Try an explicit location.  */
   orig = arg = *stringp;
-  event_location_up location = string_to_explicit_location (&arg, language, 0);
+  event_location_up location = string_to_explicit_location (&arg, language, NULL);
   if (location != NULL)
     {
       /* It was a valid explicit location.  Advance STRINGP to
diff --git a/gdb/location.h b/gdb/location.h
index 7e1f012..58536e0 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -218,20 +218,37 @@ extern event_location_up
   string_to_event_location_basic (char **argp,
 				  const struct language_defn *language);
 
+/* Structure filled in by string_to_explicit_location to aid the
+   completer.  */
+struct explicit_completion_info
+{
+  /* Pointer to the last option found.  E.g., in "b -sou src.c -fun
+     func", LAST_OPTION is left pointing at "-fun func".  */
+  const char *last_option = NULL;
+
+  /* These point to the start and end of a quoted argument, iff the
+     last argument was quoted.  If parsing finds an incomplete quoted
+     string (e.g., "break -function 'fun"), then QUOTED_ARG_START is
+     set to point to the opening \', and QUOTED_ARG_END is left NULL.
+     If the last option is not quoted, then both are set to NULL. */
+  const char *quoted_arg_start = NULL;
+  const char *quoted_arg_end = NULL;
+};
+
 /* Attempt to convert the input string in *ARGP into an explicit location.
    ARGP is advanced past any processed input.  Returns an event_location
    (malloc'd) if an explicit location was successfully found in *ARGP,
    NULL otherwise.
 
-   IF !DONT_THROW, this function may call error() if *ARGP looks like
-   properly formed input, e.g., if it is called with missing argument
-   parameters or invalid options.  If DONT_THROW is non-zero, this function
-   will not throw any exceptions.  */
+   If COMPLETION_INFO is NULL, this function may call error() if *ARGP
+   looks like improperly formed input, e.g., if it is called with
+   missing argument parameters or invalid options.  If COMPLETION_INFO
+   is not NULL, this function will not throw any exceptions.  */
 
 extern event_location_up
   string_to_explicit_location (const char **argp,
-			       const struct language_defn *langauge,
-			       int dont_throw);
+			       const struct language_defn *language,
+			       explicit_completion_info *completion_info);
 
 /* A convenience function for testing for unset locations.  */
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index b077369..d4e107a 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4984,6 +4984,7 @@ add_symtab_completions (struct compunit_symtab *cust,
 void
 default_collect_symbol_completion_matches_break_on
   (completion_tracker &tracker,
+   complete_symbol_mode mode,
    const char *text, const char *word,
    const char *break_on, enum type_code code)
 {
@@ -5004,6 +5005,9 @@ default_collect_symbol_completion_matches_break_on
   int sym_text_len;
 
   /* Now look for the symbol we are supposed to complete on.  */
+  if (mode == complete_symbol_mode::LINESPEC)
+    sym_text = text;
+  else
   {
     const char *p;
     char quote_found;
@@ -5209,10 +5213,11 @@ default_collect_symbol_completion_matches_break_on
 
 void
 default_collect_symbol_completion_matches (completion_tracker &tracker,
+					   complete_symbol_mode mode,
 					   const char *text, const char *word,
 					   enum type_code code)
 {
-  return default_collect_symbol_completion_matches_break_on (tracker,
+  return default_collect_symbol_completion_matches_break_on (tracker, mode,
 							     text, word, "",
 							     code);
 }
@@ -5222,9 +5227,10 @@ default_collect_symbol_completion_matches (completion_tracker &tracker,
 
 void
 collect_symbol_completion_matches (completion_tracker &tracker,
+				   complete_symbol_mode mode,
 				   const char *text, const char *word)
 {
-  current_language->la_collect_symbol_completion_matches (tracker,
+  current_language->la_collect_symbol_completion_matches (tracker, mode,
 							  text, word,
 							  TYPE_CODE_UNDEF);
 }
@@ -5237,10 +5243,12 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 					const char *text, const char *word,
 					enum type_code code)
 {
+  complete_symbol_mode mode = complete_symbol_mode::EXPRESSION;
+
   gdb_assert (code == TYPE_CODE_UNION
 	      || code == TYPE_CODE_STRUCT
 	      || code == TYPE_CODE_ENUM);
-  current_language->la_collect_symbol_completion_matches (tracker,
+  current_language->la_collect_symbol_completion_matches (tracker, mode,
 							  text, word, code);
 }
 
@@ -5249,6 +5257,7 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 
 void
 collect_file_symbol_completion_matches (completion_tracker &tracker,
+					complete_symbol_mode mode,
 					const char *text, const char *word,
 					const char *srcfile)
 {
@@ -5259,6 +5268,9 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
 
   /* Now look for the symbol we are supposed to complete on.
      FIXME: This should be language-specific.  */
+  if (mode == complete_symbol_mode::LINESPEC)
+    sym_text = text;
+  else
   {
     const char *p;
     char quote_found;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 8b975ad..35949f0 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1499,22 +1499,37 @@ extern void forget_cached_source_info (void);
 
 extern void select_source_symtab (struct symtab *);
 
+/* The reason we're calling into a completion match list collector
+   function.  */
+enum class complete_symbol_mode
+  {
+    /* Completing an expression.  */
+    EXPRESSION,
+
+    /* Completing a linespec.  */
+    LINESPEC,
+  };
+
 extern void default_collect_symbol_completion_matches_break_on
   (completion_tracker &tracker,
+   complete_symbol_mode mode,
    const char *text, const char *word, const char *break_on,
    enum type_code code);
 extern void default_collect_symbol_completion_matches
   (completion_tracker &tracker,
+   complete_symbol_mode,
    const char *,
    const char *,
    enum type_code);
 extern void collect_symbol_completion_matches (completion_tracker &tracker,
+					       complete_symbol_mode,
 					       const char *, const char *);
 extern void collect_symbol_completion_matches_type (completion_tracker &tracker,
 						    const char *, const char *,
 						    enum type_code);
 
 extern void collect_file_symbol_completion_matches (completion_tracker &tracker,
+						    complete_symbol_mode,
 						    const char *,
 						    const char *,
 						    const char *);
diff --git a/gdb/testsuite/gdb.linespec/ls-errs.exp b/gdb/testsuite/gdb.linespec/ls-errs.exp
index 1f78ca6..7942f1f 100644
--- a/gdb/testsuite/gdb.linespec/ls-errs.exp
+++ b/gdb/testsuite/gdb.linespec/ls-errs.exp
@@ -167,11 +167,14 @@ proc do_test {lang} {
 	test_break "-source $x -line 3" invalid_file [string trim $x \"']
     }
 
-    # Test that option lexing stops at whitespace boundaries
+    # Test that option lexing stops at whitespace boundaries, except
+    # when lexing function names, where we want to handle setting
+    # breakpoints on e.g., "int template_function<int>()".
     test_break "-source this file has spaces.c -line 3" invalid_file "this"
-    test_break "-function function whitespace" invalid_function "function"
-    test_break "-source $srcfile -function function whitespace" \
-	       invalid_function_f "function" $srcfile
+    test_break "-function ret_type tmpl_function" \
+	invalid_function "ret_type tmpl_function"
+    test_break "-source $srcfile -function ret_type tmpl_function" \
+	       invalid_function_f "ret_type tmpl_function" $srcfile
 
     test_break "-function main -label label whitespace" \
 	       invalid_label "label" "main"
@@ -232,7 +235,12 @@ proc do_test {lang} {
     foreach x {"3" "+100" "-100" "foo"} {
 	test_break "main 3" invalid_function "main 3"
 	test_break "-function \"main $x\"" invalid_function "main $x"
-	test_break "main:here $x" invalid_label "here $x" "main"
+	if {$x == "foo"} {
+	    test_break "main:here $x" unexpected_opt "string" $x
+	} else {
+	    test_break "main:here $x" unexpected_opt "number" $x
+	}
+
 	test_break "-function main -label \"here $x\"" \
 		   invalid_label "here $x" "main"
     }
-- 
2.5.5

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

* Re: [PATCH 18/40] A smarter linespec completer
  2017-07-17 19:02       ` Keith Seitz
@ 2017-07-17 19:33         ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 19:33 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/17/2017 08:02 PM, Keith Seitz wrote:
> On 07/17/2017 11:21 AM, Pedro Alves wrote:

>> Here are the comments that I came up with.  WDYT?
> 
> Perfect! Thanks!

Alright, here's the resulting patch, then, with the test hunk
that was misplaced in the explicit locations patch moved
here.

From c45ec17c07d8aa4554b0b2ca67a5f4dc2c87acc4 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 17 Jul 2017 20:08:02 +0100
Subject: [PATCH] A smarter linespec completer

Continuing the theme of the explicit locations patch, this patch gets
rid of the need for quoting function names in linespec TAB completion.
To recap, when you have overloads in your program, and you want to set
a breakpoint in one of them:

 void function(int);  // set breakpoint here.
 void function(long);

 (gdb) b function(i[TAB]
 <all the symbols in the program that start with "i" are uselessly shown...>

This patch gets rid of the need for quoting by switching the linespec
completer to use the custom completion word point mechanism added in
the previous explicit location patch (extending it as needed), to
correctly determine the right completion word point.  In the case
above, we want the completer to figure out that it's completing a
function name that starts with "function(i", and it now does.

We also want the completer to know when it's potentially completing a
source file name, for:

(gdb) break source.[TAB] -> source.c:
(gdb) break source.c:  # Type line number or function name now

And we want it to know to complete label names, which it doesn't today:

(gdb) break function:lab[TAB]

etc., etc.

So what we want is for completion to grok the input string as closely
to how the linespec parser groks it.

With that in mind, the solution suggests itself - make the linespec
completer use the same parsing code as normal linespec parsing.

That's what the patch does.  The old completer is replaced by one that
reuses the actual linespec parser as much as possible.  This (ideally)
eliminate differences between what completion understands and actually
setting breakpoints understands by design.

The completer now offers sensible completion candidates depending on
which component of the linespec is being completed, source filename,
function, line number, expression, and (a new addition), labels.  For
example, when completing the function part, we now show the full name
of the method as completion candidates, instead of showing whatever
comes after what readline considered the word break character:

 (gdb) break klass::method[TAB]
 klass:method1(int)
 klass:method2()

If input is past the function, then we now offer keyword condidates:

  (gdb) b function(int) [TAB]
  if      task    thread

If input is past a keyword, we offer expression completion, which is
different from linespec completion:

  (gdb) b main if 1 + glo[TAB]
  global

(e.g., completes on types, struct data fields, etc.)

As mentioned, this teaches the linespec completer about completing
label symbols too:

  (gdb) b source.c:function:lab[TAB]

A nice convenience is that when completion uniquely matches a source
name, gdb adds the ":" automatically for you:

  (gdb) b filenam[TAB]
  (gdb) b filename.c:  # ':' auto-added, cursor right after it.

It's the little details.  :-)

I worked on this patch in parallel with writing the (big) testcase
added closer to the end of the series, which exercises many many
tricky cases around quoting and whitespace insertion placement.  In
general, I think it now all Just Works.

gdb/ChangeLog:
2017-07-17  Pedro Alves  <palves@redhat.com>

	* completer.c (complete_source_filenames): New function.
	(complete_address_and_linespec_locations): New function.
	(location_completer): Use complete_address_and_linespec_locations.
	(completion_tracker::build_completion_result): Honor the tracker's
	request to suppress append.
	* completer.h (completion_tracker::suppress_append_ws)
	(completion_tracker::set_suppress_append_ws): New methods.
	(completion_tracker::m_suppress_append_ws): New field.
	(complete_source_filenames): New declaration.
	* linespec.c (linespec_complete_what): New.
	(struct ls_parser) <complete_what, completion_word,
	completion_quote_char, completion_quote_end, completion_tracker>:
	New fields.
	(string_find_incomplete_keyword_at_end): New.
	(linespec_lexer_lex_string): Record quote char.  If in completion
	mode, don't throw.
	(linespec_lexer_consume_token): Advance the completion word point.
	(linespec_lexer_peek_token): Save/restore completion info.
	(save_stream_and_consume_token): New.
	(set_completion_after_number): New.
	(linespec_parse_basic): Set what to complete next depending on
	token.  Handle function and label completions specially.
	(parse_linespec): Disable objc shortcut in completion mode.  Set
	what to complete next depending on token type.  Skip keyword if in
	completion mode.
	(complete_linespec_component, linespec_complete): New.
	* linespec.h (linespec_complete): Declare.

gdb/testsuite/ChangeLog:
2017-07-17  Pedro Alves  <palves@redhat.com>

	* gdb.base/completion.exp: Adjust expected output.
	* gdb.linespec/ls-errs.exp: Don't send tab characters, now that
	the completer works.
---
 gdb/ChangeLog                          |  30 ++
 gdb/testsuite/ChangeLog                |   6 +
 gdb/completer.c                        |  79 +++--
 gdb/completer.h                        |  28 ++
 gdb/linespec.c                         | 628 +++++++++++++++++++++++++++++++--
 gdb/linespec.h                         |   5 +
 gdb/testsuite/gdb.base/completion.exp  |   2 +-
 gdb/testsuite/gdb.linespec/ls-errs.exp |   9 +-
 8 files changed, 722 insertions(+), 65 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8147cc6..20344f7 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,35 @@
 2017-07-17  Pedro Alves  <palves@redhat.com>
 
+	* completer.c (complete_source_filenames): New function.
+	(complete_address_and_linespec_locations): New function.
+	(location_completer): Use complete_address_and_linespec_locations.
+	(completion_tracker::build_completion_result): Honor the tracker's
+	request to suppress append.
+	* completer.h (completion_tracker::suppress_append_ws)
+	(completion_tracker::set_suppress_append_ws): New methods.
+	(completion_tracker::m_suppress_append_ws): New field.
+	(complete_source_filenames): New declaration.
+	* linespec.c (linespec_complete_what): New.
+	(struct ls_parser) <complete_what, completion_word,
+	completion_quote_char, completion_quote_end, completion_tracker>:
+	New fields.
+	(string_find_incomplete_keyword_at_end): New.
+	(linespec_lexer_lex_string): Record quote char.  If in completion
+	mode, don't throw.
+	(linespec_lexer_consume_token): Advance the completion word point.
+	(linespec_lexer_peek_token): Save/restore completion info.
+	(save_stream_and_consume_token): New.
+	(set_completion_after_number): New.
+	(linespec_parse_basic): Set what to complete next depending on
+	token.  Handle function and label completions specially.
+	(parse_linespec): Disable objc shortcut in completion mode.  Set
+	what to complete next depending on token type.  Skip keyword if in
+	completion mode.
+	(complete_linespec_component, linespec_complete): New.
+	* linespec.h (linespec_complete): Declare.
+
+2017-07-17  Pedro Alves  <palves@redhat.com>
+
 	* linespec.c (linespec_lexer_lex_string, find_toplevel_char):
 	Handle 'operator<' / 'operator<<'.
 
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index f1f1ecb..3f0a13e 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,11 @@
 2017-07-17  Pedro Alves  <palves@redhat.com>
 
+	* gdb.base/completion.exp: Adjust expected output.
+	* gdb.linespec/ls-errs.exp: Don't send tab characters, now that
+	the completer works.
+
+2017-07-17  Pedro Alves  <palves@redhat.com>
+
 	* gdb.linespec/ls-errs.exp (do_test): Adjust expected output.
 
 2017-07-15  Andrew Burgess  <andrew.burgess@embecosm.com>
diff --git a/gdb/completer.c b/gdb/completer.c
index c2bb4ee..ba2e860 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -45,9 +45,6 @@
 
 #include "completer.h"
 
-static void complete_expression (completion_tracker &tracker,
-				 const char *text, const char *word);
-
 /* Misc state that needs to be tracked across several different
    readline completer entry point calls, all related to a single
    completion invocation.  */
@@ -558,8 +555,46 @@ complete_files_symbols (completion_tracker &tracker,
     }
 }
 
+/* See completer.h.  */
+
+completion_list
+complete_source_filenames (const char *text)
+{
+  size_t text_len = strlen (text);
+
+  /* If text includes characters which cannot appear in a file name,
+     the user cannot be asking for completion on files.  */
+  if (strcspn (text,
+	       gdb_completer_file_name_break_characters)
+      == text_len)
+    return make_source_files_completion_list (text, text);
+
+  return {};
+}
+
+/* Complete address and linespec locations.  */
+
+static void
+complete_address_and_linespec_locations (completion_tracker &tracker,
+					 const char *text)
+{
+  if (*text == '*')
+    {
+      tracker.advance_custom_word_point_by (1);
+      text++;
+      const char *word
+	= advance_to_expression_complete_word_point (tracker, text);
+      complete_expression (tracker, text, word);
+    }
+  else
+    {
+      linespec_complete (tracker, text);
+    }
+}
+
 /* The explicit location options.  Note that indexes into this array
    must match the explicit_location_match_type enumerators.  */
+
 static const char *const explicit_options[] =
   {
     "-source",
@@ -801,7 +836,7 @@ complete_explicit_location (completion_tracker &tracker,
 void
 location_completer (struct cmd_list_element *ignore,
 		    completion_tracker &tracker,
-		    const char *text, const char *word_entry)
+		    const char *text, const char * /* word */)
 {
   int found_probe_option = -1;
 
@@ -872,27 +907,7 @@ location_completer (struct cmd_list_element *ignore,
   else
     {
       /* This is an address or linespec location.  */
-      if (*text == '*')
-	{
-	  tracker.advance_custom_word_point_by (1);
-	  text++;
-	  const char *word
-	    = advance_to_expression_complete_word_point (tracker, text);
-	  complete_expression (tracker, text, word);
-	}
-      else
-	{
-	  /* Fall back to the old linespec completer, for now.  */
-
-	  if (word_entry == NULL)
-	    {
-	     /* We're in the handle_brkchars phase.  */
-	      tracker.set_use_custom_word_point (false);
-	      return;
-	    }
-
-	  complete_files_symbols (tracker, text, word_entry);
-	}
+      complete_address_and_linespec_locations (tracker, text);
     }
 
   /* Add matches for option names, if either:
@@ -984,11 +999,9 @@ add_struct_fields (struct type *type, completion_list &output,
     }
 }
 
-/* Complete on expressions.  Often this means completing on symbol
-   names, but some language parsers also have support for completing
-   field names.  */
+/* See completer.h.  */
 
-static void
+void
 complete_expression (completion_tracker &tracker,
 		     const char *text, const char *word)
 {
@@ -1944,10 +1957,12 @@ completion_tracker::build_completion_result (const char *text,
 				buf, (char *) NULL);
       match_list[1] = NULL;
 
-      /* If we already have a space at the end of the match, tell
-	 readline to skip appending another.  */
+      /* If the tracker wants to, or we already have a space at the
+	 end of the match, tell readline to skip appending
+	 another.  */
       bool completion_suppress_append
-	= (match_list[0][strlen (match_list[0]) - 1] == ' ');
+	= (suppress_append_ws ()
+	   || match_list[0][strlen (match_list[0]) - 1] == ' ');
 
       return completion_result (match_list, 1, completion_suppress_append);
     }
diff --git a/gdb/completer.h b/gdb/completer.h
index 40d2f58..f68c6dc 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -192,6 +192,16 @@ public:
   /* Advance the custom word point by LEN.  */
   void advance_custom_word_point_by (size_t len);
 
+  /* Whether to tell readline to skip appending a whitespace after the
+     completion.  See m_suppress_append_ws.  */
+  bool suppress_append_ws () const
+  { return m_suppress_append_ws; }
+
+  /* Set whether to tell readline to skip appending a whitespace after
+     the completion.  See m_suppress_append_ws.  */
+  void set_suppress_append_ws (bool suppress)
+  { m_suppress_append_ws = suppress; }
+
   /* Return true if we only have one completion, and it matches
      exactly the completion word.  I.e., completing results in what we
      already have.  */
@@ -255,6 +265,14 @@ private:
      completable words.  */
   int m_custom_word_point = 0;
 
+  /* If true, tell readline to skip appending a whitespace after the
+     completion.  Automatically set if we have a unique completion
+     that already has a space at the end.  A completer may also
+     explicitly set this.  E.g., the linespec completer sets this when
+     the completion ends with the ":" separator between filename and
+     function name.  */
+  bool m_suppress_append_ws = false;
+
   /* Our idea of lowest common denominator to hand over to readline.
      See intro.  */
   char *m_lowest_common_denominator = NULL;
@@ -353,6 +371,16 @@ extern completer_handle_brkchars_ftype *
 
 /* Exported to linespec.c */
 
+/* Return a list of all source files whose names begin with matching
+   TEXT.  */
+extern completion_list complete_source_filenames (const char *text);
+
+/* Complete on expressions.  Often this means completing on symbol
+   names, but some language parsers also have support for completing
+   field names.  */
+extern void complete_expression (completion_tracker &tracker,
+				 const char *text, const char *word);
+
 extern const char *skip_quoted_chars (const char *, const char *,
 				      const char *);
 
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 26baad0..136cb65 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -46,6 +46,35 @@
 #include "location.h"
 #include "common/function-view.h"
 
+/* An enumeration of the various things a user might attempt to
+   complete for a linespec location.  */
+
+enum class linespec_complete_what
+{
+  /* Nothing, no possible completion.  */
+  NOTHING,
+
+  /* A function/method name.  Due to ambiguity between
+
+       (gdb) b source[TAB]
+       source_file.c
+       source_function
+
+     this can also indicate a source filename, iff we haven't seen a
+     separate source filename component, as in "b source.c:function".  */
+  FUNCTION,
+
+  /* A label symbol.  E.g., break file.c:function:LABEL.  */
+  LABEL,
+
+  /* An expression.  E.g., "break foo if EXPR", or "break *EXPR".  */
+  EXPRESSION,
+
+  /* A linespec keyword ("if"/"thread"/"task").
+     E.g., "break func threa<tab>".  */
+  KEYWORD,
+};
+
 typedef struct symbol *symbolp;
 DEF_VEC_P (symbolp);
 
@@ -271,6 +300,29 @@ struct ls_parser
   /* The result of the parse.  */
   struct linespec result;
 #define PARSER_RESULT(PPTR) (&(PPTR)->result)
+
+  /* What the parser believes the current word point should complete
+     to.  */
+  linespec_complete_what complete_what;
+
+  /* The completion word point.  The parser advances this as it skips
+     tokens.  At some point the input string will end or parsing will
+     fail, and then we attempt completion at the captured completion
+     word point, interpreting the string at completion_word as
+     COMPLETE_WHAT.  */
+  const char *completion_word;
+
+  /* If the current token was a quoted string, then this is the
+     quoting character (either " or ').  */
+  int completion_quote_char;
+
+  /* If the current token was a quoted string, then this points at the
+     end of the quoted string.  */
+  const char *completion_quote_end;
+
+  /* If parsing for completion, then this points at the completion
+     tracker.  Otherwise, this is NULL.  */
+  struct completion_tracker *completion_tracker;
 };
 typedef struct ls_parser linespec_parser;
 
@@ -543,6 +595,30 @@ find_parameter_list_end (const char *input)
   return p;
 }
 
+/* If the [STRING, STRING_LEN) string ends with what looks like a
+   keyword, return the keyword start offset in STRING.  Return -1
+   otherwise.  */
+
+static size_t
+string_find_incomplete_keyword_at_end (const char * const *keywords,
+				       const char *string, size_t string_len)
+{
+  const char *end = string + string_len;
+  const char *p = end;
+
+  while (p > string && *p != ' ')
+    --p;
+  if (p > string)
+    {
+      p++;
+      size_t len = end - p;
+      for (size_t i = 0; keywords[i] != NULL; ++i)
+	if (strncmp (keywords[i], p, len) == 0)
+	  return p - string;
+    }
+
+  return -1;
+}
 
 /* Lex a string from the input in PARSER.  */
 
@@ -590,13 +666,31 @@ linespec_lexer_lex_string (linespec_parser *parser)
       /* Skip to the ending quote.  */
       end = skip_quote_char (PARSER_STREAM (parser), quote_char);
 
-      /* Error if the input did not terminate properly.  */
-      if (end == NULL)
-	error (_("unmatched quote"));
+      /* This helps the completer mode decide whether we have a
+	 complete string.  */
+      parser->completion_quote_char = quote_char;
+      parser->completion_quote_end = end;
 
-      /* Skip over the ending quote and mark the length of the string.  */
-      PARSER_STREAM (parser) = (char *) ++end;
-      LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start;
+      /* Error if the input did not terminate properly, unless in
+	 completion mode.  */
+      if (end == NULL)
+	{
+	  if (parser->completion_tracker == NULL)
+	    error (_("unmatched quote"));
+
+	  /* In completion mode, we'll try to complete the incomplete
+	     token.  */
+	  token.type = LSTOKEN_STRING;
+	  while (*PARSER_STREAM (parser) != '\0')
+	    PARSER_STREAM (parser)++;
+	  LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 1 - start;
+	}
+      else
+	{
+	  /* Skip over the ending quote and mark the length of the string.  */
+	  PARSER_STREAM (parser) = (char *) ++end;
+	  LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start;
+	}
     }
   else
     {
@@ -835,13 +929,48 @@ linespec_lexer_lex_one (linespec_parser *parser)
 }
 
 /* Consume the current token and return the next token in PARSER's
-   input stream.  */
+   input stream.  Also advance the completion word for completion
+   mode.  */
 
 static linespec_token
 linespec_lexer_consume_token (linespec_parser *parser)
 {
+  gdb_assert (parser->lexer.current.type != LSTOKEN_EOI);
+
+  bool advance_word = (parser->lexer.current.type != LSTOKEN_STRING
+		       || *PARSER_STREAM (parser) != '\0');
+
+  /* If we're moving past a string to some other token, it must be the
+     quote was terminated.  */
+  if (parser->completion_quote_char)
+    {
+      gdb_assert (parser->lexer.current.type == LSTOKEN_STRING);
+
+      /* If the string was the last (non-EOI) token, we're past the
+	 quote, but remember that for later.  */
+      if (*PARSER_STREAM (parser) != '\0')
+	{
+	  parser->completion_quote_char = '\0';
+	  parser->completion_quote_end = NULL;;
+	}
+    }
+
   parser->lexer.current.type = LSTOKEN_CONSUMED;
-  return linespec_lexer_lex_one (parser);
+  linespec_lexer_lex_one (parser);
+
+  if (parser->lexer.current.type == LSTOKEN_STRING)
+    {
+      /* Advance the completion word past a potential initial
+	 quote-char.  */
+      parser->completion_word = LS_TOKEN_STOKEN (parser->lexer.current).ptr;
+    }
+  else if (advance_word)
+    {
+      /* Advance the completion word past any whitespace.  */
+      parser->completion_word = PARSER_STREAM (parser);
+    }
+
+  return parser->lexer.current;
 }
 
 /* Return the next token without consuming the current token.  */
@@ -852,10 +981,16 @@ linespec_lexer_peek_token (linespec_parser *parser)
   linespec_token next;
   const char *saved_stream = PARSER_STREAM (parser);
   linespec_token saved_token = parser->lexer.current;
+  int saved_completion_quote_char = parser->completion_quote_char;
+  const char *saved_completion_quote_end = parser->completion_quote_end;
+  const char *saved_completion_word = parser->completion_word;
 
   next = linespec_lexer_consume_token (parser);
   PARSER_STREAM (parser) = saved_stream;
   parser->lexer.current = saved_token;
+  parser->completion_quote_char = saved_completion_quote_char;
+  parser->completion_quote_end = saved_completion_quote_end;
+  parser->completion_word = saved_completion_word;
   return next;
 }
 
@@ -1599,6 +1734,17 @@ source_file_not_found_error (const char *name)
   throw_error (NOT_FOUND_ERROR, _("No source file named %s."), name);
 }
 
+/* Unless at EIO, save the current stream position as completion word
+   point, and consume the next token.  */
+
+static linespec_token
+save_stream_and_consume_token (linespec_parser *parser)
+{
+  if (linespec_lexer_peek_token (parser).type != LSTOKEN_EOI)
+    parser->completion_word = PARSER_STREAM (parser);
+  return linespec_lexer_consume_token (parser);
+}
+
 /* See description in linespec.h.  */
 
 struct line_offset
@@ -1626,6 +1772,26 @@ linespec_parse_line_offset (const char *string)
   return line_offset;
 }
 
+/* In completion mode, if the user is still typing the number, there's
+   no possible completion to offer.  But if there's already input past
+   the number, setup to expect NEXT.  */
+
+static void
+set_completion_after_number (linespec_parser *parser,
+			     linespec_complete_what next)
+{
+  if (*PARSER_STREAM (parser) == ' ')
+    {
+      parser->completion_word = skip_spaces_const (PARSER_STREAM (parser) + 1);
+      parser->complete_what = next;
+    }
+  else
+    {
+      parser->completion_word = PARSER_STREAM (parser);
+      parser->complete_what = linespec_complete_what::NOTHING;
+    }
+}
+
 /* Parse the basic_spec in PARSER's input.  */
 
 static void
@@ -1641,11 +1807,20 @@ linespec_parse_basic (linespec_parser *parser)
   token = linespec_lexer_lex_one (parser);
 
   /* If it is EOI or KEYWORD, issue an error.  */
-  if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI)
-    unexpected_linespec_error (parser);
+  if (token.type == LSTOKEN_KEYWORD)
+    {
+      parser->complete_what = linespec_complete_what::NOTHING;
+      unexpected_linespec_error (parser);
+    }
+  else if (token.type == LSTOKEN_EOI)
+    {
+      unexpected_linespec_error (parser);
+    }
   /* If it is a LSTOKEN_NUMBER, we have an offset.  */
   else if (token.type == LSTOKEN_NUMBER)
     {
+      set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
       /* Record the line offset and get the next token.  */
       name = copy_token_string (token);
       cleanup = make_cleanup (xfree, name);
@@ -1657,7 +1832,10 @@ linespec_parse_basic (linespec_parser *parser)
 
       /* If the next token is a comma, stop parsing and return.  */
       if (token.type == LSTOKEN_COMMA)
-	return;
+	{
+	  parser->complete_what = linespec_complete_what::NOTHING;
+	  return;
+	}
 
       /* If the next token is anything but EOI or KEYWORD, issue
 	 an error.  */
@@ -1670,12 +1848,58 @@ linespec_parse_basic (linespec_parser *parser)
 
   /* Next token must be LSTOKEN_STRING.  */
   if (token.type != LSTOKEN_STRING)
-    unexpected_linespec_error (parser);
+    {
+      parser->complete_what = linespec_complete_what::NOTHING;
+      unexpected_linespec_error (parser);
+    }
 
   /* The current token will contain the name of a function, method,
      or label.  */
-  name  = copy_token_string (token);
-  cleanup = make_cleanup (xfree, name);
+  name = copy_token_string (token);
+  cleanup = make_cleanup (free_current_contents, &name);
+
+  if (parser->completion_tracker != NULL)
+    {
+      /* If the function name ends with a ":", then this may be an
+	 incomplete "::" scope operator instead of a label separator.
+	 E.g.,
+	   "b klass:<tab>"
+	 which should expand to:
+	   "b klass::method()"
+
+	 Do a tentative completion assuming the later.  If we find
+	 completions, advance the stream past the colon token and make
+	 it part of the function name/token.  */
+
+      if (!parser->completion_quote_char
+	  && strcmp (PARSER_STREAM (parser), ":") == 0)
+	{
+	  completion_tracker tmp_tracker;
+	  const char *source_filename
+	    = PARSER_EXPLICIT (parser)->source_filename;
+
+	  linespec_complete_function (tmp_tracker,
+				      parser->completion_word,
+				      source_filename);
+
+	  if (tmp_tracker.have_completions ())
+	    {
+	      PARSER_STREAM (parser)++;
+	      LS_TOKEN_STOKEN (token).length++;
+
+	      xfree (name);
+	      name = savestring (parser->completion_word,
+				 (PARSER_STREAM (parser)
+				  - parser->completion_word));
+	    }
+	}
+
+      PARSER_EXPLICIT (parser)->function_name = name;
+      discard_cleanups (cleanup);
+    }
+  else
+    {
+      /* XXX Reindent before pushing.  */
 
   /* Try looking it up as a function/method.  */
   find_linespec_symbols (PARSER_STATE (parser),
@@ -1735,11 +1959,19 @@ linespec_parse_basic (linespec_parser *parser)
 	  return;
 	}
     }
+    }
+
+  int previous_qc = parser->completion_quote_char;
 
   /* Get the next token.  */
   token = linespec_lexer_consume_token (parser);
 
-  if (token.type == LSTOKEN_COLON)
+  if (token.type == LSTOKEN_EOI)
+    {
+      if (previous_qc && !parser->completion_quote_char)
+	parser->complete_what = linespec_complete_what::KEYWORD;
+    }
+  else if (token.type == LSTOKEN_COLON)
     {
       /* User specified a label or a lineno.  */
       token = linespec_lexer_consume_token (parser);
@@ -1748,17 +1980,56 @@ linespec_parse_basic (linespec_parser *parser)
 	{
 	  /* User specified an offset.  Record the line offset and
 	     get the next token.  */
+	  set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
 	  name = copy_token_string (token);
 	  cleanup = make_cleanup (xfree, name);
 	  PARSER_EXPLICIT (parser)->line_offset
 	    = linespec_parse_line_offset (name);
 	  do_cleanups (cleanup);
 
-	  /* Ge the next token.  */
+	  /* Get the next token.  */
 	  token = linespec_lexer_consume_token (parser);
 	}
+      else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+	{
+	  parser->complete_what = linespec_complete_what::LABEL;
+	}
       else if (token.type == LSTOKEN_STRING)
 	{
+	  parser->complete_what = linespec_complete_what::LABEL;
+
+	  /* If we have text after the label separated by whitespace
+	     (e.g., "b func():lab i<tab>"), don't consider it part of
+	     the label.  In completion mode that should complete to
+	     "if", in normal mode, the 'i' should be treated as
+	     garbage.  */
+	  if (parser->completion_quote_char == '\0')
+	    {
+	      const char *ptr = LS_TOKEN_STOKEN (token).ptr;
+	      for (size_t i = 0; i < LS_TOKEN_STOKEN (token).length; i++)
+		{
+		  if (ptr[i] == ' ')
+		    {
+		      LS_TOKEN_STOKEN (token).length = i;
+		      PARSER_STREAM (parser) = skip_spaces_const (ptr + i + 1);
+		      break;
+		    }
+		}
+	    }
+
+	  if (parser->completion_tracker != NULL)
+	    {
+	      if (PARSER_STREAM (parser)[-1] == ' ')
+		{
+		  parser->completion_word = PARSER_STREAM (parser);
+		  parser->complete_what = linespec_complete_what::KEYWORD;
+		}
+	    }
+	  else
+	    {
+	      /* XXX Reindent before pushing.  */
+
 	  /* Grab a copy of the label's name and look it up.  */
 	  name = copy_token_string (token);
 	  cleanup = make_cleanup (xfree, name);
@@ -1781,8 +2052,10 @@ linespec_parse_basic (linespec_parser *parser)
 				     name);
 	    }
 
+	    }
+
 	  /* Check for a line offset.  */
-	  token = linespec_lexer_consume_token (parser);
+	  token = save_stream_and_consume_token (parser);
 	  if (token.type == LSTOKEN_COLON)
 	    {
 	      /* Get the next token.  */
@@ -2272,11 +2545,15 @@ parse_linespec (linespec_parser *parser, const char *arg)
   struct gdb_exception file_exception = exception_none;
   struct cleanup *cleanup;
 
+  values.nelts = 0;
+  values.sals = NULL;
+
   /* A special case to start.  It has become quite popular for
      IDEs to work around bugs in the previous parser by quoting
      the entire linespec, so we attempt to deal with this nicely.  */
   parser->is_quote_enclosed = 0;
-  if (!is_ada_operator (arg)
+  if (parser->completion_tracker == NULL
+      && !is_ada_operator (arg)
       && strchr (linespec_quote_characters, *arg) != NULL)
     {
       const char *end;
@@ -2293,20 +2570,34 @@ parse_linespec (linespec_parser *parser, const char *arg)
 
   parser->lexer.saved_arg = arg;
   parser->lexer.stream = arg;
+  parser->completion_word = arg;
+  parser->complete_what = linespec_complete_what::FUNCTION;
 
   /* Initialize the default symtab and line offset.  */
   initialize_defaults (&PARSER_STATE (parser)->default_symtab,
 		       &PARSER_STATE (parser)->default_line);
 
   /* Objective-C shortcut.  */
-  values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
-  if (values.sals != NULL)
-    return values;
+  if (parser->completion_tracker == NULL)
+    {
+      values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
+      if (values.sals != NULL)
+	return values;
+    }
+  else
+    {
+      /* "-"/"+" is either an objc selector, or a number.  There's
+	 nothing to complete the latter to, so just let the caller
+	 complete on functions, which finds objc selectors, if there's
+	 any.  */
+      if ((arg[0] == '-' || arg[0] == '+') && arg[1] == '\0')
+	return {};
+    }
 
   /* Start parsing.  */
 
   /* Get the first token.  */
-  token = linespec_lexer_lex_one (parser);
+  token = linespec_lexer_consume_token (parser);
 
   /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER.  */
   if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$')
@@ -2314,7 +2605,8 @@ parse_linespec (linespec_parser *parser, const char *arg)
       char *var;
 
       /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
-      VEC_safe_push (symtab_ptr, PARSER_RESULT (parser)->file_symtabs, NULL);
+      if (parser->completion_tracker == NULL)
+	VEC_safe_push (symtab_ptr, PARSER_RESULT (parser)->file_symtabs, NULL);
 
       /* User specified a convenience variable or history value.  */
       var = copy_token_string (token);
@@ -2333,8 +2625,16 @@ parse_linespec (linespec_parser *parser, const char *arg)
 	  goto convert_to_sals;
 	}
     }
+  else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+    {
+      /* Let the default linespec_complete_what::FUNCTION kick in.  */
+      unexpected_linespec_error (parser);
+    }
   else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER)
-    unexpected_linespec_error (parser);
+    {
+      parser->complete_what = linespec_complete_what::NOTHING;
+      unexpected_linespec_error (parser);
+    }
 
   /* Shortcut: If the next token is not LSTOKEN_COLON, we know that
      this token cannot represent a filename.  */
@@ -2382,8 +2682,9 @@ parse_linespec (linespec_parser *parser, const char *arg)
 	}
     }
   /* If the next token is not EOI, KEYWORD, or COMMA, issue an error.  */
-  else if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD
-	   && token.type != LSTOKEN_COMMA)
+  else if (parser->completion_tracker == NULL
+	   && (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD
+	       && token.type != LSTOKEN_COMMA))
     {
       /* TOKEN is the _next_ token, not the one currently in the parser.
 	 Consuming the token will give the correct error message.  */
@@ -2399,7 +2700,8 @@ parse_linespec (linespec_parser *parser, const char *arg)
   /* Parse the rest of the linespec.  */
   linespec_parse_basic (parser);
 
-  if (PARSER_RESULT (parser)->function_symbols == NULL
+  if (parser->completion_tracker == NULL
+      && PARSER_RESULT (parser)->function_symbols == NULL
       && PARSER_RESULT (parser)->labels.label_symbols == NULL
       && PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN
       && PARSER_RESULT (parser)->minimal_symbols == NULL)
@@ -2420,11 +2722,21 @@ parse_linespec (linespec_parser *parser, const char *arg)
      if necessary.  */
   token = linespec_lexer_lex_one (parser);
   if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD)
-    PARSER_STREAM (parser) = LS_TOKEN_STOKEN (token).ptr;
+    unexpected_linespec_error (parser);
+  else if (token.type == LSTOKEN_KEYWORD)
+    {
+      /* Setup the completion word past the keyword.  Lexing never
+	 advances past a keyword automatically, so skip it
+	 manually.  */
+      parser->completion_word
+	= skip_spaces_const (skip_to_space_const (PARSER_STREAM (parser)));
+      parser->complete_what = linespec_complete_what::EXPRESSION;
+    }
 
   /* Convert the data in PARSER_RESULT to SALs.  */
-  values = convert_linespec_to_sals (PARSER_STATE (parser),
-				     PARSER_RESULT (parser));
+  if (parser->completion_tracker == NULL)
+    values = convert_linespec_to_sals (PARSER_STATE (parser),
+				       PARSER_RESULT (parser));
 
   return values;
 }
@@ -2562,6 +2874,67 @@ linespec_complete_function (completion_tracker &tracker,
     collect_symbol_completion_matches (tracker, mode, function, function);
 }
 
+/* Helper for complete_linespec to simplify it.  SOURCE_FILENAME is
+   only meaningful if COMPONENT is FUNCTION.  */
+
+static void
+complete_linespec_component (linespec_parser *parser,
+			     completion_tracker &tracker,
+			     const char *text,
+			     linespec_complete_what component,
+			     const char *source_filename)
+{
+  if (component == linespec_complete_what::KEYWORD)
+    {
+      complete_on_enum (tracker, linespec_keywords, text, text);
+    }
+  else if (component == linespec_complete_what::EXPRESSION)
+    {
+      const char *word
+	= advance_to_expression_complete_word_point (tracker, text);
+      complete_expression (tracker, text, word);
+    }
+  else if (component == linespec_complete_what::FUNCTION)
+    {
+      completion_list fn_list;
+
+      linespec_complete_function (tracker, text, source_filename);
+      if (source_filename == NULL)
+	{
+	  /* Haven't seen a source component, like in "b
+	     file.c:function[TAB]".  Maybe this wasn't a function, but
+	     a filename instead, like "b file.[TAB]".  */
+	  fn_list = complete_source_filenames (text);
+	}
+
+      /* If we only have a single filename completion, append a ':' for
+	 the user, since that's the only thing that can usefully follow
+	 the filename.  */
+      if (fn_list.size () == 1 && !tracker.have_completions ())
+	{
+	  char *fn = fn_list[0].release ();
+
+	  /* If we also need to append a quote char, it needs to be
+	     appended before the ':'.  Append it now, and make ':' the
+	     new "quote" char.  */
+	  if (tracker.quote_char ())
+	    {
+	      char quote_char_str[2] = { tracker.quote_char () };
+
+	      fn = reconcat (fn, fn, quote_char_str, (char *) NULL);
+	      tracker.set_quote_char (':');
+	    }
+	  else
+	    fn = reconcat (fn, fn, ":", (char *) NULL);
+	  fn_list[0].reset (fn);
+
+	  /* Tell readline to skip appending a space.  */
+	  tracker.set_suppress_append_ws (true);
+	}
+      tracker.add_completions (std::move (fn_list));
+    }
+}
+
 /* Helper for linespec_complete_label.  Find labels that match
    LABEL_NAME in the function symbols listed in the PARSER, and add
    them to the tracker.  */
@@ -2625,6 +2998,201 @@ linespec_complete_label (completion_tracker &tracker,
   do_cleanups (cleanup);
 }
 
+/* See description in linespec.h.  */
+
+void
+linespec_complete (completion_tracker &tracker, const char *text)
+{
+  linespec_parser parser;
+  struct cleanup *cleanup;
+  const char *orig = text;
+
+  linespec_parser_new (&parser, 0, current_language, NULL, NULL, 0, NULL);
+  cleanup = make_cleanup (linespec_parser_delete, &parser);
+  parser.lexer.saved_arg = text;
+  PARSER_STREAM (&parser) = text;
+
+  parser.completion_tracker = &tracker;
+  PARSER_STATE (&parser)->is_linespec = 1;
+
+  /* Parse as much as possible.  parser.completion_word will hold
+     furthest completion point we managed to parse to.  */
+  TRY
+    {
+      parse_linespec (&parser, text);
+    }
+  CATCH (except, RETURN_MASK_ERROR)
+    {
+    }
+  END_CATCH
+
+  if (parser.completion_quote_char != '\0'
+      && parser.completion_quote_end != NULL
+      && parser.completion_quote_end[1] == '\0')
+    {
+      /* If completing a quoted string with the cursor right at
+	 terminating quote char, complete the completion word without
+	 interpretation, so that readline advances the cursor one
+	 whitespace past the quote, even if there's no match.  This
+	 makes these cases behave the same:
+
+	   before: "b function()"
+	   after:  "b function() "
+
+	   before: "b 'function()'"
+	   after:  "b 'function()' "
+
+	 and trusts the user in this case:
+
+	   before: "b 'not_loaded_function_yet()'"
+	   after:  "b 'not_loaded_function_yet()' "
+      */
+      parser.complete_what = linespec_complete_what::NOTHING;
+      parser.completion_quote_char = '\0';
+
+      gdb::unique_xmalloc_ptr<char> text_copy
+	(xstrdup (parser.completion_word));
+      tracker.add_completion (std::move (text_copy));
+    }
+
+  tracker.set_quote_char (parser.completion_quote_char);
+
+  if (parser.complete_what == linespec_complete_what::LABEL)
+    {
+      parser.complete_what = linespec_complete_what::NOTHING;
+
+      const char *func_name = PARSER_EXPLICIT (&parser)->function_name;
+
+      VEC (symbolp) *function_symbols;
+      VEC (bound_minimal_symbol_d) *minimal_symbols;
+      find_linespec_symbols (PARSER_STATE (&parser),
+			     PARSER_RESULT (&parser)->file_symtabs,
+			     func_name,
+			     &function_symbols, &minimal_symbols);
+
+      PARSER_RESULT (&parser)->function_symbols = function_symbols;
+      PARSER_RESULT (&parser)->minimal_symbols = minimal_symbols;
+
+      complete_label (tracker, &parser, parser.completion_word);
+    }
+  else if (parser.complete_what == linespec_complete_what::FUNCTION)
+    {
+      /* While parsing/lexing, we didn't know whether the completion
+	 word completes to a unique function/source name already or
+	 not.
+
+	 E.g.:
+	   "b function() <tab>"
+	 may need to complete either to:
+	   "b function() const"
+	 or to:
+	   "b function() if/thread/task"
+
+	 Or, this:
+	   "b foo t"
+	 may need to complete either to:
+	   "b foo template_fun<T>()"
+	 with "foo" being the template function's return type, or to:
+	   "b foo thread/task"
+
+	 Or, this:
+	   "b file<TAB>"
+	 may need to complete either to a source file name:
+	   "b file.c"
+	 or this, also a filename, but a unique completion:
+	   "b file.c:"
+	 or to a function name:
+	   "b file_function"
+
+	 Address that by completing assuming source or function, and
+	 seeing if we find a completion that matches exactly the
+	 completion word.  If so, then it must be a function (see note
+	 below) and we advance the completion word to the end of input
+	 and switch to KEYWORD completion mode.
+
+	 Note: if we find a unique completion for a source filename,
+	 then it won't match the completion word, because the LCD will
+	 contain a trailing ':'.  And if we're completing at or after
+	 the ':', then complete_linespec_component won't try to
+	 complete on source filenames.  */
+
+      const char *text = parser.completion_word;
+      const char *word = parser.completion_word;
+
+      complete_linespec_component (&parser, tracker,
+				   parser.completion_word,
+				   linespec_complete_what::FUNCTION,
+				   PARSER_EXPLICIT (&parser)->source_filename);
+
+      parser.complete_what = linespec_complete_what::NOTHING;
+
+      if (tracker.quote_char ())
+	{
+	  /* The function/file name was not close-quoted, so this
+	     can't be a keyword.  Note: complete_linespec_component
+	     may have swapped the original quote char for ':' when we
+	     get here, but that still indicates the same.  */
+	}
+      else if (!tracker.have_completions ())
+	{
+	  size_t key_start;
+	  size_t wordlen = strlen (parser.completion_word);
+
+	  key_start
+	    = string_find_incomplete_keyword_at_end (linespec_keywords,
+						     parser.completion_word,
+						     wordlen);
+
+	  if (key_start != -1
+	      || (wordlen > 0
+		  && parser.completion_word[wordlen - 1] == ' '))
+	    {
+	      parser.completion_word += key_start;
+	      parser.complete_what = linespec_complete_what::KEYWORD;
+	    }
+	}
+      else if (tracker.completes_to_completion_word (word))
+	{
+	  /* Skip the function and complete on keywords.  */
+	  parser.completion_word += strlen (word);
+	  parser.complete_what = linespec_complete_what::KEYWORD;
+	  tracker.discard_completions ();
+	}
+    }
+
+  tracker.advance_custom_word_point_by (parser.completion_word - orig);
+
+  complete_linespec_component (&parser, tracker,
+			       parser.completion_word,
+			       parser.complete_what,
+			       PARSER_EXPLICIT (&parser)->source_filename);
+
+  /* If we're past the "filename:function:label:offset" linespec, and
+     didn't find any match, then assume the user might want to create
+     a pending breakpoint anyway and offer the keyword
+     completions.  */
+  if (!parser.completion_quote_char
+      && (parser.complete_what == linespec_complete_what::FUNCTION
+	  || parser.complete_what == linespec_complete_what::LABEL
+	  || parser.complete_what == linespec_complete_what::NOTHING)
+      && !tracker.have_completions ())
+    {
+      const char *end
+	= parser.completion_word + strlen (parser.completion_word);
+
+      if (end > orig && end[-1] == ' ')
+	{
+	  tracker.advance_custom_word_point_by (end - parser.completion_word);
+
+	  complete_linespec_component (&parser, tracker, end,
+				       linespec_complete_what::KEYWORD,
+				       NULL);
+	}
+    }
+
+  do_cleanups (cleanup);
+}
+
 /* A helper function for decode_line_full and decode_line_1 to
    turn LOCATION into symtabs_and_lines.  */
 
diff --git a/gdb/linespec.h b/gdb/linespec.h
index f16bb81..27d237a 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -186,6 +186,11 @@ extern void linespec_lex_to_end (char **stringp);
 
 extern const char * const linespec_keywords[];
 
+/* Complete a linespec.  */
+
+extern void linespec_complete (completion_tracker &tracker,
+			       const char *text);
+
 /* Complete a function symbol, in linespec mode.  If SOURCE_FILENAME
    is non-NULL, limits completion to the list of functions defined in
    source files that match SOURCE_FILENAME.  */
diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
index 6597ea7..f03bfc3 100644
--- a/gdb/testsuite/gdb.base/completion.exp
+++ b/gdb/testsuite/gdb.base/completion.exp
@@ -790,7 +790,7 @@ gdb_test_multiple "" $test {
     -re "break\.c.*break1\.c.*$gdb_prompt " {
 	send_gdb "1\t\n"
 	gdb_test_multiple "" $test {
-	    -re ".*Function \"$srcfile2\" not defined\..*$gdb_prompt " {
+	    -re "malformed linespec error: unexpected end of input\r\n$gdb_prompt " {
 		pass $test
 	    }
 	    -re "$gdb_prompt p$" {
diff --git a/gdb/testsuite/gdb.linespec/ls-errs.exp b/gdb/testsuite/gdb.linespec/ls-errs.exp
index 7942f1f..f55cd0e8 100644
--- a/gdb/testsuite/gdb.linespec/ls-errs.exp
+++ b/gdb/testsuite/gdb.linespec/ls-errs.exp
@@ -96,8 +96,13 @@ proc do_test {lang} {
     }
 
     # Some commonly used whitespace tests around ':'.
-    set spaces [list ":" ": " " :" " : " "\t:  " "  :\t" "\t:\t" \
-		     " \t:\t " "\t  \t:\t  \t  \t"]
+    set spaces [list \
+		    ":" \
+		    ": " \
+		    " :" \
+		    " : " \
+		    "  :  " \
+		   ]
 
     # A list of invalid offsets.
     set invalid_offsets [list -100 +500 1000]
-- 
2.5.5

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

* Re: [PATCH 17/40] Linespec lexing and C++ operators
  2017-07-14 21:45   ` Keith Seitz
@ 2017-07-17 19:34     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 19:34 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/14/2017 10:45 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:

>>
>>  (gdb) b -function operator<(int, int) -labe[TAB]    # nothing happens
>>
>> gdb incorrectly thinks "-labe" is part of the "unclosed" template
>> parameter list started with "<".
> 
> Ouch. The best laid plans...

:-)

>> +		    p--;
>> +		  if (p - start >= CP_OPERATOR_LEN)
>> +		    {
>> +		      p-= CP_OPERATOR_LEN;
> 
> This is a cut-n-paste-o (that's a typo propagated via cut-n-paste). Missing a space before the operator.

Whoops.

>> +		  LS_TOKEN_STOKEN (token).ptr = start;
>> +		  LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;
> 
> Line length > 80?

Indeed.

Pushed with the obvious fixes.

Thanks,
Pedro Alves

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

* Re: [PATCH 20/40] Eliminate block_iter_name_*
  2017-06-02 12:30 ` [PATCH 20/40] Eliminate block_iter_name_* Pedro Alves
@ 2017-07-17 19:47   ` Keith Seitz
  2017-07-20 17:05     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-17 19:47 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> This patch gets rid of block_iter_name_* as being unnecessary.  It's
> the same as calling block_iter_match_*, and passing strcmp_iw as
> comparison routine.

I was curious how this situation arose. Looking at the archives, I think it safe to say that someone was playing it safe when extending the interfaces for Ada. [I certainly don't blame people for playing it safe. I do it all the time.]

So, LGTM.

Keith



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

* Re: [PATCH 19/40] Fix cp_find_first_component_aux bug
  2017-07-17 19:17   ` Keith Seitz
@ 2017-07-17 19:50     ` Pedro Alves
  2017-07-17 21:38       ` Keith Seitz
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-17 19:50 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


-- 
Thanks,
Pedro Alves
On 07/17/2017 08:17 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* cp-support.c (cp_find_first_component_aux): Add missing case for
>> 	end of string.
> 
> That's pretty obvious, even to me. Do we a test case would add anything? /me not so sure [but then it would be rather trivial to write, eh?]

There's a bunch later in the series in:
  [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests

(that's how I spotted the bug; gdb sometimes failed those tests.)

To confirm I added:

 		case '\0':
 +		  gdb_assert (0);
 		  return index;

on top of the whole series, and reran that testcase:

 Running src/gdb/testsuite/gdb.linespec/cpls-ops.exp ...
 FAIL: gdb.linespec/cpls-ops.exp: operator-delete: "b test_op_delete::operator" creates no bp locations: set breakpoint (GDB internal error)
 FAIL: gdb.linespec/cpls-ops.exp: operator-delete: "b test_op_delete::operator" creates no bp locations: matches
 FAIL: gdb.linespec/cpls-ops.exp: operator-delete: "b -function test_op_delete::operator" creates no bp locations: set breakpoint (GDB internal error)
 FAIL: gdb.linespec/cpls-ops.exp: operator-delete: "b -function test_op_delete::operator" creates no bp locations: matches
 FAIL: gdb.linespec/cpls-ops.exp: operator-delete[]: "b test_op_delete_array::operator" creates no bp locations: set breakpoint (GDB internal error)
(...)

Unfortunately, that testcase won't work yet as is, until a few
patches more down the series (I think patch #34, even).  With
patch #19 alone:

FAIL: gdb.linespec/cpls-ops.exp: operator-delete: tab complete "b test_op_delete::operator delete " (timeout)
FAIL: gdb.linespec/cpls-ops.exp: operator-delete: cmd complete "b test_op_delete::operator delete "
FAIL: gdb.linespec/cpls-ops.exp: operator-delete: tab complete "b test_op_delete::operator delete  (" (timeout)
FAIL: gdb.linespec/cpls-ops.exp: operator-delete: cmd complete "b test_op_delete::operator delete  ("
FAIL: gdb.linespec/cpls-ops.exp: operator-delete: tab complete "b test_op_delete::operator delete  ( void* " (timeout)
FAIL: gdb.linespec/cpls-ops.exp: operator-delete: cmd complete "b test_op_delete::operator delete  ( void* "
FAIL: gdb.linespec/cpls-ops.exp: operator-delete: tab complete "b test_op_delete::operator delete  ( void * " (timeout)
FAIL: gdb.linespec/cpls-ops.exp: operator-delete: cmd complete "b test_op_delete::operator delete  ( void * "
(...)

Would you be OK with adding the fix without the test yet?

Thanks,
Pedro Alves

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

* Re: [PATCH 19/40] Fix cp_find_first_component_aux bug
  2017-07-17 19:50     ` Pedro Alves
@ 2017-07-17 21:38       ` Keith Seitz
  2017-07-20 17:03         ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-17 21:38 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 07/17/2017 12:50 PM, Pedro Alves wrote:
> Would you be OK with adding the fix without the test yet?

Sure.

Keith

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

* Re: [PATCH 21/40] Use SYMBOL_MATCHES_SEARCH_NAME some more
  2017-06-02 12:29 ` [PATCH 21/40] Use SYMBOL_MATCHES_SEARCH_NAME some more Pedro Alves
@ 2017-07-17 21:39   ` Keith Seitz
  2017-07-20 17:08     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-17 21:39 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* dwarf2read.c (dw2_lookup_symbol): Use
> 	SYMBOL_MATCHES_SEARCH_NAME.
> 	* psymtab.c (psym_lookup_symbol): Use SYMBOL_MATCHES_SEARCH_NAME.

I'd say that falls under the "obvious" banner, no? ;-)

Keith

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

* Re: [PATCH 22/40] get_int_var_value
  2017-06-02 12:31 ` [PATCH 22/40] get_int_var_value Pedro Alves
@ 2017-07-17 22:11   ` Keith Seitz
  2017-07-20 17:15     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-17 22:11 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> I noticed that get_int_var_value's parameters could use some
> constification.  And then realized that client code would become
> simpler by changing the interface to return the success/failure
> indication as actual return value, as allows getting rid of the the
> local "boolean" variable.

It is certainly more appropriate for Joel to comment, but I did look through the patch, and it is a pretty straightforward change. I didn't notice any issues.

Keith

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

* Re: [PATCH 23/40] Make language_def O(1)
  2017-06-02 12:39 ` [PATCH 23/40] Make language_def O(1) Pedro Alves
@ 2017-07-17 23:03   ` Keith Seitz
  2017-07-20 17:40     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-17 23:03 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> IMO, the add_language mechanism is pointless, because "enum language"
> implies the core of GDB needs to know about all languages anyway.

I don't know how others feel about it, but since I am giving feedback on this patch, I will say that I agree with you.

Now, if there was a mechanism by which we could dynamically add languages, add_language would make much more sense.

> Note that "local_language_defn" is gone along the way.  AFAICT, it's
> just a copy of "auto", so the new code simply maps one to the other.
> One fewer place to update when we need to change the language
> vector...

I've searched the manual, and the "local" language is mentioned only in two places, once in the description of "set language" (where it explains that "local" and "auto" are the same) and once when we describe -data-evaluate-expression's --language flag.

Honestly, is there any reason to keep it at all given it is a synonym of auto? [I realize that it doesn't cost much to maintain with this patch.] I'm not asking for any changes in this regard, just throwing the possibility out there. TBH, I didn't even know "local" existed.

Just two trivial nits.

> diff --git a/gdb/language.c b/gdb/language.c
> index d30f4f0..df4f3cd 100644
> --- a/gdb/language.c
> +++ b/gdb/language.c
> @@ -530,55 +522,51 @@ show_check (char *ignore, int from_tty)
>    cmd_show_list (showchecklist, from_tty, "");
>  }
>  \f
> -/* Add a language to the set of known languages.  */
>  
> -void
> -add_language (const struct language_defn *lang)
> +/* Compare C strings for std::sort.  */
> +
> +static bool
> +compare_cstrings (const char *str1, const char *str2)
>  {
> -  /* For the "set language" command.  */
> -  static const char **language_names = NULL;
> -  /* For the "help set language" command.  */
> +  return strcmp (str1, str2) < 0;
> +}

Doesn't completer.c define the same function? Perhaps move to utils.c? I would guess this is not the last time we will want to do this "sort" of thing. :-)

>  
> -  if (lang->la_magic != LANG_MAGIC)
> -    {
> -      fprintf_unfiltered (gdb_stderr,
> -			  "Magic number of %s language struct wrong\n",
> -			  lang->la_name);
> -      internal_error (__FILE__, __LINE__,
> -		      _("failed internal consistency check"));
> -    }
> +static void
> +build_language_commands ()
> +{

missing comment<cough/>

> diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
> index 8c0b29c..c25069a 100644
> --- a/gdb/testsuite/gdb.base/default.exp
> +++ b/gdb/testsuite/gdb.base/default.exp
> @@ -511,7 +511,7 @@ gdb_test "set history size" "Argument required .integer to set it to.*" "set his
>  #test set history
>  gdb_test "set history" "\"set history\" must be followed by the name of a history subcommand.(\[^\r\n\]*\[\r\n\])+List of set history subcommands:(\[^\r\n\]*\[\r\n\])+set history expansion -- Set history expansion on command input(\[^\r\n\]*\[\r\n\])+set history filename -- Set the filename in which to record the command history(\[^\r\n\]*\[\r\n\])+set history save -- Set saving of the history record on exit(\[^\r\n\]*\[\r\n\])+set history size -- Set the size of the command history(\[^\r\n\]*\[\r\n\])+Type \"help set history\" followed by set history subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set history"
>  #test set language
> -gdb_test "set language" "Requires an argument. Valid arguments are ada, c, c.., asm, minimal, d, fortran, go, auto, local, unknown, modula-2, objective-c, opencl, pascal, rust." "set language"
> +gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, asm, c, c.., d, fortran, go, minimal, modula-2, objective-c, opencl, pascal, rust." "set language"

Nice catch.

Otherwise, LGTM.

Keith

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

* Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction
  2017-07-17 13:56     ` Pedro Alves
@ 2017-07-18  8:23       ` Christophe Lyon
  2017-07-18  9:04         ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Christophe Lyon @ 2017-07-18  8:23 UTC (permalink / raw)
  To: gdb-patches

On 17/07/2017 15:56, Pedro Alves wrote:
> On 07/14/2017 06:23 PM, Keith Seitz wrote:
>> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>>> Adds a new "completion_tracker" class that is meant to hold everything
>>> about the state of the current completion operation.
>> This is quite close to the approach I attempted in the series I submitted (cough) in 2015.
> Yeah, I'm sorry about that.  As you know, I wasn't involved
> in that review back then, and I had assumed master already contained
> all the patches you had had back then...  After you pointed me at them,
> I considered rebasing on top of them.   But since your patches (naturally)
> were still using VEC (since they predated C++), we'd end touching/redoing
> the same exact same code throughout twice, to work with the
> completion_tracker_t methods.  :-/
>
>> Just have a few minor comments (about comments!).
>>
>>> diff --git a/gdb/completer.c b/gdb/completer.c
>>> index fe69faa..c6e1e28 100644
>>> --- a/gdb/completer.c
>>> +++ b/gdb/completer.c
>>> @@ -429,10 +420,10 @@ backup_text_ptr (const char *p, const char *text)
>>>   /* A completer function for explicit locations.  This function
>>>      completes both options ("-source", "-line", etc) and values.  */
>>>   
>>> -static VEC (char_ptr) *
>>> -explicit_location_completer (struct cmd_list_element *ignore,
>>> -			     struct event_location *location,
>>> -			     const char *text, const char *word)
>>> +static void
>>> +complete_explicit_location (completion_tracker &tracker,
>>> +			    struct event_location *location,
>>> +			    const char *text, const char *word)
>>>   {
>>>     const char *p;
>>>     VEC (char_ptr) *matches = NULL;
>> `matches' is no longer used. [I realize this will disappear in a later patch.]
>>
>>>   /* See completer.h.  */
>>>   
>>> -enum maybe_add_completion_enum
>>> -maybe_add_completion (completion_tracker_t tracker, char *name)
>>> +bool
>>> +completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
>>>   {
>> [snip]
>>
>>>   
>>> -  return (htab_elements (tracker) < max_completions
>>> -	  ? MAYBE_ADD_COMPLETION_OK
>>> -	  : MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
>>> +void
>>> +completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
>>> +{
>>> +  if (!maybe_add_completion (std::move (name)))
>>> +    throw_max_completions_reached_error ();
>>>   }
>>>   
>>>   void
>>> @@ -1075,10 +1075,16 @@ throw_max_completions_reached_error (void)
>>>     throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
>>>   }
>>>   
>>> -/* Generate completions all at once.  Returns a vector of unique strings
>>> -   allocated with xmalloc.  Returns NULL if there are no completions
>>> -   or if max_completions is 0.  If max_completions is non-negative, this will
>>> -   return at most max_completions strings.
>>> +void
>>> +completion_tracker::add_completions (completion_list &&list)
>>> +{
>>> +  for (auto &candidate : list)
>>> +    add_completion (std::move (candidate));
>>> +}
>> Some of the above methods have comments (per convention, "See XYZ.h"), some do not.  [There are a bunch more methods with no comments below this, too.]
>>
>> Is this requirement being relaxed for C++? I certainly wouldn't mind if we started assuming "See XYZ.h" for all methods, but I don't think convention has been discussed/codified yet.
> Indeed, I don't think it's been discussed.  For now, I added the
> comments, but I wouldn't mind relaxing either.
>
>>> +
>>> +/* Build a new C string that is a copy or LCD with the whitespace of
>>> +   ORIG/ORIG_LEN preserved.
>> Is this supposed to be "a copy *of* LCD"?
> Indeed.  an "inline line" in "we want to end up with an input line
> like" was supposed to be "input line".  Gah.
>
>>> +
>>> +/* Helper for gdb_rl_attempted_completion_function, which does most of
>>> +   the work.  This is called by readline to build the match list
>>> +   array, and determining the lowest common denominator.  The real
>> This last sentence isn't right. At its simplest, it says, "This is called, and determining the lowest common denominator." Is that supposed to be, "This is called to build.. and to determine"?
> Yup, thanks.
>
>>> diff --git a/gdb/symtab.c b/gdb/symtab.c
>>> index 09c9411b..cd78a16 100644
>>> --- a/gdb/symtab.c
>>> +++ b/gdb/symtab.c
>>>   
>>>   /* Return a vector of all symbols (regardless of class) which begin by
>>>      matching TEXT.  If the answer is no symbols, then the return value
>>>      is NULL.  */
>> This comment needs updating ("Return a vector...").
> Did that now.
>
>>>   
>>> -VEC (char_ptr) *
>>> -make_symbol_completion_list (const char *text, const char *word)
>>> +void
>>> +collect_symbol_completion_matches (completion_tracker &tracker,
>>> +				   const char *text, const char *word)
>>>   {
>>> -  return current_language->la_make_symbol_completion_list (text, word,
>>> -							   TYPE_CODE_UNDEF);
>>> +  current_language->la_collect_symbol_completion_matches (tracker,
>>> +							  text, word,
>>> +							  TYPE_CODE_UNDEF);
>>>   }
>>>   
>>> -/* Like make_symbol_completion_list, but only return STRUCT_DOMAIN
>>> -   symbols whose type code is CODE.  */
>>> +/* Like collect_symbol_completion_matches, but only return
>>> +   STRUCT_DOMAIN symbols whose type code is CODE.  */
>>>   
>> s/return/collect/ ?
> Fixed.
>
>>> -VEC (char_ptr) *
>>> -make_symbol_completion_type (const char *text, const char *word,
>>> -			     enum type_code code)
>>> +void
>>> +collect_symbol_completion_matches_type (completion_tracker &tracker,
>>> +					const char *text, const char *word,
>>> +					enum type_code code)
>>>   {
>>>     gdb_assert (code == TYPE_CODE_UNION
>>>   	      || code == TYPE_CODE_STRUCT
>>>   	      || code == TYPE_CODE_ENUM);
>>> -  return current_language->la_make_symbol_completion_list (text, word, code);
>>> +  current_language->la_collect_symbol_completion_matches (tracker,
>>> +							  text, word, code);
>>>   }
>>>   
>>> @@ -5503,17 +5419,16 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
>>>   
>>>   /* Return a vector of all source files whose names begin with matching
>>>      TEXT.  The file names are looked up in the symbol tables of this
>>> -   program.  If the answer is no matchess, then the return value is
>>> -   NULL.  */
>>> +   program.  */
>> This comment also needs updating ("Return a vector...").
>>
> Thanks much Keith.  I pushed the patch with the below squashed in.
>
> (While adding the missing comments I noticed that
> throw_max_completions_reached_error could be static, and then
> since there's only one caller, I inlined it.)

Hi Pedro,

Since you committed this patch, I've noticed that gdb fails to builds for some targets, such as arm-none-eabi (build is OK for arm-linux-gnueabi)

The build log says:

../../../gdb/remote-sim.c: In function 'void _initialize_remote_sim()':
../../../gdb/remote-sim.c:1350:46: error: invalid conversion from 'VEC_char_ptr* (*)(cmd_list_element*, const char*, const char*)' to 'void (*)(cmd_list_element*, completion_tracker&, const char*, const char*)' [-fpermissive]
    set_cmd_completer (c, sim_command_completer);
                                               ^
In file included from ../../../gdb/completer.h:21:0,
                  from ../../../gdb/symtab.h:28,
                  from ../../../gdb/language.h:26,
                  from ../../../gdb/frame.h:72,
                  from ../../../gdb/gdbarch.h:39,
                  from ../../../gdb/defs.h:636,
                  from ../../../gdb/remote-sim.c:23:
../../../gdb/command.h:197:13: error:   initializing argument 2 of 'void set_cmd_completer(cmd_list_element*, void (*)(cmd_list_element*, completion_tracker&, const char*, const char*))' [-fpermissive]
  extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
make[2]: *** [remote-sim.o] Error 1

Can you have a look?

Thanks

> Thanks,
> Pedro Alves
>
> diff --git i/gdb/completer.c w/gdb/completer.c
> index c6e1e28..85e6d88 100644
> --- i/gdb/completer.c
> +++ w/gdb/completer.c
> @@ -426,7 +426,6 @@ complete_explicit_location (completion_tracker &tracker,
>   			    const char *text, const char *word)
>   {
>     const char *p;
> -  VEC (char_ptr) *matches = NULL;
>   
>     /* Find the beginning of the word.  This is necessary because
>        we need to know if we are completing an option name or value.  We
> @@ -1029,6 +1028,8 @@ completion_tracker::completion_tracker ()
>   				      NULL, xcalloc, xfree);
>   }
>   
> +/* See completer.h.  */
> +
>   completion_tracker::~completion_tracker ()
>   {
>     xfree (m_lowest_common_denominator);
> @@ -1062,18 +1063,16 @@ completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
>     return true;
>   }
>   
> +/* See completer.h.  */
> +
>   void
>   completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
>   {
>     if (!maybe_add_completion (std::move (name)))
> -    throw_max_completions_reached_error ();
> +    throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
>   }
>   
> -void
> -throw_max_completions_reached_error (void)
> -{
> -  throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
> -}
> +/* See completer.h.  */
>   
>   void
>   completion_tracker::add_completions (completion_list &&list)
> @@ -1337,7 +1336,7 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
>       }
>   }
>   
> -/* Build a new C string that is a copy or LCD with the whitespace of
> +/* Build a new C string that is a copy of LCD with the whitespace of
>      ORIG/ORIG_LEN preserved.
>   
>      Say the user is completing a symbol name, with spaces, like:
> @@ -1348,7 +1347,7 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
>   
>        "foo(int)"
>   
> -   we want to end up with an inline line like:
> +   we want to end up with an input line like:
>   
>        "foo ( int)"
>         ^^^^^^^      => text from LCD [1], whitespace from ORIG preserved.
> @@ -1402,6 +1401,8 @@ expand_preserving_ws (const char *orig, size_t orig_len,
>     return xstrdup (res.c_str ());
>   }
>   
> +/* See completer.h.  */
> +
>   completion_result
>   completion_tracker::build_completion_result (const char *text,
>   					     int start, int end)
> @@ -1443,11 +1444,15 @@ completion_tracker::build_completion_result (const char *text,
>       }
>   }
>   
> +/* See completer.h  */
> +
>   completion_result::completion_result ()
>     : match_list (NULL), number_matches (0),
>       completion_suppress_append (false)
>   {}
>   
> +/* See completer.h  */
> +
>   completion_result::completion_result (char **match_list_,
>   				      size_t number_matches_,
>   				      bool completion_suppress_append_)
> @@ -1456,11 +1461,15 @@ completion_result::completion_result (char **match_list_,
>       completion_suppress_append (completion_suppress_append_)
>   {}
>   
> +/* See completer.h  */
> +
>   completion_result::~completion_result ()
>   {
>     reset_match_list ();
>   }
>   
> +/* See completer.h  */
> +
>   completion_result::completion_result (completion_result &&rhs)
>   {
>     if (this == &rhs)
> @@ -1473,6 +1482,8 @@ completion_result::completion_result (completion_result &&rhs)
>     rhs.number_matches = 0;
>   }
>   
> +/* See completer.h  */
> +
>   char **
>   completion_result::release_match_list ()
>   {
> @@ -1489,6 +1500,8 @@ compare_cstrings (const char *str1, const char *str2)
>     return strcmp (str1, str2) < 0;
>   }
>   
> +/* See completer.h  */
> +
>   void
>   completion_result::sort_match_list ()
>   {
> @@ -1502,6 +1515,8 @@ completion_result::sort_match_list ()
>       }
>   }
>   
> +/* See completer.h  */
> +
>   void
>   completion_result::reset_match_list ()
>   {
> @@ -1515,9 +1530,9 @@ completion_result::reset_match_list ()
>   }
>   
>   /* Helper for gdb_rl_attempted_completion_function, which does most of
> -   the work.  This is called by readline to build the match list
> -   array, and determining the lowest common denominator.  The real
> -   matches list starts at match[1], while match[0] is the slot holding
> +   the work.  This is called by readline to build the match list array
> +   and to determine the lowest common denominator.  The real matches
> +   list starts at match[1], while match[0] is the slot holding
>      readline's idea of the lowest common denominator of all matches,
>      which is what readline replaces the completion "word" with.
>   
> diff --git i/gdb/completer.h w/gdb/completer.h
> index e554bff..4b3b188 100644
> --- i/gdb/completer.h
> +++ w/gdb/completer.h
> @@ -274,9 +274,4 @@ extern const char *skip_quoted (const char *);
>   
>   extern int max_completions;
>   
> -
> -/* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR.  */
> -
> -extern void throw_max_completions_reached_error (void);
> -
>   #endif /* defined (COMPLETER_H) */
> diff --git i/gdb/symtab.c w/gdb/symtab.c
> index b70a818..57fb355 100644
> --- i/gdb/symtab.c
> +++ w/gdb/symtab.c
> @@ -5217,9 +5217,8 @@ default_collect_symbol_completion_matches (completion_tracker &tracker,
>   							     code);
>   }
>   
> -/* Return a vector of all symbols (regardless of class) which begin by
> -   matching TEXT.  If the answer is no symbols, then the return value
> -   is NULL.  */
> +/* Collect all symbols (regardless of class) which begin by matching
> +   TEXT.  */
>   
>   void
>   collect_symbol_completion_matches (completion_tracker &tracker,
> @@ -5230,7 +5229,7 @@ collect_symbol_completion_matches (completion_tracker &tracker,
>   							  TYPE_CODE_UNDEF);
>   }
>   
> -/* Like collect_symbol_completion_matches, but only return
> +/* Like collect_symbol_completion_matches, but only collect
>      STRUCT_DOMAIN symbols whose type code is CODE.  */
>   
>   void
> @@ -5406,7 +5405,7 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
>       }
>   }
>   
> -/* Return a vector of all source files whose names begin with matching
> +/* Return a list of all source files whose names begin with matching
>      TEXT.  The file names are looked up in the symbol tables of this
>      program.  */
>   
> .
>

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

* Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction
  2017-07-18  8:23       ` Christophe Lyon
@ 2017-07-18  9:04         ` Pedro Alves
  2017-07-18 10:42           ` [pushed] Fix GDB builds that include the simulator (Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction) Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-18  9:04 UTC (permalink / raw)
  To: Christophe Lyon, gdb-patches

On 07/18/2017 09:23 AM, Christophe Lyon wrote:

> ../../../gdb/remote-sim.c: In function 'void _initialize_remote_sim()':
> ../../../gdb/remote-sim.c:1350:46: error: invalid conversion from
> 'VEC_char_ptr* (*)(cmd_list_element*, const char*, const char*)' to
> 'void (*)(cmd_list_element*, completion_tracker&, const char*, const
> char*)' [-fpermissive]
>    set_cmd_completer (c, sim_command_completer);

> ../../../gdb/command.h:197:13: error:   initializing argument 2 of 'void
> set_cmd_completer(cmd_list_element*, void (*)(cmd_list_element*,
> completion_tracker&, const char*, const char*))' [-fpermissive]
>  extern void set_cmd_completer (struct cmd_list_element *,
> completer_ftype *);
> make[2]: *** [remote-sim.o] Error 1

Sorry about that.

> Can you have a look?

sim_command_completer needs to be adjusted to the new interface.
I'm building a --target-none-eabi gdb to fix this.

Thanks,
Pedro Alves

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

* [pushed] Fix GDB builds that include the simulator (Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction)
  2017-07-18  9:04         ` Pedro Alves
@ 2017-07-18 10:42           ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-18 10:42 UTC (permalink / raw)
  To: Christophe Lyon, gdb-patches

On 07/18/2017 10:04 AM, Pedro Alves wrote:
> sim_command_completer needs to be adjusted to the new interface.
> I'm building a --target-none-eabi gdb to fix this.

Thanks again for reporting.  Fixed with the patch below.

Pedro Alves

From 386535dd91432b784f6a46f8a92c6a599ba30174 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Tue, 18 Jul 2017 11:38:17 +0100
Subject: [PATCH] Fix GDB builds that include the simulator
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The completer rewrite series missed adjusting target sim to the new
completion_tracker interface.

src/gdb/remote-sim.c: In function ‘void _initialize_remote_sim()’:
src/gdb/remote-sim.c:1350:46: error: invalid conversion from ‘VEC_char_ptr* (*)(cmd_list_element*, const char*, const char*)’ to ‘void (*)(cmd_list_element*, completion_tracker&, const char*, const char*)’ [-fpermissive]
   set_cmd_completer (c, sim_command_completer);
                                              ^

This commit fixes it, and also takes care to be exception safe (the
previous code would leak if growing the VEC throws).

Tested manually with a --target=arm-none-eabi build.

gdb/ChangeLog:
2017-07-18  Pedro Alves  <palves@redhat.com>

	* remote-sim.c (sim_command_completer): Adjust to work with a
	completion_tracker instead of a VEC.
---
 gdb/ChangeLog    |  5 +++++
 gdb/remote-sim.c | 48 +++++++++++++++++++++++++++++++++---------------
 2 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 20344f7..66ae527 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2017-07-18  Pedro Alves  <palves@redhat.com>
+
+	* remote-sim.c (sim_command_completer): Adjust to work with a
+	completion_tracker instead of a VEC.
+
 2017-07-17  Pedro Alves  <palves@redhat.com>
 
 	* completer.c (complete_source_filenames): New function.
diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c
index 13137ab..508e2c2 100644
--- a/gdb/remote-sim.c
+++ b/gdb/remote-sim.c
@@ -1220,30 +1220,48 @@ simulator_command (char *args, int from_tty)
   registers_changed ();
 }
 
-static VEC (char_ptr) *
-sim_command_completer (struct cmd_list_element *ignore, const char *text,
-		       const char *word)
+static void
+sim_command_completer (struct cmd_list_element *ignore,
+		       completion_tracker &tracker,
+		       const char *text, const char *word)
 {
   struct sim_inferior_data *sim_data;
-  char **tmp;
-  int i;
-  VEC (char_ptr) *result = NULL;
 
   sim_data = ((struct sim_inferior_data *)
 	      inferior_data (current_inferior (), sim_inferior_data_key));
   if (sim_data == NULL || sim_data->gdbsim_desc == NULL)
-    return NULL;
+    return;
 
-  tmp = sim_complete_command (sim_data->gdbsim_desc, text, word);
-  if (tmp == NULL)
-    return NULL;
+  /* sim_complete_command returns a NULL-terminated malloc'ed array of
+     malloc'ed strings.  */
+  struct sim_completions_deleter
+  {
+    void operator() (char **ptr) const
+    {
+      for (size_t i = 0; ptr[i] != NULL; i++)
+	xfree (ptr[i]);
+      xfree (ptr);
+    }
+  };
+
+  std::unique_ptr<char *[], sim_completions_deleter> sim_completions
+    (sim_complete_command (sim_data->gdbsim_desc, text, word));
+  if (sim_completions == NULL)
+    return;
 
-  /* Transform the array into a VEC, and then free the array.  */
-  for (i = 0; tmp[i] != NULL; i++)
-    VEC_safe_push (char_ptr, result, tmp[i]);
-  xfree (tmp);
+  /* Count the elements and add completions from tail to head because
+     below we'll swap elements out of the array in case add_completion
+     throws and the deleter deletes until it finds a NULL element.  */
+  size_t count = 0;
+  while (sim_completions[count] != NULL)
+    count++;
 
-  return result;
+  for (size_t i = count; i > 0; i--)
+    {
+      gdb::unique_xmalloc_ptr<char> match (sim_completions[i - 1]);
+      sim_completions[i - 1] = NULL;
+      tracker.add_completion (std::move (match));
+    }
 }
 
 /* Check to see if a thread is still alive.  */
-- 
2.5.5


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

* Re: [PATCH 24/40] Per-language symbol name hashing algorithm
  2017-06-02 12:28 ` [PATCH 24/40] Per-language symbol name hashing algorithm Pedro Alves
@ 2017-07-18 17:33   ` Keith Seitz
  2017-07-20 18:53     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-18 17:33 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
> This patch starts fixing this, by doing two things:
> 
> #1 - adds a language vector method to let each language decide how to
>      compute a symbol name hash.
> 
> #2 - makes dictionaries know the language of the symbols they hold,
>      and then use the dictionaries language to decide which hashing
>      method to use.

Me likey! :-)

Again, just the usual trivial comments.

> 
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index cbad027..15f65a8 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -1047,11 +1055,11 @@ prepare_for_building (const char *name, CORE_ADDR start_addr)
>  
>  struct compunit_symtab *
>  start_symtab (struct objfile *objfile, const char *name, const char *comp_dir,
> -	      CORE_ADDR start_addr)
> +	      CORE_ADDR start_addr, enum language language)

Unlike start_buildsym_compunit, this function is exported. IMO, `language' should be mentioned in the comment.

> diff --git a/gdb/coffread.c b/gdb/coffread.c
> index 9db4792..db0b77a 100644
> --- a/gdb/coffread.c
> +++ b/gdb/coffread.c
> @@ -394,7 +394,9 @@ coff_start_symtab (struct objfile *objfile, const char *name)
>  		 NULL,
>    /* The start address is irrelevant, since we set
>       last_source_start_addr in coff_end_symtab.  */
> -		 0);
> +		 0,
> +  /* Let buildsym.c deduce the language for this symtab.  */
> +		 language_unknown);

re: "deduce the language" if language == language_unknown... That's not obvious from start_symtab's documentation, so I think that deserves a mention there.

> diff --git a/gdb/dictionary.h b/gdb/dictionary.h
> index 4f4f160..ef5fbed 100644
> --- a/gdb/dictionary.h
> +++ b/gdb/dictionary.h
> @@ -45,6 +45,7 @@ struct pending;
>     initialized from SYMBOL_LIST.  */
>  
>  extern struct dictionary *dict_create_hashed (struct obstack *obstack,
> +					      enum language language,
>  					      const struct pending
>  					      *symbol_list);
>  
> @@ -53,7 +54,8 @@ extern struct dictionary *dict_create_hashed (struct obstack *obstack,
>     it, call dict_add_symbol().  Call dict_free() when you're done with
>     it.  */
>  
> -extern struct dictionary *dict_create_hashed_expandable (void);
> +extern struct dictionary *
> +  dict_create_hashed_expandable (enum language language);
>  
>  /* Create a dictionary implemented via a fixed-size array.  All memory
>     it uses is allocated on OBSTACK; the environment is initialized
> @@ -61,6 +63,7 @@ extern struct dictionary *dict_create_hashed_expandable (void);
>     that they're found in SYMBOL_LIST.  */
>  
>  extern struct dictionary *dict_create_linear (struct obstack *obstack,
> +					      enum language language,
>  					      const struct pending
>  					      *symbol_list);
>  
> @@ -69,8 +72,8 @@ extern struct dictionary *dict_create_linear (struct obstack *obstack,
>     it, call dict_add_symbol().  Call dict_free() when you're done with
>     it.  */
>  
> -extern struct dictionary *dict_create_linear_expandable (void);
> -
> +extern struct dictionary *
> +  dict_create_linear_expandable (enum language language);
>  
>  /* The functions providing the interface to dictionaries.  Note that
>     the most common parts of the interface, namely symbol lookup, are

All of the above functions are exported, so I think our custom is to document all formal parameters, no?

> diff --git a/gdb/symtab.h b/gdb/symtab.h
> index fffe0f87..20904e4 100644
> --- a/gdb/symtab.h
> +++ b/gdb/symtab.h
> @@ -277,6 +277,9 @@ extern const char *symbol_search_name (const struct general_symbol_info *);
>  #define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
>    (strcmp_iw (SYMBOL_SEARCH_NAME (symbol), (name)) == 0)
>  
> +extern unsigned int search_name_hash (enum language language,
> +				      const char *search_name);
> +

symtab.c says, "See symtab.h." Missing comment here.

Keith

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

* Re: [PATCH 01/40] Make gdb.base/dmsym.exp independent of "set language ada"
  2017-06-02 12:22 ` [PATCH 01/40] Make gdb.base/dmsym.exp independent of "set language ada" Pedro Alves
@ 2017-07-18 19:42   ` Simon Marchi
  2017-07-20 17:00     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Simon Marchi @ 2017-07-18 19:42 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

On 2017-06-02 14:21, Pedro Alves wrote:
> This test is using "set language ada" expecting that to cause GDB to
> do Ada symbol name matching.  That won't work when GDB uses the
> symbol's language to decide which symbol matching algorithm to use,
> because the test's symbols are C symbols.
> 
> So generalize the test a bit to not rely on Ada name matching rules.
> 
> Confirmed that by undoing the original fix the test was written for,
> the test still fails.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.base/dmsym.c (pck__foo__bar__minsym): Rename to ...
> 	(test_minsym): ... this, and make static.
> 	(get_pck__foo__bar__minsym): Rename to ...
> 	(get_test_minsym): ... this.
> 	* gdb.base/dmsym.exp (): Remove "set language ada" call.  Adjust
> 	symbol names and comments.
> 	* gdb.base/dmsym_main.c (get_pck__foo__bar__minsym): Rename to ...
> 	(get_test_minsym): ... this.
> 	(pck__foo__bar__minsym__2): Rename to ...
> 	(test_minsym): ... this.
> 	(main): Adjust.
> ---
>  gdb/testsuite/gdb.base/dmsym.c      |  8 ++++----
>  gdb/testsuite/gdb.base/dmsym.exp    | 39 
> +++++++++++++------------------------
>  gdb/testsuite/gdb.base/dmsym_main.c | 10 +++++-----
>  3 files changed, 23 insertions(+), 34 deletions(-)
> 
> diff --git a/gdb/testsuite/gdb.base/dmsym.c 
> b/gdb/testsuite/gdb.base/dmsym.c
> index f358b51..dccea23 100644
> --- a/gdb/testsuite/gdb.base/dmsym.c
> +++ b/gdb/testsuite/gdb.base/dmsym.c
> @@ -15,11 +15,11 @@
>     You should have received a copy of the GNU General Public License
>     along with this program.  If not, see 
> <http://www.gnu.org/licenses/>.  */
> 
> -int pck__foo__bar__minsym = 123;
> +static int test_minsym = 123;
> 
>  int
> -get_pck__foo__bar__minsym (void)
> +get_test_minsym (void)
>  {
> -  pck__foo__bar__minsym++;
> -  return pck__foo__bar__minsym;
> +  test_minsym++;
> +  return test_minsym;
>  }
> diff --git a/gdb/testsuite/gdb.base/dmsym.exp 
> b/gdb/testsuite/gdb.base/dmsym.exp
> index a318080..191a319 100644
> --- a/gdb/testsuite/gdb.base/dmsym.exp
> +++ b/gdb/testsuite/gdb.base/dmsym.exp
> @@ -44,42 +44,31 @@ clean_restart ${testfile}
>  set num "\[0-9\]+"
>  set addr "0x\[0-9a-zA-Z\]+"
> 
> -# Although the test program is written in C, the original problem
> -# occurs only when the language is Ada. The use of a C program is
> -# only a convenience to be able to exercise the original problem
> -# without requiring an Ada compiler. In the meantime, temporarily
> -# force the language to Ada.
> -
> -gdb_test_no_output "set lang ada"
> -
> -# Verify that setting a breakpoint on `pck__foo__bar__minsym' only
> -# results in one location found (function pck__foo__bar__minsym__2).
> -# A mistake would be to also insert a breakpoint where
> -# pck__foo__bar__minsym is defined.  Despite the fact that there is
> -# no debugging info available, this is a data symbol and thus should
> -# not be used for breakpoint purposes.
> -
> -gdb_test "break pck__foo__bar__minsym" \
> +# Verify that setting a breakpoint on `test_minsym' only results in
> +# one location found.  A mistake would be to also insert a breakpoint
> +# in the test_minsym data symbol in dmsym.c.  Despite the fact that
> +# there is no debugging info available, this is a data symbol and thus
> +# should not be used for breakpoint purposes.
> +
> +gdb_test "break test_minsym" \
>           "Breakpoint $num at $addr.: file .*dmsym_main\\.c, line 
> $num\\."
> 
>  # However, verify that the `info line' command, on the other hand,
>  # finds both locations.
> 
> -gdb_test "info line pck__foo__bar__minsym" \
> -         "Line $num of \".*dmsym_main\\.c\" .*\r\nNo line number
> information available for address $addr <pck__foo__bar__minsym>"
> -
> -gdb_test_no_output "set lang auto"
> +gdb_test "info line test_minsym" \
> +         "Line $num of \".*dmsym_main\\.c\" .*\r\nNo line number
> information available for address $addr <test_minsym>"
> 
> -# Now, run the program until we get past the call to
> -# pck__foo__bar__minsym__2. Except when using hardware breakpoints,
> -# inferior behavior is going to be affected if a breakpoint was
> -# incorrectly inserted at pck__foo__bar__minsym.
> +# Now, run the program until we get past the call to test_minsym.
> +# Except when using hardware breakpoints, inferior behavior is going
> +# to be affected if a breakpoint was incorrectly inserted at
> +# test_minsym.
> 
>  gdb_breakpoint dmsym_main.c:[gdb_get_line_number "BREAK" dmsym_main.c]
> 
>  gdb_run_cmd
>  gdb_test "" \
> -         "Breakpoint $num, pck__foo__bar__minsym__2 \\(\\) at.*" \
> +         "Breakpoint $num, test_minsym \\(\\) at.*" \
>           "run until breakpoint at BREAK"
> 
>  gdb_test "continue" \
> diff --git a/gdb/testsuite/gdb.base/dmsym_main.c
> b/gdb/testsuite/gdb.base/dmsym_main.c
> index 99589b8..0338dc7 100644
> --- a/gdb/testsuite/gdb.base/dmsym_main.c
> +++ b/gdb/testsuite/gdb.base/dmsym_main.c
> @@ -15,18 +15,18 @@
>     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 get_pck__foo__bar__minsym (void);
> +extern int get_test_minsym (void);
> 
> -int
> -pck__foo__bar__minsym__2 (void)
> +static int
> +test_minsym (void)
>  {
> -  return get_pck__foo__bar__minsym ();
> +  return get_test_minsym ();
>  }
> 
>  int
>  main (void)
>  {
> -  int val = pck__foo__bar__minsym__2 ();
> +  int val = test_minsym ();
> 
>    if (val != 124) /* BREAK */
>      return 1;

I don't think anybody looked at this one, so I did.  Looks good to me.

Simon

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-06-02 12:39 ` [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching Pedro Alves
@ 2017-07-18 20:14   ` Keith Seitz
  2017-07-18 22:31     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-07-18 20:14 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
>
> gdb/ChangeLog:
> yyyy-mm-dd   Pedro Alves  <palves@redhat.com>
> 
[holy-moly-mega-snip]
> 
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd   Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.ada/complete.exp (p <Exported_Capitalized>): New test.
> 	(p Exported_Capitalized): New test.
> 	(p exported_capitalized): New test.
> 	* utils.c (enum class strncmp_iw_mode): Moved to utils.h.
> 	(strncmp_iw_with_mode): Now extern.
> 	* utils.h (enum class strncmp_iw_mode): Moved from utils.c.
> 	(strncmp_iw_with_mode): Declare.

utils.[ch]? Wrong ChangeLog?

I'm seeing regressions with this patch (no subsequent patch seems to fix it, either):

FAIL: gdb.ada/arrayidx.exp: print u_one_two_three, indexes off
FAIL: gdb.ada/arrayidx.exp: print u_one_two_three
FAIL: gdb.ada/disc_arr_bound.exp: print r
FAIL: gdb.ada/disc_arr_bound.exp: print r.a
FAIL: gdb.ada/mi_dyn_arr.exp: create bt varobj (unexpected output)
FAIL: gdb.ada/pckd_arr_ren.exp: print var

Keith

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-07-18 20:14   ` Keith Seitz
@ 2017-07-18 22:31     ` Pedro Alves
  2017-07-20 19:00       ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-18 22:31 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/18/2017 09:14 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
> utils.[ch]? Wrong ChangeLog?

Whoops.

> 
> I'm seeing regressions with this patch (no subsequent patch seems to fix it, either):
> 
> FAIL: gdb.ada/arrayidx.exp: print u_one_two_three, indexes off
> FAIL: gdb.ada/arrayidx.exp: print u_one_two_three
> FAIL: gdb.ada/disc_arr_bound.exp: print r
> FAIL: gdb.ada/disc_arr_bound.exp: print r.a
> FAIL: gdb.ada/mi_dyn_arr.exp: create bt varobj (unexpected output)
> FAIL: gdb.ada/pckd_arr_ren.exp: print var
> 

Fun.  From the file names, sounds like they're all related to dynamic arrays.

I don't see those.  Likely this is compiler related.  I'm on F23, with
GNAT 5.3.1.

This patch actually causes a progression for me:

 -KFAIL: gdb.ada/str_ref_cmp.exp: operator = works for strings (PRMS: ada/12607) 
 -UNTESTED: gdb.ada/str_ref_cmp.exp: str_ref_cmp.exp
 +PASS: gdb.ada/str_ref_cmp.exp: operator = works for strings 
 +PASS: gdb.ada/str_ref_cmp.exp: print String_Var (1 .. 3) = "Hel" 
 +PASS: gdb.ada/str_ref_cmp.exp: print String_Var (1 .. 3) = "hel" 

(triple-checked this.)

Here are the gdb.ada/ *FAILs I see before the patch:

 $ grep FAIL gdb.sum
 KFAIL: gdb.ada/array_ptr_renaming.exp: print ntp (PRMS: gdb/NNNN)
 KFAIL: gdb.ada/bad-task-bp-keyword.exp: break *break_me'address TASK Task TaSK 2 (PRMS: gdb/14111)
 XFAIL: gdb.ada/dyn_loc.exp: info locals
 XFAIL: gdb.ada/funcall_ref.exp: p get ("Hello world!")
 XFAIL: gdb.ada/funcall_ref.exp: ptype get ("Hello world!")
 FAIL: gdb.ada/fun_renaming.exp: print next(1) (timeout)
 FAIL: gdb.ada/fun_renaming.exp: print n(1)
 XFAIL: gdb.ada/fun_renaming.exp: print renamed_next(1)
 XFAIL: gdb.ada/fun_renaming.exp: print pack.renamed_next(1)
 FAIL: gdb.ada/mi_ex_cond.exp: catch C_E if i = 2 (unexpected output)
 FAIL: gdb.ada/mi_ex_cond.exp: run to exception catchpoint hit (unknown output after running)
 FAIL: gdb.ada/mi_ex_cond.exp: -break-list (unexpected output)
 KFAIL: gdb.ada/str_ref_cmp.exp: operator = works for strings (PRMS: ada/12607)

Thanks,
Pedro Alves

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

* Re: [PATCH 01/40] Make gdb.base/dmsym.exp independent of "set language ada"
  2017-07-18 19:42   ` Simon Marchi
@ 2017-07-20 17:00     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 17:00 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches

On 07/18/2017 08:42 PM, Simon Marchi wrote:

> I don't think anybody looked at this one, so I did.  Looks good to me.

Thanks, I pushed it in.

-- 
Pedro Alves

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

* Re: [PATCH 19/40] Fix cp_find_first_component_aux bug
  2017-07-17 21:38       ` Keith Seitz
@ 2017-07-20 17:03         ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 17:03 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/17/2017 10:38 PM, Keith Seitz wrote:
> On 07/17/2017 12:50 PM, Pedro Alves wrote:
>> Would you be OK with adding the fix without the test yet?
> 
> Sure.

OK, I pushed it in now.

Thanks,
Pedro Alves

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

* Re: [PATCH 20/40] Eliminate block_iter_name_*
  2017-07-17 19:47   ` Keith Seitz
@ 2017-07-20 17:05     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 17:05 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/17/2017 08:47 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> This patch gets rid of block_iter_name_* as being unnecessary.  It's
>> the same as calling block_iter_match_*, and passing strcmp_iw as
>> comparison routine.
> 
> I was curious how this situation arose. Looking at the archives, I think it safe to say that someone was playing it safe when extending the interfaces for Ada. [I certainly don't blame people for playing it safe. I do it all the time.]
> 
> So, LGTM.

Thanks for checking that!  I pushed it in now.

-- 
Pedro Alves

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

* Re: [PATCH 21/40] Use SYMBOL_MATCHES_SEARCH_NAME some more
  2017-07-17 21:39   ` Keith Seitz
@ 2017-07-20 17:08     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 17:08 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/17/2017 10:39 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* dwarf2read.c (dw2_lookup_symbol): Use
>> 	SYMBOL_MATCHES_SEARCH_NAME.
>> 	* psymtab.c (psym_lookup_symbol): Use SYMBOL_MATCHES_SEARCH_NAME.
> 
> I'd say that falls under the "obvious" banner, no? ;-)

I guess.  :-)  I pushed it in.

Thanks,
Pedro Alves

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

* Re: [PATCH 22/40] get_int_var_value
  2017-07-17 22:11   ` Keith Seitz
@ 2017-07-20 17:15     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 17:15 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 07/17/2017 11:11 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> I noticed that get_int_var_value's parameters could use some
>> constification.  And then realized that client code would become
>> simpler by changing the interface to return the success/failure
>> indication as actual return value, as allows getting rid of the the
>> local "boolean" variable.
> 
> It is certainly more appropriate for Joel to comment, but I did look through the patch, and it is a pretty straightforward change. I didn't notice any issues.

Thanks!  I want ahead and push it in to get it out of
the way of the following, more interesting patches.  I'll of
course gladly address any concern that Joel may have.

Thanks,
Pedro Alves

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

* Re: [PATCH 23/40] Make language_def O(1)
  2017-07-17 23:03   ` Keith Seitz
@ 2017-07-20 17:40     ` Pedro Alves
  2017-07-20 18:12       ` Get rid of "set language local"? (was: Re: [PATCH 23/40] Make language_def O(1)) Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 17:40 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/18/2017 12:03 AM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> IMO, the add_language mechanism is pointless, because "enum language"
>> implies the core of GDB needs to know about all languages anyway.
> 
> I don't know how others feel about it, but since I am giving feedback on this patch, I will say that I agree with you.
> 
> Now, if there was a mechanism by which we could dynamically add languages, add_language would make much more sense.

Yeah.

> 
>> Note that "local_language_defn" is gone along the way.  AFAICT, it's
>> just a copy of "auto", so the new code simply maps one to the other.
>> One fewer place to update when we need to change the language
>> vector...
> 
> I've searched the manual, and the "local" language is mentioned only in two places, once in the description of "set language" (where it explains that "local" and "auto" are the same) and once when we describe -data-evaluate-expression's --language flag.
> 
> Honestly, is there any reason to keep it at all given it is a synonym of auto? [I realize that it doesn't cost much to maintain with this patch.] I'm not asking for any changes in this regard, just throwing the possibility out there. TBH, I didn't even know "local" existed.

I didn't know about "local" until I stumbled on it working on this
series, either.  :-)

I'll send a follow up reply with a different subject
to see if anyone else has comments on that.

> 
> Just two trivial nits.
> 
>> diff --git a/gdb/language.c b/gdb/language.c
>> index d30f4f0..df4f3cd 100644
>> --- a/gdb/language.c
>> +++ b/gdb/language.c
>> @@ -530,55 +522,51 @@ show_check (char *ignore, int from_tty)
>>    cmd_show_list (showchecklist, from_tty, "");
>>  }
>>  \f
>> -/* Add a language to the set of known languages.  */
>>  
>> -void
>> -add_language (const struct language_defn *lang)
>> +/* Compare C strings for std::sort.  */
>> +
>> +static bool
>> +compare_cstrings (const char *str1, const char *str2)
>>  {
>> -  /* For the "set language" command.  */
>> -  static const char **language_names = NULL;
>> -  /* For the "help set language" command.  */
>> +  return strcmp (str1, str2) < 0;
>> +}
> 
> Doesn't completer.c define the same function? Perhaps move to utils.c? I would guess this is not the last time we will want to do this "sort" of thing. :-)

Indeed.  :-)  I put it in utils.h, so that it can still be inlined.


>> +static void
>> +build_language_commands ()
>> +{
> 
> missing comment<cough/>

Whoops.  While adding a comment I realized that the function's name isn't
ideal.  I renamed it to add_set_language_command().

>> -gdb_test "set language" "Requires an argument. Valid arguments are ada, c, c.., asm, minimal, d, fortran, go, auto, local, unknown, modula-2, objective-c, opencl, pascal, rust." "set language"
>> +gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, asm, c, c.., d, fortran, go, minimal, modula-2, objective-c, opencl, pascal, rust." "set language"
> 
> Nice catch.

This was: https://sourceware.org/ml/gdb-patches/2016-11/msg00528.html

> 
> Otherwise, LGTM.

Great, thanks for the review!

Here's what I pushed in.

From 47e77640be31fc1a4eb3718f594ed5fd0faff065 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 20 Jul 2017 18:28:01 +0100
Subject: [PATCH] Make language_def O(1)

Profiling GDB with the rest of series applied, I saw calls to
language_def showing up high in some runs.  The problem is that
language_def is O(N) currently, since walk the languages vector each
time to find the matching language_defn.

IMO, the add_language mechanism is pointless, because "enum language"
implies the core of GDB needs to know about all languages anyway.  So
simply make the languages vector array be an array where each
element's index is the corresponding enum language enumerator.  Note
that "local_language_defn" is gone along the way.  It's just a copy of
"auto", so the new code simply maps one to the other.  One fewer place
to update when we need to change the language vector...

Also, a while ago the output of "set language" was made out of order
as side effect of some other change.  While I was at it, I made them
sorted again.

gdb/ChangeLog:
2017-07-20  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_language_defn): Make extern.
	(_initialize_ada_language): Remove add_language call.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Make extern.
	(_initialize_c_language): Delete.
	* completer.c (compare_cstrings): Delete, moved to utils.h.
	* d-lang.c (d_language_defn): Make extern.
	(_initialize_d_language): Remove add_language calls.
	* defs.h (enum language): Add comment.
	* f-lang.c (f_language_defn): Make extern.
	(_initialize_f_language): Remove add_language call.
	* go-lang.c (go_language_defn): Make extern.
	(_initialize_go_language): Remove add_language call.
	* language.c: Include <algorithm>.
	(languages): Redefine as const array.
	(languages_size, languages_allocsize, DEFAULT_ALLOCSIZE): Delete.
	(set_language_command): Handle "local".  Use for-range loop.
	(set_language): Remove loop.
	(language_enum): Rewrite.
	(language_def, language_str): Remove loops.
	(add_language): Delete.
	(add_set_language_command): New, based on add_languages.
	(skip_language_trampoline): Adjust.
	(local_language_defn): Delete.
	(language_gdbarch_post_init): Adjust.
	(_initialize_language): Remove add_language calls.  Call
	add_set_language_command.
	* language.h (add_language): Delete.
	(auto_language_defn)
	(unknown_language_defn, minimal_language_defn, ada_language_defn)
	(asm_language_defn, c_language_defn, cplus_language_defn)
	(d_language_defn, f_language_defn, go_language_defn)
	(m2_language_defn, objc_language_defn, opencl_language_defn)
	(pascal_language_defn, rust_language_defn): Declare.
	* m2-lang.c (m2_language_defn): Make extern.
	(_initialize_m2_language): Remove add_language call.
	* objc-lang.c (objc_language_defn): Make extern.
	(_initialize_objc_language): Remove add_language call.
	* opencl-lang.c (opencl_language_defn): Make extern.
	(_initialize_opencl_language): Remove add_language call.
	* p-lang.c (pascal_language_defn): Make extern.
	(_initialize_pascal_language): Delete.
	* rust-lang.c (rust_language_defn): Make extern.
	(_initialize_rust_language): Delete.
	* utils.h (compare_cstrings): New static inline function.

gdb/testsuite/ChangeLog:
2017-07-20  Pedro Alves  <palves@redhat.com>

	* gdb.base/default.exp (set language): Adjust expected output.
---
 gdb/ChangeLog                      |  48 +++++++
 gdb/testsuite/ChangeLog            |   4 +
 gdb/ada-lang.c                     |   4 +-
 gdb/c-lang.c                       |  19 +--
 gdb/completer.c                    |   8 --
 gdb/d-lang.c                       |   4 +-
 gdb/defs.h                         |   3 +-
 gdb/f-lang.c                       |   4 +-
 gdb/go-lang.c                      |   4 +-
 gdb/language.c                     | 248 +++++++++++++------------------------
 gdb/language.h                     |  23 +++-
 gdb/m2-lang.c                      |   4 +-
 gdb/objc-lang.c                    |   3 +-
 gdb/opencl-lang.c                  |   3 +-
 gdb/p-lang.c                       |  11 +-
 gdb/rust-lang.c                    |  10 +-
 gdb/testsuite/gdb.base/default.exp |   2 +-
 gdb/utils.h                        |   8 ++
 18 files changed, 184 insertions(+), 226 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index a9ae09f..62f3bfb 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,53 @@
 2017-07-20  Pedro Alves  <palves@redhat.com>
 
+	* ada-lang.c (ada_language_defn): Make extern.
+	(_initialize_ada_language): Remove add_language call.
+	* c-lang.c (c_language_defn, cplus_language_defn)
+	(asm_language_defn, minimal_language_defn): Make extern.
+	(_initialize_c_language): Delete.
+	* completer.c (compare_cstrings): Delete, moved to utils.h.
+	* d-lang.c (d_language_defn): Make extern.
+	(_initialize_d_language): Remove add_language calls.
+	* defs.h (enum language): Add comment.
+	* f-lang.c (f_language_defn): Make extern.
+	(_initialize_f_language): Remove add_language call.
+	* go-lang.c (go_language_defn): Make extern.
+	(_initialize_go_language): Remove add_language call.
+	* language.c: Include <algorithm>.
+	(languages): Redefine as const array.
+	(languages_size, languages_allocsize, DEFAULT_ALLOCSIZE): Delete.
+	(set_language_command): Handle "local".  Use for-range loop.
+	(set_language): Remove loop.
+	(language_enum): Rewrite.
+	(language_def, language_str): Remove loops.
+	(add_language): Delete.
+	(add_set_language_command): New, based on add_languages.
+	(skip_language_trampoline): Adjust.
+	(local_language_defn): Delete.
+	(language_gdbarch_post_init): Adjust.
+	(_initialize_language): Remove add_language calls.  Call
+	add_set_language_command.
+	* language.h (add_language): Delete.
+	(auto_language_defn)
+	(unknown_language_defn, minimal_language_defn, ada_language_defn)
+	(asm_language_defn, c_language_defn, cplus_language_defn)
+	(d_language_defn, f_language_defn, go_language_defn)
+	(m2_language_defn, objc_language_defn, opencl_language_defn)
+	(pascal_language_defn, rust_language_defn): Declare.
+	* m2-lang.c (m2_language_defn): Make extern.
+	(_initialize_m2_language): Remove add_language call.
+	* objc-lang.c (objc_language_defn): Make extern.
+	(_initialize_objc_language): Remove add_language call.
+	* opencl-lang.c (opencl_language_defn): Make extern.
+	(_initialize_opencl_language): Remove add_language call.
+	* p-lang.c (pascal_language_defn): Make extern.
+	(_initialize_pascal_language): Delete.
+	* rust-lang.c (rust_language_defn): Make extern.
+	(_initialize_rust_language): Delete.
+	* utils.h (compare_cstrings): New static inline function.
+
+2017-07-20  Pedro Alves  <palves@redhat.com>
+
 	* ada-lang.c (ada_to_fixed_type_1): Adjust.
 	(get_var_value): Constify parameters.
 	(get_int_var_value): Change prototype.
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index ac68fba..eef141d 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,9 @@
 2017-07-20  Pedro Alves  <palves@redhat.com>
 
+	* gdb.base/default.exp (set language): Adjust expected output.
+
+2017-07-20  Pedro Alves  <palves@redhat.com>
+
 	* gdb.base/dmsym.c (pck__foo__bar__minsym): Rename to ...
 	(test_minsym): ... this, and make static.
 	(get_pck__foo__bar__minsym): Rename to ...
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 1dd30b7..280247b 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13960,7 +13960,7 @@ static const char *ada_extensions[] =
   ".adb", ".ads", ".a", ".ada", ".dg", NULL
 };
 
-const struct language_defn ada_language_defn = {
+extern const struct language_defn ada_language_defn = {
   "ada",                        /* Language name */
   "Ada",
   language_ada,
@@ -14090,8 +14090,6 @@ ada_free_objfile_observer (struct objfile *objfile)
 void
 _initialize_ada_language (void)
 {
-  add_language (&ada_language_defn);
-
   initialize_ada_catchpoint_ops ();
 
   add_prefix_cmd ("ada", no_class, set_ada_command,
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 695b237..f86e26e 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -35,8 +35,6 @@
 #include <ctype.h>
 #include "gdbcore.h"
 
-extern void _initialize_c_language (void);
-
 /* Given a C string type, STR_TYPE, return the corresponding target
    character set name.  */
 
@@ -831,7 +829,7 @@ static const char *c_extensions[] =
   ".c", NULL
 };
 
-const struct language_defn c_language_defn =
+extern const struct language_defn c_language_defn =
 {
   "c",				/* Language name */
   "C",
@@ -975,7 +973,7 @@ static const char *cplus_extensions[] =
   ".C", ".cc", ".cp", ".cpp", ".cxx", ".c++", NULL
 };
 
-const struct language_defn cplus_language_defn =
+extern const struct language_defn cplus_language_defn =
 {
   "c++",			/* Language name */
   "C++",
@@ -1028,7 +1026,7 @@ static const char *asm_extensions[] =
   ".s", ".sx", ".S", NULL
 };
 
-const struct language_defn asm_language_defn =
+extern const struct language_defn asm_language_defn =
 {
   "asm",			/* Language name */
   "assembly",
@@ -1081,7 +1079,7 @@ const struct language_defn asm_language_defn =
    to do some simple operations when debugging applications that use
    a language currently not supported by GDB.  */
 
-const struct language_defn minimal_language_defn =
+extern const struct language_defn minimal_language_defn =
 {
   "minimal",			/* Language name */
   "Minimal",
@@ -1128,12 +1126,3 @@ const struct language_defn minimal_language_defn =
   NULL,
   LANG_MAGIC
 };
-
-void
-_initialize_c_language (void)
-{
-  add_language (&c_language_defn);
-  add_language (&cplus_language_defn);
-  add_language (&asm_language_defn);
-  add_language (&minimal_language_defn);
-}
diff --git a/gdb/completer.c b/gdb/completer.c
index ba2e860..a029263 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -2026,14 +2026,6 @@ completion_result::release_match_list ()
   return ret;
 }
 
-/* Compare C strings for std::sort.  */
-
-static bool
-compare_cstrings (const char *str1, const char *str2)
-{
-  return strcmp (str1, str2) < 0;
-}
-
 /* See completer.h  */
 
 void
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 74cceaa..941d3ed 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -204,7 +204,7 @@ static const char *d_extensions[] =
   ".d", NULL
 };
 
-static const struct language_defn d_language_defn =
+extern const struct language_defn d_language_defn =
 {
   "d",
   "D",
@@ -349,6 +349,4 @@ void
 _initialize_d_language (void)
 {
   d_type_data = gdbarch_data_register_post_init (build_d_types);
-
-  add_language (&d_language_defn);
 }
diff --git a/gdb/defs.h b/gdb/defs.h
index 55d16d1..e180523 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -203,7 +203,8 @@ extern void quit_serial_event_clear (void);
    several languages.  For that reason, the constants here are sorted
    in the order we'll attempt demangling them.  For example: Rust uses
    C++ mangling, so must come after C++; Ada must come last (see
-   ada_sniff_from_mangled_name).  */
+   ada_sniff_from_mangled_name).  (Keep this order in sync with the
+   'languages' array in language.c.)  */
 
 enum language
   {
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 937ebff..903cfd1 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -245,7 +245,7 @@ static const char *f_extensions[] =
   NULL
 };
 
-const struct language_defn f_language_defn =
+extern const struct language_defn f_language_defn =
 {
   "fortran",
   "Fortran",
@@ -369,6 +369,4 @@ void
 _initialize_f_language (void)
 {
   f_type_data = gdbarch_data_register_post_init (build_fortran_types);
-
-  add_language (&f_language_defn);
 }
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index f3b4f5c..60bb3c5 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -565,7 +565,7 @@ go_language_arch_info (struct gdbarch *gdbarch,
   lai->bool_type_default = builtin->builtin_bool;
 }
 
-static const struct language_defn go_language_defn =
+extern const struct language_defn go_language_defn =
 {
   "go",
   "Go",
@@ -676,6 +676,4 @@ void
 _initialize_go_language (void)
 {
   go_type_data = gdbarch_data_register_post_init (build_go_types);
-
-  add_language (&go_language_defn);
 }
diff --git a/gdb/language.c b/gdb/language.c
index d30f4f0..073039e 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -44,6 +44,7 @@
 #include "cp-support.h"
 #include "frame.h"
 #include "c-lang.h"
+#include <algorithm>
 
 extern void _initialize_language (void);
 
@@ -91,12 +92,26 @@ enum language_mode language_mode = language_mode_auto;
 
 const struct language_defn *expected_language;
 
-/* The list of supported languages.  The list itself is malloc'd.  */
-
-static const struct language_defn **languages;
-static unsigned languages_size;
-static unsigned languages_allocsize;
-#define	DEFAULT_ALLOCSIZE 4
+/* The list of supported languages.  Keep this in the same order as
+   the 'enum language' values.  */
+
+static const struct language_defn *languages[] = {
+  &unknown_language_defn,
+  &auto_language_defn,
+  &c_language_defn,
+  &objc_language_defn,
+  &cplus_language_defn,
+  &d_language_defn,
+  &go_language_defn,
+  &f_language_defn,
+  &m2_language_defn,
+  &asm_language_defn,
+  &pascal_language_defn,
+  &opencl_language_defn,
+  &rust_language_defn,
+  &minimal_language_defn,
+  &ada_language_defn,
+};
 
 /* The current values of the "set language/type/range" enum
    commands.  */
@@ -148,16 +163,19 @@ show_language_command (struct ui_file *file, int from_tty,
 static void
 set_language_command (char *ignore, int from_tty, struct cmd_list_element *c)
 {
-  int i;
   enum language flang = language_unknown;
 
+  /* "local" is a synonym of "auto".  */
+  if (strcmp (language, "local") == 0)
+    language = "auto";
+
   /* Search the list of languages for a match.  */
-  for (i = 0; i < languages_size; i++)
+  for (const auto &lang : languages)
     {
-      if (strcmp (languages[i]->la_name, language) == 0)
+      if (strcmp (lang->la_name, language) == 0)
 	{
 	  /* Found it!  Go into manual mode, and use this language.  */
-	  if (languages[i]->la_language == language_auto)
+	  if (lang->la_language == language_auto)
 	    {
 	      /* Enter auto mode.  Set to the current frame's language, if
                  known, or fallback to the initial language.  */
@@ -186,7 +204,7 @@ set_language_command (char *ignore, int from_tty, struct cmd_list_element *c)
 	    {
 	      /* Enter manual mode.  Set the specified language.  */
 	      language_mode = language_mode_manual;
-	      current_language = languages[i];
+	      current_language = lang;
 	      set_range_case ();
 	      expected_language = current_language;
 	      return;
@@ -364,21 +382,11 @@ set_range_case (void)
 enum language
 set_language (enum language lang)
 {
-  int i;
   enum language prev_language;
 
   prev_language = current_language->la_language;
-
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i]->la_language == lang)
-	{
-	  current_language = languages[i];
-	  set_range_case ();
-	  break;
-	}
-    }
-
+  current_language = languages[lang];
+  set_range_case ();
   return prev_language;
 }
 \f
@@ -474,11 +482,12 @@ range_error (const char *string,...)
 enum language
 language_enum (char *str)
 {
-  int i;
+  for (const auto &lang : languages)
+    if (strcmp (lang->la_name, str) == 0)
+      return lang->la_language;
 
-  for (i = 0; i < languages_size; i++)
-    if (strcmp (languages[i]->la_name, str) == 0)
-      return languages[i]->la_language;
+  if (strcmp (str, "local") == 0)
+    return language_auto;
 
   return language_unknown;
 }
@@ -488,32 +497,15 @@ language_enum (char *str)
 const struct language_defn *
 language_def (enum language lang)
 {
-  int i;
-
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i]->la_language == lang)
-	{
-	  return languages[i];
-	}
-    }
-  return NULL;
+  return languages[lang];
 }
 
 /* Return the language as a string.  */
+
 const char *
 language_str (enum language lang)
 {
-  int i;
-
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i]->la_language == lang)
-	{
-	  return languages[i]->la_name;
-	}
-    }
-  return "Unknown";
+  return languages[lang]->la_name;
 }
 
 static void
@@ -530,55 +522,45 @@ show_check (char *ignore, int from_tty)
   cmd_show_list (showchecklist, from_tty, "");
 }
 \f
-/* Add a language to the set of known languages.  */
 
-void
-add_language (const struct language_defn *lang)
-{
-  /* For the "set language" command.  */
-  static const char **language_names = NULL;
-  /* For the "help set language" command.  */
+/* Build and install the "set language LANG" command.  */
 
-  if (lang->la_magic != LANG_MAGIC)
-    {
-      fprintf_unfiltered (gdb_stderr,
-			  "Magic number of %s language struct wrong\n",
-			  lang->la_name);
-      internal_error (__FILE__, __LINE__,
-		      _("failed internal consistency check"));
-    }
+static void
+add_set_language_command ()
+{
+  static const char **language_names;
 
-  if (!languages)
-    {
-      languages_allocsize = DEFAULT_ALLOCSIZE;
-      languages = XNEWVEC (const struct language_defn *, languages_allocsize);
-    }
-  if (languages_size >= languages_allocsize)
+  /* Build the language names array, to be used as enumeration in the
+     "set language" enum command.  +1 for "local" and +1 for NULL
+     termination.  */
+  language_names = new const char *[ARRAY_SIZE (languages) + 2];
+
+  /* Display "auto", "local" and "unknown" first, and then the rest,
+     alpha sorted.  */
+  const char **language_names_p = language_names;
+  *language_names_p++ = auto_language_defn.la_name;
+  *language_names_p++ = "local";
+  *language_names_p++ = unknown_language_defn.la_name;
+  const char **sort_begin = language_names_p;
+  for (const auto &lang : languages)
     {
-      languages_allocsize *= 2;
-      languages = (const struct language_defn **) xrealloc ((char *) languages,
-				 languages_allocsize * sizeof (*languages));
+      /* Already handled above.  */
+      if (lang->la_language == language_auto
+	  || lang->la_language == language_unknown)
+	continue;
+      *language_names_p++ = lang->la_name;
     }
-  languages[languages_size++] = lang;
-
-  /* Build the language names array, to be used as enumeration in the
-     set language" enum command.  */
-  language_names = XRESIZEVEC (const char *, language_names,
-			       languages_size + 1);
-
-  for (int i = 0; i < languages_size; ++i)
-    language_names[i] = languages[i]->la_name;
-  language_names[languages_size] = NULL;
+  *language_names_p = NULL;
+  std::sort (sort_begin, language_names_p, compare_cstrings);
 
   /* Add the filename extensions.  */
-  if (lang->la_filename_extensions != NULL)
-    {
-      int i;
-
-      for (i = 0; lang->la_filename_extensions[i] != NULL; ++i)
-	add_filename_language (lang->la_filename_extensions[i],
-			       lang->la_language);
-    }
+  for (const auto &lang : languages)
+    if (lang->la_filename_extensions != NULL)
+      {
+	for (size_t i = 0; lang->la_filename_extensions[i] != NULL; ++i)
+	  add_filename_language (lang->la_filename_extensions[i],
+				 lang->la_language);
+      }
 
   /* Build the "help set language" docs.  */
   string_file doc;
@@ -587,24 +569,24 @@ add_language (const struct language_defn *lang)
 		"The currently understood settings are:\n\nlocal or "
 		"auto    Automatic setting based on source file\n"));
 
-  for (int i = 0; i < languages_size; ++i)
+  for (const auto &lang : languages)
     {
       /* Already dealt with these above.  */
-      if (languages[i]->la_language == language_unknown
-	  || languages[i]->la_language == language_auto)
+      if (lang->la_language == language_unknown
+	  || lang->la_language == language_auto)
 	continue;
 
       /* FIXME: i18n: for now assume that the human-readable name is
 	 just a capitalization of the internal name.  */
       doc.printf ("%-16s Use the %c%s language\n",
-		  languages[i]->la_name,
+		  lang->la_name,
 		  /* Capitalize first letter of language name.  */
-		  toupper (languages[i]->la_name[0]),
-		  languages[i]->la_name + 1);
+		  toupper (lang->la_name[0]),
+		  lang->la_name + 1);
     }
 
   add_setshow_enum_cmd ("language", class_support,
-			(const char **) language_names,
+			language_names,
 			&language,
 			doc.c_str (),
 			_("Show the current source language."),
@@ -620,13 +602,11 @@ add_language (const struct language_defn *lang)
 CORE_ADDR 
 skip_language_trampoline (struct frame_info *frame, CORE_ADDR pc)
 {
-  int i;
-
-  for (i = 0; i < languages_size; i++)
+  for (const auto &lang : languages)
     {
-      if (languages[i]->skip_trampoline)
+      if (lang->skip_trampoline != NULL)
 	{
-	  CORE_ADDR real_pc = (languages[i]->skip_trampoline) (frame, pc);
+	  CORE_ADDR real_pc = lang->skip_trampoline (frame, pc);
 
 	  if (real_pc)
 	    return real_pc;
@@ -919,53 +899,6 @@ const struct language_defn auto_language_defn =
   LANG_MAGIC
 };
 
-const struct language_defn local_language_defn =
-{
-  "local",
-  "Local",
-  language_auto,
-  range_check_off,
-  case_sensitive_on,
-  array_row_major,
-  macro_expansion_no,
-  NULL,
-  &exp_descriptor_standard,
-  unk_lang_parser,
-  unk_lang_error,
-  null_post_parser,
-  unk_lang_printchar,		/* Print character constant */
-  unk_lang_printstr,
-  unk_lang_emit_char,
-  unk_lang_print_type,		/* Print a type using appropriate syntax */
-  default_print_typedef,	/* Print a typedef using appropriate syntax */
-  unk_lang_val_print,		/* Print a value using appropriate syntax */
-  unk_lang_value_print,		/* Print a top-level value */
-  default_read_var_value,	/* la_read_var_value */
-  unk_lang_trampoline,		/* Language specific skip_trampoline */
-  "this", 		        /* name_of_this */
-  basic_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
-  basic_lookup_transparent_type,/* lookup_transparent_type */
-  unk_lang_demangle,		/* Language specific symbol demangler */
-  NULL,
-  unk_lang_class_name,		/* Language specific
-				   class_name_from_physname */
-  unk_op_print_tab,		/* expression operators for printing */
-  1,				/* c-style arrays */
-  0,				/* String lower bound */
-  default_word_break_characters,
-  default_collect_symbol_completion_matches,
-  unknown_language_arch_info,	/* la_language_arch_info.  */
-  default_print_array_index,
-  default_pass_by_reference,
-  default_get_string,
-  c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
-  iterate_over_symbols,
-  &default_varobj_ops,
-  NULL,
-  NULL,
-  LANG_MAGIC
-};
 \f
 /* Per-architecture language information.  */
 
@@ -982,16 +915,15 @@ static void *
 language_gdbarch_post_init (struct gdbarch *gdbarch)
 {
   struct language_gdbarch *l;
-  int i;
 
   l = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct language_gdbarch);
-  for (i = 0; i < languages_size; i++)
-    {
-      if (languages[i] != NULL
-	  && languages[i]->la_language_arch_info != NULL)
-	languages[i]->la_language_arch_info
-	  (gdbarch, l->arch_info + languages[i]->la_language);
-    }
+  for (const auto &lang : languages)
+    if (lang != NULL && lang->la_language_arch_info != NULL)
+      {
+	lang->la_language_arch_info (gdbarch,
+				     l->arch_info + lang->la_language);
+      }
+
   return l;
 }
 
@@ -1205,9 +1137,7 @@ For Fortran the default is off; for other languages the default is on."),
 			show_case_command,
 			&setlist, &showlist);
 
-  add_language (&auto_language_defn);
-  add_language (&local_language_defn);
-  add_language (&unknown_language_defn);
+  add_set_language_command ();
 
   language = xstrdup ("auto");
   type = xstrdup ("auto");
diff --git a/gdb/language.h b/gdb/language.h
index 75b9438..f4852c1 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -568,10 +568,6 @@ extern const struct language_defn *language_def (enum language);
 
 extern const char *language_str (enum language);
 
-/* Add a language to the set known by GDB (at initialization time).  */
-
-extern void add_language (const struct language_defn *);
-
 /* Check for a language-specific trampoline.  */
 
 extern CORE_ADDR skip_language_trampoline (struct frame_info *, CORE_ADDR pc);
@@ -618,4 +614,23 @@ void default_get_string (struct value *value, gdb_byte **buffer, int *length,
 void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 		   struct type **char_type, const char **charset);
 
+/* The languages supported by GDB.  */
+
+extern const struct language_defn auto_language_defn;
+extern const struct language_defn unknown_language_defn;
+extern const struct language_defn minimal_language_defn;
+
+extern const struct language_defn ada_language_defn;
+extern const struct language_defn asm_language_defn;
+extern const struct language_defn c_language_defn;
+extern const struct language_defn cplus_language_defn;
+extern const struct language_defn d_language_defn;
+extern const struct language_defn f_language_defn;
+extern const struct language_defn go_language_defn;
+extern const struct language_defn m2_language_defn;
+extern const struct language_defn objc_language_defn;
+extern const struct language_defn opencl_language_defn;
+extern const struct language_defn pascal_language_defn;
+extern const struct language_defn rust_language_defn;
+
 #endif /* defined (LANGUAGE_H) */
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 0035a52..b9ab2b3 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -354,7 +354,7 @@ const struct exp_descriptor exp_descriptor_modula2 =
   evaluate_subexp_modula2
 };
 
-const struct language_defn m2_language_defn =
+extern const struct language_defn m2_language_defn =
 {
   "modula-2",
   "Modula-2",
@@ -439,6 +439,4 @@ void
 _initialize_m2_language (void)
 {
   m2_type_data = gdbarch_data_register_post_init (build_m2_types);
-
-  add_language (&m2_language_defn);
 }
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index af27268..6ffe85e 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -364,7 +364,7 @@ static const char *objc_extensions[] =
   ".m", NULL
 };
 
-const struct language_defn objc_language_defn = {
+extern const struct language_defn objc_language_defn = {
   "objective-c",		/* Language name */
   "Objective-C",
   language_objc,
@@ -1377,7 +1377,6 @@ extern initialize_file_ftype _initialize_objc_language;
 void
 _initialize_objc_language (void)
 {
-  add_language (&objc_language_defn);
   add_info ("selectors", selectors_info,    /* INFO SELECTORS command.  */
 	    _("All Objective-C selectors, or those matching REGEXP."));
   add_info ("classes", classes_info, 	    /* INFO CLASSES   command.  */
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index 116d107..9b0f015 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1043,7 +1043,7 @@ const struct exp_descriptor exp_descriptor_opencl =
   evaluate_subexp_opencl
 };
 
-const struct language_defn opencl_language_defn =
+extern const struct language_defn opencl_language_defn =
 {
   "opencl",			/* Language name */
   "OpenCL C",
@@ -1185,5 +1185,4 @@ void
 _initialize_opencl_language (void)
 {
   opencl_type_data = gdbarch_data_register_post_init (build_opencl_types);
-  add_language (&opencl_language_defn);
 }
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 77913fc..439a377 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -32,9 +32,6 @@
 #include <ctype.h>
 #include "c-lang.h"
 
-extern void _initialize_pascal_language (void);
-
-
 /* All GPC versions until now (2007-09-27) also define a symbol called
    '_p_initialize'.  Check for the presence of this symbol first.  */
 static const char GPC_P_INITIALIZE[] = "_p_initialize";
@@ -418,7 +415,7 @@ static const char *p_extensions[] =
   ".pas", ".p", ".pp", NULL
 };
 
-const struct language_defn pascal_language_defn =
+extern const struct language_defn pascal_language_defn =
 {
   "pascal",			/* Language name */
   "Pascal",
@@ -464,9 +461,3 @@ const struct language_defn pascal_language_defn =
   NULL,
   LANG_MAGIC
 };
-
-void
-_initialize_pascal_language (void)
-{
-  add_language (&pascal_language_defn);
-}
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index ef41f56..817976a 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -35,8 +35,6 @@
 #include <string>
 #include <vector>
 
-extern initialize_file_ftype _initialize_rust_language;
-
 /* Returns the last segment of a Rust path like foo::bar::baz.  Will
    not handle cases where the last segment contains generics.  This
    will return NULL if the last segment cannot be found.  */
@@ -2150,7 +2148,7 @@ static const char *rust_extensions[] =
   ".rs", NULL
 };
 
-static const struct language_defn rust_language_defn =
+extern const struct language_defn rust_language_defn =
 {
   "rust",
   "Rust",
@@ -2197,9 +2195,3 @@ static const struct language_defn rust_language_defn =
   NULL,
   LANG_MAGIC
 };
-
-void
-_initialize_rust_language (void)
-{
-  add_language (&rust_language_defn);
-}
diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 8c0b29c..c25069a 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -511,7 +511,7 @@ gdb_test "set history size" "Argument required .integer to set it to.*" "set his
 #test set history
 gdb_test "set history" "\"set history\" must be followed by the name of a history subcommand.(\[^\r\n\]*\[\r\n\])+List of set history subcommands:(\[^\r\n\]*\[\r\n\])+set history expansion -- Set history expansion on command input(\[^\r\n\]*\[\r\n\])+set history filename -- Set the filename in which to record the command history(\[^\r\n\]*\[\r\n\])+set history save -- Set saving of the history record on exit(\[^\r\n\]*\[\r\n\])+set history size -- Set the size of the command history(\[^\r\n\]*\[\r\n\])+Type \"help set history\" followed by set history subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set history"
 #test set language
-gdb_test "set language" "Requires an argument. Valid arguments are ada, c, c.., asm, minimal, d, fortran, go, auto, local, unknown, modula-2, objective-c, opencl, pascal, rust." "set language"
+gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, asm, c, c.., d, fortran, go, minimal, modula-2, objective-c, opencl, pascal, rust." "set language"
 #test set listsize
 gdb_test "set listsize" "Argument required .integer to set it to.*" "set listsize"
 #test set print "p" abbreviation
diff --git a/gdb/utils.h b/gdb/utils.h
index 6df752f..48330a1 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -59,6 +59,14 @@ extern int subset_compare (const char *, const char *);
 int compare_positive_ints (const void *ap, const void *bp);
 int compare_strings (const void *ap, const void *bp);
 
+/* Compare C strings for std::sort.  */
+
+static inline bool
+compare_cstrings (const char *str1, const char *str2)
+{
+  return strcmp (str1, str2) < 0;
+}
+
 /* A wrapper for bfd_errmsg to produce a more helpful error message
    in the case of bfd_error_file_ambiguously recognized.
    MATCHING, if non-NULL, is the corresponding argument to
-- 
2.5.5

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

* Get rid of "set language local"? (was: Re: [PATCH 23/40] Make language_def O(1))
  2017-07-20 17:40     ` Pedro Alves
@ 2017-07-20 18:12       ` Pedro Alves
  2017-07-20 23:44         ` Matt Rice
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 18:12 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/20/2017 06:40 PM, Pedro Alves wrote:
> On 07/18/2017 12:03 AM, Keith Seitz wrote:
>> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>>> Note that "local_language_defn" is gone along the way.  AFAICT, it's
>>> just a copy of "auto", so the new code simply maps one to the other.
>>> One fewer place to update when we need to change the language
>>> vector...
>>
>> I've searched the manual, and the "local" language is mentioned only in two places, once in the description of "set language" (where it explains that "local" and "auto" are the same) and once when we describe -data-evaluate-expression's --language flag.
>>
>> Honestly, is there any reason to keep it at all given it is a synonym of auto? [I realize that it doesn't cost much to maintain with this patch.] I'm not asking for any changes in this regard, just throwing the possibility out there. TBH, I didn't even know "local" existed.
> 
> I didn't know about "local" until I stumbled on it working on this
> series, either.  :-)
> 
> I'll send a follow up reply with a different subject
> to see if anyone else has comments on that.

Does anyone here ever use "set language local" instead
of "set language auto"?  Do we need to keep "set language local" ?
Seems odd to have two ways to do the exact same thing.

Thanks,
Pedro Alves

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

* Re: [PATCH 24/40] Per-language symbol name hashing algorithm
  2017-07-18 17:33   ` Keith Seitz
@ 2017-07-20 18:53     ` Pedro Alves
  2017-11-08 16:08       ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 18:53 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/18/2017 06:33 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>> This patch starts fixing this, by doing two things:
>>
>> #1 - adds a language vector method to let each language decide how to
>>      compute a symbol name hash.
>>
>> #2 - makes dictionaries know the language of the symbols they hold,
>>      and then use the dictionaries language to decide which hashing
>>      method to use.
> 
> Me likey! :-)
> 

Ahah, it was your idea.  :-)

> Again, just the usual trivial comments.

[snip]

Thanks much.  I added the missing comments now.

Here's the updated patch.

From 0c5e2585d45f057823a932adf68846d6d543b364 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 20 Jul 2017 19:52:24 +0100
Subject: [PATCH] Per-language symbol name hashing algorithm

Currently, we have a mess of symbol name hashing/comparison routines.
There's msymbol_hash for mangled names, and dict_hash and
msymbol_hash_iw for demangled names.  Then there's strcmp_iw,
strcmp_iw_ordered and Ada's full_match/wild_match, which all have to
agree with the hashing routines.  That's why dict_hash is really about
Ada names.  From the inconsistency department, minimal symbol hashing
doesn't go via dict_hash, so Ada's wild matching can't ever work with
minimal symbols.

This patch starts fixing this, by doing two things:

#1 - adds a language vector method to let each language decide how to
     compute a symbol name hash.

#2 - makes dictionaries know the language of the symbols they hold,
     and then use the dictionaries language to decide which hashing
     method to use.

For now, this is just scaffolding, since all languages install the
default method.  The series will make C++ install its own hashing
method later on, and will add per-language symbol name comparison
routines too.

This patch is based on a patch that Keith wrote for the libcc1/C++ WIP
support.

gdb/ChangeLog:
yyyy-mm-dd  Keith Seitz  <keiths@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_language_defn): Install
	default_search_name_hash.
	* buildsym.c (struct buildsym_compunit): <language>: New field.
	(finish_block_internal): Pass language when creating dictionaries.
	(start_buildsym_compunit, start_symtab): New language parameters.
	Use them.
	(restart_symtab): Pass down compilation unit's language.
	* buildsym.h (enum language): Forward declare.
	(start_symtab): New 'language' parameter.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Install
	default_search_name_hash.
	* coffread.c (coff_start_symtab): Adjust.
	* d-lang.c (d_language_defn): Install default_search_name_hash.
	* dbxread.c (struct symloc): Add 'pst_language' field.
	(PST_LANGUAGE): Define.
	(start_psymtab, read_ofile_symtab): Use it.
	(process_one_symbol): New 'language' parameter.  Pass it down.
	* dictionary.c (struct dictionary) <language>: New field.
	(DICT_LANGUAGE): Define.
	(dict_create_hashed, dict_create_hashed_expandable)
	(dict_create_linear, dict_create_linear_expandable): New parameter
	'language'.  Set the dictionary's language.
	(iter_match_first_hashed): Adjust to rename.
	(insert_symbol_hashed): Assert we don't see mismatching
	languages.  Adjust to rename.
	(dict_hash): Rename to ...
	(default_search_name_hash): ... this and make extern.
	* dictionary.h (struct language_defn): Forward declare.
	(dict_create_hashed): New parameter 'language'.
	* dwarf2read.c (dwarf2_start_symtab): Pass down language.
	* f-lang.c (f_language_defn): Install default_search_name_hash.
	* go-lang.c (go_language_defn): Install default_search_name_hash.
	* jit.c (finalize_symtab): Pass compunit's language to dictionary
	creation.
	* language.c (unknown_language_defn, auto_language_defn):
	* language.h (language_defn::la_search_name_hash): New field.
	(default_search_name_hash): Declare.
	* m2-lang.c (m2_language_defn): Install default_search_name_hash.
	* mdebugread.c (new_block): New parameter 'language'.
	* mdebugread.c (parse_symbol): Pass symbol language to block
	allocation.
	(psymtab_to_symtab_1): Pass down language.
	(new_symtab): Pass compunit's language to block allocation.
	* objc-lang.c (objc_language_defn): Install
	default_search_name_hash.
	* opencl-lang.c (opencl_language_defn):
	* p-lang.c (pascal_language_defn): Install
	default_search_name_hash.
	* rust-lang.c (rust_language_defn): Install
	default_search_name_hash.
	* stabsread.h (enum language): Forward declare.
	(process_one_symbol): Add 'language' parameter.
	* symtab.c (search_name_hash): New function.
	* symtab.h (search_name_hash): Declare.
	* xcoffread.c (read_xcoff_symtab): Pass language to start_symtab.
---
 gdb/ada-lang.c    |  1 +
 gdb/buildsym.c    | 34 +++++++++++++++++++++++-----------
 gdb/buildsym.h    |  4 +++-
 gdb/c-lang.c      |  4 ++++
 gdb/coffread.c    |  4 +++-
 gdb/d-lang.c      |  1 +
 gdb/dbxread.c     | 15 ++++++++++-----
 gdb/dictionary.c  | 55 +++++++++++++++++++++++++++----------------------------
 gdb/dictionary.h  | 42 +++++++++++++++++++++++-------------------
 gdb/dwarf2read.c  |  2 +-
 gdb/f-lang.c      |  1 +
 gdb/go-lang.c     |  1 +
 gdb/jit.c         |  6 ++++--
 gdb/language.c    |  2 ++
 gdb/language.h    | 13 +++++++++++++
 gdb/m2-lang.c     |  1 +
 gdb/mdebugread.c  | 31 +++++++++++++++++--------------
 gdb/objc-lang.c   |  1 +
 gdb/opencl-lang.c |  1 +
 gdb/p-lang.c      |  1 +
 gdb/rust-lang.c   |  1 +
 gdb/stabsread.h   |  3 ++-
 gdb/symtab.c      |  8 ++++++++
 gdb/symtab.h      |  5 +++++
 gdb/xcoffread.c   | 11 +++++++----
 25 files changed, 161 insertions(+), 87 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 280247b..b6eb382 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14002,6 +14002,7 @@ extern const struct language_defn ada_language_defn = {
   c_watch_location_expression,
   ada_get_symbol_name_cmp,	/* la_get_symbol_name_cmp */
   ada_iterate_over_symbols,
+  default_search_name_hash,
   &ada_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index cbad027..94f2510 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -128,6 +128,9 @@ struct buildsym_compunit
 
   /* The compunit we are building.  */
   struct compunit_symtab *compunit_symtab;
+
+  /* Language of this compunit_symtab.  */
+  enum language language;
 };
 
 /* The work-in-progress of the compunit we are building.
@@ -351,20 +354,23 @@ finish_block_internal (struct symbol *symbol,
 
   if (symbol)
     {
-      BLOCK_DICT (block) = dict_create_linear (&objfile->objfile_obstack,
-					       *listhead);
+      BLOCK_DICT (block)
+	= dict_create_linear (&objfile->objfile_obstack,
+			      buildsym_compunit->language, *listhead);
     }
   else
     {
       if (expandable)
 	{
-	  BLOCK_DICT (block) = dict_create_hashed_expandable ();
+	  BLOCK_DICT (block)
+	    = dict_create_hashed_expandable (buildsym_compunit->language);
 	  dict_add_pending (BLOCK_DICT (block), *listhead);
 	}
       else
 	{
 	  BLOCK_DICT (block) =
-	    dict_create_hashed (&objfile->objfile_obstack, *listhead);
+	    dict_create_hashed (&objfile->objfile_obstack,
+				buildsym_compunit->language, *listhead);
 	}
     }
 
@@ -768,7 +774,8 @@ start_subfile (const char *name)
    (or NULL if not known).  */
 
 static struct buildsym_compunit *
-start_buildsym_compunit (struct objfile *objfile, const char *comp_dir)
+start_buildsym_compunit (struct objfile *objfile, const char *comp_dir,
+			 enum language language)
 {
   struct buildsym_compunit *bscu;
 
@@ -777,6 +784,7 @@ start_buildsym_compunit (struct objfile *objfile, const char *comp_dir)
 
   bscu->objfile = objfile;
   bscu->comp_dir = (comp_dir == NULL) ? NULL : xstrdup (comp_dir);
+  bscu->language = language;
 
   /* Initialize the debug format string to NULL.  We may supply it
      later via a call to record_debugformat.  */
@@ -1041,17 +1049,20 @@ prepare_for_building (const char *name, CORE_ADDR start_addr)
    TAG_compile_unit DIE is seen.  It indicates the start of data for
    one original source file.
 
-   NAME is the name of the file (cannot be NULL).  COMP_DIR is the directory in
-   which the file was compiled (or NULL if not known).  START_ADDR is the
-   lowest address of objects in the file (or 0 if not known).  */
+   NAME is the name of the file (cannot be NULL).  COMP_DIR is the
+   directory in which the file was compiled (or NULL if not known).
+   START_ADDR is the lowest address of objects in the file (or 0 if
+   not known).  LANGUAGE is the language of the source file, or
+   language_unknown if not known, in which case it'll be deduced from
+   the filename.  */
 
 struct compunit_symtab *
 start_symtab (struct objfile *objfile, const char *name, const char *comp_dir,
-	      CORE_ADDR start_addr)
+	      CORE_ADDR start_addr, enum language language)
 {
   prepare_for_building (name, start_addr);
 
-  buildsym_compunit = start_buildsym_compunit (objfile, comp_dir);
+  buildsym_compunit = start_buildsym_compunit (objfile, comp_dir, language);
 
   /* Allocate the compunit symtab now.  The caller needs it to allocate
      non-primary symtabs.  It is also needed by get_macro_table.  */
@@ -1088,7 +1099,8 @@ restart_symtab (struct compunit_symtab *cust,
   prepare_for_building (name, start_addr);
 
   buildsym_compunit = start_buildsym_compunit (COMPUNIT_OBJFILE (cust),
-					       COMPUNIT_DIRNAME (cust));
+					       COMPUNIT_DIRNAME (cust),
+					       compunit_language (cust));
   buildsym_compunit->compunit_symtab = cust;
 }
 
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index 60109a0..80ca7da 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -23,6 +23,7 @@ struct objfile;
 struct symbol;
 struct addrmap;
 struct compunit_symtab;
+enum language;
 
 /* This module provides definitions used for creating and adding to
    the symbol table.  These routines are called from various symbol-
@@ -254,7 +255,8 @@ extern record_line_ftype record_line;
 extern struct compunit_symtab *start_symtab (struct objfile *objfile,
 					     const char *name,
 					     const char *comp_dir,
-					     CORE_ADDR start_addr);
+					     CORE_ADDR start_addr,
+					     enum language language);
 
 extern void restart_symtab (struct compunit_symtab *cust,
 			    const char *name, CORE_ADDR start_addr);
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index f86e26e..9749935 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -871,6 +871,7 @@ extern const struct language_defn c_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &c_varobj_ops,
   c_get_compile_context,
   c_compute_program,
@@ -1015,6 +1016,7 @@ extern const struct language_defn cplus_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &cplus_varobj_ops,
   NULL,
   NULL,
@@ -1068,6 +1070,7 @@ extern const struct language_defn asm_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
@@ -1121,6 +1124,7 @@ extern const struct language_defn minimal_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/coffread.c b/gdb/coffread.c
index 9db4792..db0b77a 100644
--- a/gdb/coffread.c
+++ b/gdb/coffread.c
@@ -394,7 +394,9 @@ coff_start_symtab (struct objfile *objfile, const char *name)
 		 NULL,
   /* The start address is irrelevant, since we set
      last_source_start_addr in coff_end_symtab.  */
-		 0);
+		 0,
+  /* Let buildsym.c deduce the language for this symtab.  */
+		 language_unknown);
   record_debugformat ("COFF");
 }
 
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 941d3ed..b89b636 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -247,6 +247,7 @@ extern const struct language_defn d_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/dbxread.c b/gdb/dbxread.c
index daa3ce9..d48575b 100644
--- a/gdb/dbxread.c
+++ b/gdb/dbxread.c
@@ -54,7 +54,6 @@
 #include "cp-support.h"
 #include "psympriv.h"
 #include "block.h"
-
 #include "aout/aout64.h"
 #include "aout/stab_gnu.h"	/* We always use GNU stabs, not
 				   native, now.  */
@@ -92,6 +91,7 @@ struct symloc
     int symbol_offset;
     int string_offset;
     int file_string_offset;
+    enum language pst_language;
   };
 
 #define LDSYMOFF(p) (((struct symloc *)((p)->read_symtab_private))->ldsymoff)
@@ -101,6 +101,7 @@ struct symloc
 #define SYMBOL_OFFSET(p) (SYMLOC(p)->symbol_offset)
 #define STRING_OFFSET(p) (SYMLOC(p)->string_offset)
 #define FILE_STRING_OFFSET(p) (SYMLOC(p)->file_string_offset)
+#define PST_LANGUAGE(p) (SYMLOC(p)->pst_language)
 \f
 
 /* The objfile we are currently reading.  */
@@ -2017,6 +2018,7 @@ start_psymtab (struct objfile *objfile, const char *filename, CORE_ADDR textlow,
 
   /* Deduce the source language from the filename for this psymtab.  */
   psymtab_language = deduce_language_from_filename (filename);
+  PST_LANGUAGE (result) = psymtab_language;
 
   return result;
 }
@@ -2403,7 +2405,8 @@ read_ofile_symtab (struct objfile *objfile, struct partial_symtab *pst)
 		 positive offsets.  */
 	    nlist.n_value = (nlist.n_value ^ 0x80000000) - 0x80000000;
 	  process_one_symbol (type, nlist.n_desc, nlist.n_value,
-			      namestring, section_offsets, objfile);
+			      namestring, section_offsets, objfile,
+			      PST_LANGUAGE (pst));
 	}
       /* We skip checking for a new .o or -l file; that should never
          happen in this routine.  */
@@ -2498,12 +2501,14 @@ cp_set_block_scope (const struct symbol *symbol,
    the pst->section_offsets.  All symbols that refer to memory
    locations need to be offset by these amounts.
    OBJFILE is the object file from which we are reading symbols.  It
-   is used in end_symtab.  */
+   is used in end_symtab.
+   LANGUAGE is the language of the symtab.
+*/
 
 void
 process_one_symbol (int type, int desc, CORE_ADDR valu, const char *name,
 		    const struct section_offsets *section_offsets,
-		    struct objfile *objfile)
+		    struct objfile *objfile, enum language language)
 {
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
   struct context_stack *newobj;
@@ -2717,7 +2722,7 @@ process_one_symbol (int type, int desc, CORE_ADDR valu, const char *name,
       function_start_offset = 0;
 
       start_stabs ();
-      start_symtab (objfile, name, NULL, valu);
+      start_symtab (objfile, name, NULL, valu, language);
       record_debugformat ("stabs");
       break;
 
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index b2cfca2..1ffa4f3 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -165,6 +165,7 @@ struct dictionary_linear_expandable
 
 struct dictionary
 {
+  const struct language_defn *language;
   const struct dict_vector *vector;
   union
   {
@@ -179,6 +180,7 @@ struct dictionary
 /* Accessor macros.  */
 
 #define DICT_VECTOR(d)			(d)->vector
+#define DICT_LANGUAGE(d)                (d)->language
 
 /* These can be used for DICT_HASHED_EXPANDABLE, too.  */
 
@@ -245,8 +247,6 @@ static struct symbol *iter_match_next_hashed (const char *name,
 					      symbol_compare_ftype *compare,
 					      struct dict_iterator *iterator);
 
-static unsigned int dict_hash (const char *string);
-
 /* Functions only for DICT_HASHED.  */
 
 static int size_hashed (const struct dictionary *dict);
@@ -348,12 +348,11 @@ static void expand_hashtable (struct dictionary *dict);
 
 /* The creation functions.  */
 
-/* Create a dictionary implemented via a fixed-size hashtable.  All
-   memory it uses is allocated on OBSTACK; the environment is
-   initialized from SYMBOL_LIST.  */
+/* See dictionary.h.  */
 
 struct dictionary *
 dict_create_hashed (struct obstack *obstack,
+		    enum language language,
 		    const struct pending *symbol_list)
 {
   struct dictionary *retval;
@@ -363,6 +362,7 @@ dict_create_hashed (struct obstack *obstack,
 
   retval = XOBNEW (obstack, struct dictionary);
   DICT_VECTOR (retval) = &dict_hashed_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
 
   /* Calculate the number of symbols, and allocate space for them.  */
   for (list_counter = symbol_list;
@@ -391,17 +391,15 @@ dict_create_hashed (struct obstack *obstack,
   return retval;
 }
 
-/* Create a dictionary implemented via a hashtable that grows as
-   necessary.  The dictionary is initially empty; to add symbols to
-   it, call dict_add_symbol().  Call dict_free() when you're done with
-   it.  */
+/* See dictionary.h.  */
 
 extern struct dictionary *
-dict_create_hashed_expandable (void)
+dict_create_hashed_expandable (enum language language)
 {
   struct dictionary *retval = XNEW (struct dictionary);
 
   DICT_VECTOR (retval) = &dict_hashed_expandable_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
   DICT_HASHED_NBUCKETS (retval) = DICT_EXPANDABLE_INITIAL_CAPACITY;
   DICT_HASHED_BUCKETS (retval) = XCNEWVEC (struct symbol *,
 					   DICT_EXPANDABLE_INITIAL_CAPACITY);
@@ -410,13 +408,11 @@ dict_create_hashed_expandable (void)
   return retval;
 }
 
-/* Create a dictionary implemented via a fixed-size array.  All memory
-   it uses is allocated on OBSTACK; the environment is initialized
-   from the SYMBOL_LIST.  The symbols are ordered in the same order
-   that they're found in SYMBOL_LIST.  */
+/* See dictionary.h.  */
 
 struct dictionary *
 dict_create_linear (struct obstack *obstack,
+		    enum language language,
 		    const struct pending *symbol_list)
 {
   struct dictionary *retval;
@@ -426,6 +422,7 @@ dict_create_linear (struct obstack *obstack,
 
   retval = XOBNEW (obstack, struct dictionary);
   DICT_VECTOR (retval) = &dict_linear_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
 
   /* Calculate the number of symbols, and allocate space for them.  */
   for (list_counter = symbol_list;
@@ -455,17 +452,15 @@ dict_create_linear (struct obstack *obstack,
   return retval;
 }
 
-/* Create a dictionary implemented via an array that grows as
-   necessary.  The dictionary is initially empty; to add symbols to
-   it, call dict_add_symbol().  Call dict_free() when you're done with
-   it.  */
+/* See dictionary.h.  */
 
 struct dictionary *
-dict_create_linear_expandable (void)
+dict_create_linear_expandable (enum language language)
 {
   struct dictionary *retval = XNEW (struct dictionary);
 
   DICT_VECTOR (retval) = &dict_linear_expandable_vector;
+  DICT_LANGUAGE (retval) = language_def (language);
   DICT_LINEAR_NSYMS (retval) = 0;
   DICT_LINEAR_EXPANDABLE_CAPACITY (retval) = DICT_EXPANDABLE_INITIAL_CAPACITY;
   DICT_LINEAR_SYMS (retval)
@@ -638,7 +633,9 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
 			 symbol_compare_ftype *compare,
 			 struct dict_iterator *iterator)
 {
-  unsigned int hash_index = dict_hash (name) % DICT_HASHED_NBUCKETS (dict);
+  unsigned int hash_index
+    = (search_name_hash (DICT_LANGUAGE (dict)->la_language, name)
+       % DICT_HASHED_NBUCKETS (dict));
   struct symbol *sym;
 
   DICT_ITERATOR_DICT (iterator) = dict;
@@ -689,10 +686,15 @@ insert_symbol_hashed (struct dictionary *dict,
 		      struct symbol *sym)
 {
   unsigned int hash_index;
+  unsigned int hash;
   struct symbol **buckets = DICT_HASHED_BUCKETS (dict);
 
-  hash_index = 
-    dict_hash (SYMBOL_SEARCH_NAME (sym)) % DICT_HASHED_NBUCKETS (dict);
+  /* We don't want to insert a symbol into a dictionary of a different
+     language.  The two may not use the same hashing algorithm.  */
+  gdb_assert (SYMBOL_LANGUAGE (sym) == DICT_LANGUAGE (dict)->la_language);
+
+  hash = search_name_hash (SYMBOL_LANGUAGE (sym), SYMBOL_SEARCH_NAME (sym));
+  hash_index = hash % DICT_HASHED_NBUCKETS (dict);
   sym->hash_next = buckets[hash_index];
   buckets[hash_index] = sym;
 }
@@ -765,13 +767,10 @@ expand_hashtable (struct dictionary *dict)
   xfree (old_buckets);
 }
 
-/* Produce an unsigned hash value from STRING0 that is consistent
-   with strcmp_iw, strcmp, and, at least on Ada symbols, wild_match.
-   That is, two identifiers equivalent according to any of those three
-   comparison operators hash to the same value.  */
+/* See dictionary.h.  */
 
-static unsigned int
-dict_hash (const char *string0)
+unsigned int
+default_search_name_hash (const char *string0)
 {
   /* The Ada-encoded version of a name P1.P2...Pn has either the form
      P1__P2__...Pn<suffix> or _ada_P1__P2__...Pn<suffix> (where the Pi
diff --git a/gdb/dictionary.h b/gdb/dictionary.h
index 4f4f160..e4a9315 100644
--- a/gdb/dictionary.h
+++ b/gdb/dictionary.h
@@ -35,42 +35,46 @@ struct dictionary;
 struct symbol;
 struct obstack;
 struct pending;
-
+struct language_defn;
 
 /* The creation functions for various implementations of
    dictionaries.  */
 
-/* Create a dictionary implemented via a fixed-size hashtable.  All
-   memory it uses is allocated on OBSTACK; the environment is
-   initialized from SYMBOL_LIST.  */
+/* Create a dictionary of symbols of language LANGUAGE implemented via
+   a fixed-size hashtable.  All memory it uses is allocated on
+   OBSTACK; the environment is initialized from SYMBOL_LIST.  */
 
 extern struct dictionary *dict_create_hashed (struct obstack *obstack,
+					      enum language language,
 					      const struct pending
 					      *symbol_list);
 
-/* Create a dictionary implemented via a hashtable that grows as
-   necessary.  The dictionary is initially empty; to add symbols to
-   it, call dict_add_symbol().  Call dict_free() when you're done with
-   it.  */
+/* Create a dictionary of symbols of language LANGUAGE, implemented
+   via a hashtable that grows as necessary.  The dictionary is
+   initially empty; to add symbols to it, call dict_add_symbol().
+   Call dict_free() when you're done with it.  */
 
-extern struct dictionary *dict_create_hashed_expandable (void);
+extern struct dictionary *
+  dict_create_hashed_expandable (enum language language);
 
-/* Create a dictionary implemented via a fixed-size array.  All memory
-   it uses is allocated on OBSTACK; the environment is initialized
-   from the SYMBOL_LIST.  The symbols are ordered in the same order
-   that they're found in SYMBOL_LIST.  */
+/* Create a dictionary of symbols of language LANGUAGE, implemented
+   via a fixed-size array.  All memory it uses is allocated on
+   OBSTACK; the environment is initialized from the SYMBOL_LIST.  The
+   symbols are ordered in the same order that they're found in
+   SYMBOL_LIST.  */
 
 extern struct dictionary *dict_create_linear (struct obstack *obstack,
+					      enum language language,
 					      const struct pending
 					      *symbol_list);
 
-/* Create a dictionary implemented via an array that grows as
-   necessary.  The dictionary is initially empty; to add symbols to
-   it, call dict_add_symbol().  Call dict_free() when you're done with
-   it.  */
-
-extern struct dictionary *dict_create_linear_expandable (void);
+/* Create a dictionary of symbols of language LANGUAGE, implemented
+   via an array that grows as necessary.  The dictionary is initially
+   empty; to add symbols to it, call dict_add_symbol().  Call
+   dict_free() when you're done with it.  */
 
+extern struct dictionary *
+  dict_create_linear_expandable (enum language language);
 
 /* The functions providing the interface to dictionaries.  Note that
    the most common parts of the interface, namely symbol lookup, are
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 2c2ecda..3c547c1 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -18874,7 +18874,7 @@ dwarf2_start_symtab (struct dwarf2_cu *cu,
 		     const char *name, const char *comp_dir, CORE_ADDR low_pc)
 {
   struct compunit_symtab *cust
-    = start_symtab (cu->objfile, name, comp_dir, low_pc);
+    = start_symtab (cu->objfile, name, comp_dir, low_pc, cu->language);
 
   record_debugformat ("DWARF 2");
   record_producer (cu->producer);
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 903cfd1..cfef64f 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -293,6 +293,7 @@ extern const struct language_defn f_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 60bb3c5..befd937 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -608,6 +608,7 @@ extern const struct language_defn go_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/jit.c b/gdb/jit.c
index ddf1005..c30a06a 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -661,12 +661,14 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
   size_t blockvector_size;
   CORE_ADDR begin, end;
   struct blockvector *bv;
+  enum language language;
 
   actual_nblocks = FIRST_LOCAL_BLOCK + stab->nblocks;
 
   cust = allocate_compunit_symtab (objfile, stab->file_name);
   allocate_symtab (cust, stab->file_name);
   add_compunit_symtab_to_objfile (cust);
+  language = compunit_language (cust);
 
   /* JIT compilers compile in memory.  */
   COMPUNIT_DIRNAME (cust) = NULL;
@@ -711,7 +713,7 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
 					   "void");
 
       BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack,
-                                                   NULL);
+						   language, NULL);
       /* The address range.  */
       BLOCK_START (new_block) = (CORE_ADDR) gdb_block_iter->begin;
       BLOCK_END (new_block) = (CORE_ADDR) gdb_block_iter->end;
@@ -749,7 +751,7 @@ finalize_symtab (struct gdb_symtab *stab, struct objfile *objfile)
 		   ? allocate_global_block (&objfile->objfile_obstack)
 		   : allocate_block (&objfile->objfile_obstack));
       BLOCK_DICT (new_block) = dict_create_linear (&objfile->objfile_obstack,
-                                                   NULL);
+						   language, NULL);
       BLOCK_SUPERBLOCK (new_block) = block_iter;
       block_iter = new_block;
 
diff --git a/gdb/language.c b/gdb/language.c
index 073039e..8f52b73 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -843,6 +843,7 @@ const struct language_defn unknown_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
@@ -893,6 +894,7 @@ const struct language_defn auto_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/language.h b/gdb/language.h
index f4852c1..cc8ec46 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -392,6 +392,11 @@ struct language_defn
       (const struct block *block, const char *name, domain_enum domain,
        gdb::function_view<symbol_found_callback_ftype> callback);
 
+    /* Hash the given symbol search name.  Use
+       default_search_name_hash if no special treatment is
+       required.  */
+    unsigned int (*la_search_name_hash) (const char *name);
+
     /* Various operations on varobj.  */
     const struct lang_varobj_ops *la_varobj_ops;
 
@@ -611,6 +616,14 @@ void default_print_typedef (struct type *type, struct symbol *new_symbol,
 void default_get_string (struct value *value, gdb_byte **buffer, int *length,
 			 struct type **char_type, const char **charset);
 
+/* Default name hashing function.  */
+
+/* Produce an unsigned hash value from SEARCH_NAME that is consistent
+   with strcmp_iw, strcmp, and, at least on Ada symbols, wild_match.
+   That is, two identifiers equivalent according to any of those three
+   comparison operators hash to the same value.  */
+extern unsigned int default_search_name_hash (const char *search_name);
+
 void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 		   struct type **char_type, const char **charset);
 
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index b9ab2b3..66ac34c 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -396,6 +396,7 @@ extern const struct language_defn m2_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index 8e27194..fa5c14b 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -236,7 +236,7 @@ static struct type *new_type (char *);
 
 enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
 
-static struct block *new_block (enum block_type);
+static struct block *new_block (enum block_type, enum language);
 
 static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
 
@@ -811,7 +811,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	TYPE_PROTOTYPED (SYMBOL_TYPE (s)) = 1;
 
       /* Create and enter a new lexical context.  */
-      b = new_block (FUNCTION_BLOCK);
+      b = new_block (FUNCTION_BLOCK, SYMBOL_LANGUAGE (s));
       SYMBOL_BLOCK_VALUE (s) = b;
       BLOCK_FUNCTION (b) = s;
       BLOCK_START (b) = BLOCK_END (b) = sh->value;
@@ -1144,7 +1144,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	}
 
       top_stack->blocktype = stBlock;
-      b = new_block (NON_FUNCTION_BLOCK);
+      b = new_block (NON_FUNCTION_BLOCK, psymtab_language);
       BLOCK_START (b) = sh->value + top_stack->procadr;
       BLOCK_SUPERBLOCK (b) = top_stack->cur_block;
       top_stack->cur_block = b;
@@ -4026,6 +4026,7 @@ psymtab_to_symtab_1 (struct objfile *objfile,
 	  if (ECOFF_IS_STAB (&sh) || (name[0] == '#'))
 	    {
 	      int type_code = ECOFF_UNMARK_STAB (sh.index);
+	      enum language language = PST_PRIVATE (pst)->pst_language;
 
 	      /* We should never get non N_STAB symbols here, but they
 	         should be harmless, so keep process_one_symbol from
@@ -4053,14 +4054,14 @@ psymtab_to_symtab_1 (struct objfile *objfile,
 		    {
 		      last_symtab_ended = 0;
 		      process_one_symbol (type_code, 0, valu, name,
-					  section_offsets, objfile);
+					  section_offsets, objfile, language);
 		    }
 		}
 	      /* Similarly a hack.  */
 	      else if (name[0] == '#')
 		{
 		  process_one_symbol (N_SLINE, 0, valu, name,
-				      section_offsets, objfile);
+				      section_offsets, objfile, language);
 		}
 	      if (type_code == N_FUN)
 		{
@@ -4721,16 +4722,18 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
   struct compunit_symtab *cust = allocate_compunit_symtab (objfile, name);
   struct symtab *symtab;
   struct blockvector *bv;
+  enum language lang;
 
   add_compunit_symtab_to_objfile (cust);
   symtab = allocate_symtab (cust, name);
 
   SYMTAB_LINETABLE (symtab) = new_linetable (maxlines);
+  lang = compunit_language (cust);
 
   /* All symtabs must have at least two blocks.  */
   bv = new_bvect (2);
-  BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK) = new_block (NON_FUNCTION_BLOCK);
-  BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK) = new_block (NON_FUNCTION_BLOCK);
+  BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK) = new_block (NON_FUNCTION_BLOCK, lang);
+  BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK) = new_block (NON_FUNCTION_BLOCK, lang);
   BLOCK_SUPERBLOCK (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) =
     BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
   COMPUNIT_BLOCKVECTOR (cust) = bv;
@@ -4812,13 +4815,13 @@ new_bvect (int nblocks)
   return bv;
 }
 
-/* Allocate and zero a new block, and set its BLOCK_DICT.  If function
-   is non-zero, assume the block is associated to a function, and make
-   sure that the symbols are stored linearly; otherwise, store them
-   hashed.  */
+/* Allocate and zero a new block of language LANGUAGE, and set its
+   BLOCK_DICT.  If function is non-zero, assume the block is
+   associated to a function, and make sure that the symbols are stored
+   linearly; otherwise, store them hashed.  */
 
 static struct block *
-new_block (enum block_type type)
+new_block (enum block_type type, enum language language)
 {
   /* FIXME: carlton/2003-09-11: This should use allocate_block to
      allocate the block.  Which, in turn, suggests that the block
@@ -4826,9 +4829,9 @@ new_block (enum block_type type)
   struct block *retval = XCNEW (struct block);
 
   if (type == FUNCTION_BLOCK)
-    BLOCK_DICT (retval) = dict_create_linear_expandable ();
+    BLOCK_DICT (retval) = dict_create_linear_expandable (language);
   else
-    BLOCK_DICT (retval) = dict_create_hashed_expandable ();
+    BLOCK_DICT (retval) = dict_create_hashed_expandable (language);
 
   return retval;
 }
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 6ffe85e..4dbc7f2 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -405,6 +405,7 @@ extern const struct language_defn objc_language_defn = {
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index 9b0f015..e5aff0d 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1085,6 +1085,7 @@ extern const struct language_defn opencl_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 439a377..2dca923 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -456,6 +456,7 @@ extern const struct language_defn pascal_language_defn =
   c_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 817976a..f0d9968 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2190,6 +2190,7 @@ extern const struct language_defn rust_language_defn =
   rust_watch_location_expression,
   NULL,				/* la_get_symbol_name_cmp */
   iterate_over_symbols,
+  default_search_name_hash,
   &default_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/stabsread.h b/gdb/stabsread.h
index b37be1a..b803cf9 100644
--- a/gdb/stabsread.h
+++ b/gdb/stabsread.h
@@ -17,6 +17,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 struct objfile;
+enum language;
 
 /* Definitions, prototypes, etc for stabs debugging format support
    functions.
@@ -169,7 +170,7 @@ extern struct partial_symtab *dbx_end_psymtab
 
 extern void process_one_symbol (int, int, CORE_ADDR, const char *,
 				const struct section_offsets *,
-				struct objfile *);
+				struct objfile *, enum language);
 
 extern void elfstab_build_psymtabs (struct objfile *objfile,
 				    asection *stabsect,
diff --git a/gdb/symtab.c b/gdb/symtab.c
index d4e107a..ba2c559 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -1803,6 +1803,14 @@ demangle_for_lookup (const char *name, enum language lang,
   return name;
 }
 
+/* See symtab.h.  */
+
+unsigned int
+search_name_hash (enum language language, const char *search_name)
+{
+  return language_def (language)->la_search_name_hash (search_name);
+}
+
 /* See symtab.h.
 
    This function (or rather its subordinates) have a bunch of loops and
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 35949f0..da5cf25 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -277,6 +277,11 @@ extern const char *symbol_search_name (const struct general_symbol_info *);
 #define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
   (strcmp_iw (SYMBOL_SEARCH_NAME (symbol), (name)) == 0)
 
+/* Compute the hash of the given symbol search name of a symbol of
+   language LANGUAGE.  */
+extern unsigned int search_name_hash (enum language language,
+				      const char *search_name);
+
 /* Classification types for a minimal symbol.  These should be taken as
    "advisory only", since if gdb can't easily figure out a
    classification it simply selects mst_unknown.  It may also have to
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index 138f941..0d6f290 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -1044,7 +1044,8 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
   last_csect_name = 0;
 
   start_stabs ();
-  start_symtab (objfile, filestring, (char *) NULL, file_start_addr);
+  start_symtab (objfile, filestring, (char *) NULL, file_start_addr,
+		language_unknown);
   record_debugformat (debugfmt);
   symnum = ((struct symloc *) pst->read_symtab_private)->first_symnum;
   max_symnum =
@@ -1137,7 +1138,8 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
 	    }
 
 	  start_stabs ();
-	  start_symtab (objfile, "_globals_", (char *) NULL, (CORE_ADDR) 0);
+	  start_symtab (objfile, "_globals_", (char *) NULL, (CORE_ADDR) 0,
+			language_unknown);
 	  record_debugformat (debugfmt);
 	  cur_src_end_addr = first_object_file_end;
 	  /* Done with all files, everything from here on is globals.  */
@@ -1227,7 +1229,7 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
 			  /* Give all csects for this source file the same
 			     name.  */
 			  start_symtab (objfile, filestring, NULL,
-					(CORE_ADDR) 0);
+					(CORE_ADDR) 0, language_unknown);
 			  record_debugformat (debugfmt);
 			}
 
@@ -1347,7 +1349,8 @@ read_xcoff_symtab (struct objfile *objfile, struct partial_symtab *pst)
 	    filestring = cs->c_name;
 
 	  start_stabs ();
-	  start_symtab (objfile, filestring, (char *) NULL, (CORE_ADDR) 0);
+	  start_symtab (objfile, filestring, (char *) NULL, (CORE_ADDR) 0,
+			language_unknown);
 	  record_debugformat (debugfmt);
 	  last_csect_name = 0;
 
-- 
2.5.5

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-07-18 22:31     ` Pedro Alves
@ 2017-07-20 19:00       ` Pedro Alves
  2017-07-20 19:06         ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 19:00 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/18/2017 11:30 PM, Pedro Alves wrote:
> On 07/18/2017 09:14 PM, Keith Seitz wrote:
>> On 06/02/2017 05:22 AM, Pedro Alves wrote:

>> I'm seeing regressions with this patch (no subsequent patch seems to fix it, either):
>>

I setup a F21 VM w/ gcc-gnat-4.9, and was able to reproduce them.

The problem was that there were some code paths that would
result in GDB Ada-encoding an already-encoded name.

Looking at https://sourceware.org/bugzilla/show_bug.cgi?id=12607 I
realized that my "progression" was really a sign of a regression,
and the cause was the same (the resolve_subexp hunk below).

Below's the patch on top of patch #25 that fixes the regressions.
It's the same as you've tested, only cleaned up a little.  Thanks
much for noticing this, and for testing the earlier patch too.

I'll send the updated patch #25 as a reply.

From 5fe82c63f79a3600247f8cd73540eac28e8a0b13 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 20 Jul 2017 12:01:50 +0100
Subject: [PATCH] Fix regressions seen with gcc-gnat-4.9 on F21

---
 gdb/ada-lang.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 94b6757..25cde2c 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -3579,7 +3579,7 @@ resolve_subexp (struct expression **expp, int *pos, int deprocedure_p,
           int n_candidates;
 
           n_candidates =
-            ada_lookup_symbol_list (ada_encode (ada_decoded_op_name (op)),
+            ada_lookup_symbol_list (ada_decoded_op_name (op),
                                     (struct block *) NULL, VAR_DOMAIN,
                                     &candidates);
           i = ada_resolve_function (candidates, n_candidates, argvec, nargs,
@@ -5661,7 +5661,6 @@ add_nonlocal_symbols (struct obstack *obstackp,
   memset (&data, 0, sizeof data);
   data.obstackp = obstackp;
 
-  const char *name = ada_lookup_name (lookup_name);
   bool is_wild_match = lookup_name.ada ().wild_match_p ();
 
   ALL_OBJFILES (objfile)
@@ -5669,12 +5668,14 @@ add_nonlocal_symbols (struct obstack *obstackp,
       data.objfile = objfile;
 
       if (is_wild_match)
-	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
+	objfile->sf->qf->map_matching_symbols (objfile, lookup_name.name ().c_str (),
+					       domain, global,
 					       aux_add_nonlocal_symbols, &data,
 					       symbol_name_match_type::WILD,
 					       NULL);
       else
-	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
+	objfile->sf->qf->map_matching_symbols (objfile, lookup_name.name ().c_str (),
+					       domain, global,
 					       aux_add_nonlocal_symbols, &data,
 					       symbol_name_match_type::FULL,
 					       compare_names);
@@ -5692,14 +5693,14 @@ add_nonlocal_symbols (struct obstack *obstackp,
 
   if (num_defns_collected (obstackp) == 0 && global && !is_wild_match)
     {
+      const char *name = ada_lookup_name (lookup_name);
+      std::string name1 = std::string ("<_ada_") + name + '>';
+
       ALL_OBJFILES (objfile)
         {
-	  char *name1 = (char *) alloca (strlen (name) + sizeof ("_ada_"));
-	  strcpy (name1, "_ada_");
-	  strcpy (name1 + sizeof ("_ada_") - 1, name);
 	  data.objfile = objfile;
-	  objfile->sf->qf->map_matching_symbols (objfile, name1, domain,
-						 global,
+	  objfile->sf->qf->map_matching_symbols (objfile, name1.c_str (),
+						 domain, global,
 						 aux_add_nonlocal_symbols,
 						 &data,
 						 symbol_name_match_type::FULL,
@@ -5888,10 +5889,19 @@ ada_lookup_encoded_symbol (const char *name, const struct block *block,
   struct block_symbol *candidates;
   int n_candidates;
 
+  /* Since we already have an encoded name, wrap it in '<>' to force a
+     verbatim match.  Otherwise, if the name happens to not look like
+     an encoded name (because it doesn't include a "__"),
+     ada_lookup_name_info would re-encode/fold it again, and that
+     would e.g., incorrectly lowercase object renaming names like
+     "R28b" -> "r28b".  */
+  std::string verbatim = std::string ("<") + name + '>';
+
   gdb_assert (info != NULL);
   memset (info, 0, sizeof (struct block_symbol));
 
-  n_candidates = ada_lookup_symbol_list (name, block, domain, &candidates);
+  n_candidates = ada_lookup_symbol_list (verbatim.c_str (), block,
+					 domain, &candidates);
   if (n_candidates == 0)
     return;
 
-- 
2.5.5


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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-07-20 19:00       ` Pedro Alves
@ 2017-07-20 19:06         ` Pedro Alves
  2017-08-08 20:29           ` Keith Seitz
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-07-20 19:06 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/20/2017 08:00 PM, Pedro Alves wrote:

> I'll send the updated patch #25 as a reply.

Here's the updated patch.  I've also force-pushed this to
the users/palves/cxx-breakpoint-improvements branch, meanwhile
rebased to current master.

(I've also fixed a few typos I found meanwhile.)

From beae767464cca5253f15319defb106e0ff7d0b96 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 20 Jul 2017 19:55:10 +0100
Subject: [PATCH] Introduce lookup_name_info and generalize Ada's FULL/WILD
 name matching

Summary:
 - This is preparation for supporting wild name matching on C++ too.
 - This is also preparation for TAB-completion fixes.
 - Makes symbol name matching (think strcmp_iw) be based on a per-language method.
 - Merges completion and non-completion name comparison (think
   language_ops::la_get_symbol_name_cmp generalized).
 - Avoid re-hashing lookup name multiple times
 - Centralizes preparing a name for lookup (Ada name encoding / C++ Demangling),
   both completion and non-completion.
 - Fixes Ada latent bug with verbatim name matches in expressions
 - Makes ada-lang.c use common|symtab.c completion code a bit more.

Ada's wild matching basically means that

 "(gdb) break foo"

will find all methods named "foo" in all packages.  Translating to
C++, it's roughly the same as saying that "break klass::method" sets
breakpoints on all "klass::method" methods of all classes, no matter
the namespace.  A following patch will teach GDB about fullname vs
wild matching for C++ too.  This patch is preparatory work to get
there.

Another idea here is to do symbol name matching based on the symbol
language's algorithm.  I.e., avoid dependency on current language set.

This allows for example doing

  (gdb) b foo::bar< int > (<tab>

and having gdb name match the C++ symbols correctly even if the
current language is C or Assembly (or Rust, or Ada, or ...), which can
easily happen if you step into an Assembly/C runtime library frame.

By encapsulating all the information related to a lookup name in a
class, we can also cache hash computation for a given language in the
lookup name object, to avoid recomputing it over and over.

Similarly, because we don't really know upfront which languages the
lookup name will be matched against, for each language we store the
lookup name transformed into a search name.  E.g., for C++, that means
demangling the name.  But for Ada, it means encoding the name.  This
actually forces us to centralize all the different lookup name
encoding in a central place, resulting in clearer code, IMO.  See
e.g., the new ada_lookup_name_info class.

The lookup name -> symbol search name computation is also done only
once per language.

The old language->la_get_symbol_name_cmp / symbol_name_cmp_ftype are
generalized to work with both completion, and normal symbol look up.

At some point early on, I had separate completion vs non-completion
language vector entry points, but a single method ends up being better
IMO for simplifying things -- the more we merge the completion /
non-completion name lookup code paths, the less changes for bugs
causing completion vs normal lookup finding different symbols.

The ada-lex.l change is necessary because when doing

  (gdb) p <UpperCase>

then the name that is passed to write_ write_var_or_type ->
ada_lookup_symbol_list misses the "<>", i.e., it's just "UpperCase",
and we end up doing a wild match against "UpperCase" lowercased by
ada_lookup_name_info's constructor.  I.e., "uppercase" wouldn't ever
match "UpperCase", and the symbol lookup fails.

This wouldn't cause any regression in the testsuite, but I added a new
test that would pass before the patch and fail after, if it weren't
for that fix.

This is latent bug that happens to go unnoticed because that
particular path was inconsistent with the rest of Ada symbol lookup by
not lowercasing the lookup name.

Ada's symbol_completion_add is deleted, replaced by using common
code's completion_list_add_name.  To make the latter work for Ada, we
needed to add a new output parameter, because Ada wants to return back
a custom completion candidates that are not the symbol name.

With this patch, minimal symbol demangled name hashing is made
consistent with regular symbol hashing.  I.e., it now goes via the
language vector's search_name_hash method too, as I had suggested in a
previous patch.

dw2_expand_symtabs_matching / .gdb_index symbol names were a
challenge.  The problem is that we have no way to telling what is the
language of each symbol name found in the index, until we expand the
corresponding full symbol, which is off course what we're trying to
avoid.  Language information is simply not considered in the index
format...  Since the symbol name hashing and comparison routines are
per-language, we now have a problem.  The patch sorts this out by
matching each name against all languages.  This is inneficient, and
indeed slows down completion several times.  E.g., with:

 $ cat script.cmd
 set pagination off
 set $count = 0
 while $count < 400
   complete b string_prin
   printf "count = %d\n", $count
   set $count = $count + 1
 end

 $ time gdb --batch -q ./gdb-with-index -ex "source script-string_printf.cmd"

I get, before patch (-O2, x86-64):

 real    0m1.773s
 user    0m1.737s
 sys     0m0.040s

While after patch (-O2, x86-64):

 real    0m9.843s
 user    0m9.482s
 sys     0m0.034s

However, the following patch will optimize this, and will actually
make this use case faster compared to the "before patch" above:

 real    0m1.321s
 user    0m1.285s
 sys     0m0.039s

gdb/ChangeLog:
yyyy-mm-dd   Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_encode): Rename to ..
	(ada_encode_1): ... this.  Add throw_errors parameter and handle
	it.
	(ada_encode): Reimplement.
	(match_name): Delete, folded into full_name.
	(resolve_subexp): No longer pass the encoded name to
	ada_lookup_symbol_list.
	(should_use_wild_match): Delete.
	(name_match_type_from_name): New.
	(ada_lookup_simple_minsym): Use lookup_name_info and the
	language's symbol_name_matcher_ftype.
	(add_symbols_from_enclosing_procs, ada_add_local_symbols)
	(ada_add_block_renamings): Adjust to use lookup_name_info.
	(ada_lookup_name): New.
	(add_nonlocal_symbols, ada_add_all_symbols)
	(ada_lookup_symbol_list_worker, ada_lookup_symbol_list)
	(ada_iterate_over_symbols): Adjust to use lookup_name_info.
	(ada_name_for_lookup): Delete.
	(ada_lookup_encoded_symbol): Construct a verbatim name.
	(wild_match): Reverse sense of return type.  Use bool.
	(full_match): Reverse sense of return type.  Inline bits of old
	match_name here.
	(ada_add_block_symbols, ada_add_block_symbols): Adjust to use
	lookup_name_info.
	(symbol_completion_match): Delete, folded into...
	(ada_lookup_name_info::matches): ... .this new method.
	(symbol_completion_add): Delete.
	(ada_collect_symbol_completion_matches): Add name_match_type
	parameter.  Adjust to use lookup_name_info and
	completion_list_add_name.
	(get_var_value, ada_add_global_exceptions): Adjust to use
	lookup_name_info.
	(do_wild_match, do_full_match): New functions.
	(ada_lookup_name_info::ada_lookup_name_info): New method.
	(ada_symbol_name_matches, ada_get_symbol_name_matcher): New
	functions.
	(ada_language_defn): Install ada_get_symbol_name_matcher.
	* ada-lex.l (processId): If name starts with '<', copy it
	verbatim.
	* block.c (block_iter_match_step, block_iter_match_first)
	(block_iter_match_next, block_lookup_symbol)
	(block_lookup_symbol_primary, block_find_symbol): Adjust to use
	lookup_name_info.
	* block.h (block_iter_match_first, block_iter_match_next)
	(ALL_BLOCK_SYMBOLS_WITH_NAME): Adjust to use lookup_name_info.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Adjust comments to
	refer to la_get_symbol_name_matcher.
	* completer.c (complete_files_symbols)
	(collect_explicit_location_matches, symbol_completer): Pass a
	symbol_name_match_type down.
	* completer.h (class completion_match, completion_match_result):
	New classes.
	(completion_tracker::reset_completion_match_result): New method.
	(completion_tracker::m_completion_match_result): New field.
	* cp-support.c (make_symbol_overload_list_block): Adjust to use
	lookup_name_info.
	(cp_fq_symbol_name_matches, cp_get_symbol_name_matcher): New
	functions.
	* cp-support.h (cp_get_symbol_name_matcher): New declaration.
	* d-lang.c: Adjust comments to refer to
	la_get_symbol_name_matcher.
	* dictionary.c (dict_vector) <iter_match_first, iter_match_next>:
	Adjust to use lookup_name_info.
	(dict_iter_match_first, dict_iter_match_next)
	(iter_match_first_hashed, iter_match_next_hashed)
	(iter_match_first_linear, iter_match_next_linear): Adjust to work
	with a lookup_name_info.
	* dictionary.h (dict_iter_match_first, dict_iter_match_next):
	Likewise.
	* dwarf2read.c (dw2_lookup_symbol): Adjust to use lookup_name_info.
	(dw2_map_matching_symbols): Adjust to use symbol_name_match_type.
	(gdb_index_symbol_name_matcher): New class.
	(dw2_expand_symtabs_matching) Adjust to use lookup_name_info and
	gdb_index_symbol_name_matcher.  Accept a NULL symbol_matcher.
	* f-lang.c (f_collect_symbol_completion_matches): Adjust to work
	with a symbol_name_match_type.
	(f_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* go-lang.c (go_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* language.c (default_symbol_name_matcher)
	(language_get_symbol_name_matcher): New functions.
	(unknown_language_defn, auto_language_defn): Adjust comments to
	refer to la_get_symbol_name_matcher.
	* language.h (symbol_name_cmp_ftype): Delete.
	(language_defn) <la_collect_symbol_completion_matches>: Add match
	type parameter.
	<la_get_symbol_name_cmp>: Delete field.
	<la_get_symbol_name_matcher>: New field.
	<la_iterate_over_symbols>: Adjust to use lookup_name_info.
	(default_symbol_name_matcher, language_get_symbol_name_matcher):
	Declare.
	* linespec.c (iterate_over_all_matching_symtabs)
	(iterate_over_file_blocks): Adjust to use lookup_name_info.
	(find_methods): Add language parameter, and use lookup_name_info
	and the language's symbol_name_matcher_ftype.
	(linespec_complete_function): Adjust.
	(linespec_complete_label, linespec_complete): Adjust.
	(lookup_prefix_sym): Use lookup_name_info.
	(add_all_symbol_names_from_pspace): Adjust.
	(find_superclass_methods): Add language parameter and pass it
	down.
	(find_method): Pass symbol language down.
	(find_linespec_symbols): Don't demangle or Ada encode here.
	(search_minsyms_for_name): Add lookup_name_info parameter.
	(add_matching_symbols_to_info): Add name_match_type parameter.
	Use lookup_name_info.
	* linespec.h (linespec_complete_function)
	(linespec_complete_label): Add name_match_type parameter.
	* m2-lang.c (m2_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* minsyms.c: Include <algorithm>.
	(add_minsym_to_demangled_hash_table): Remove table parameter and
	add objfile parameter.  Use search_name_hash, and add language to
	demangled languages vector.
	(struct found_minimal_symbols): New struct.
	(lookup_minimal_symbol_mangled, lookup_minimal_symbol_demangled):
	New functions.
	(lookup_minimal_symbol): Adjust to use them.  Don't canonicalize
	input names here.  Use lookup_name_info instead.  Lookup up
	demangled names once for each language in the demangled names
	vector.
	(iterate_over_minimal_symbols): Use lookup_name_info.  Lookup up
	demangled names once for each language in the demangled names
	vector.
	(build_minimal_symbol_hash_tables): Adjust.
	* minsyms.h (iterate_over_minimal_symbols): Adjust to pass down a
	lookup_name_info.
	* objc-lang.c (objc_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* objfiles.h: Include <vector>.
	(objfile_per_bfd_storage) <demangled_hash_languages>: New field.
	* opencl-lang.c (opencl_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* p-lang.c (pascal_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* psymtab.c (psym_lookup_symbol): Use lookup_name_info.
	(match_partial_symbol): Use symbol_name_match_type and
	lookup_name_info.
	(lookup_partial_symbol): Use lookup_name_info.
	(map_block): Use symbol_name_match_type and lookup_name_info.
	(psym_map_matching_symbols): Use symbol_name_match_type.
	(psymbol_name_matches): New.
	(recursively_search_psymtabs): Use lookup_name_info and
	psymbol_name_matches.
	(psym_expand_symtabs_matching): Use lookup_name_info.
	* rust-lang.c (rust_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* symfile-debug.c (debug_qf_map_matching_symbols)
	(debug_qf_map_matching_symbols): Use symbol_name_match_type.
	(debug_qf_expand_symtabs_matching): use lookup_name_info.
	* symfile.c (expand_symtabs_matching): Use lookup_name_info.
	* symfile.h (quick_symbol_functions) <map_matching_symbols>:
	Adjust to use symbol_name_match_type.
	<expand_symtabs_matching>: Adjust to use lookup_name_info.
	(expand_symtabs_matching): Adjust to use lookup_name_info.
	* symmisc.c (maintenance_expand_symtabs): Use
	lookup_name_info::match_any ().
	* symtab.c (symbol_matches_search_name): New.
	(eq_symbol_entry): Adjust to use lookup_name_info and the
	language's matcher.
	(demangle_for_lookup_info::demangle_for_lookup_info): New.
	(lookup_name_info::match_any): New.
	(iterate_over_symbols, search_symbols): Use lookup_name_info.
	(compare_symbol_name): Add language, lookup_name_info and
	completion_match_result parameters, and use them.
	(completion_list_add_name): Make extern.  Add language and
	lookup_name_info parameters.  Use them.
	(completion_list_add_symbol, completion_list_add_msymbol)
	(completion_list_objc_symbol): Add lookup_name_info parameters and
	adjust.  Pass down language.
	(completion_list_add_fields): Add lookup_name_info parameters and
	adjust.  Pass down language.
	(add_symtab_completions): Add lookup_name_info parameters and
	adjust.
	(default_collect_symbol_completion_matches_break_on): Add
	name_match_type parameter, and use it.  Use lookup_name_info.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches): Add name_match_type
	parameter, and pass it down.
	(collect_symbol_completion_matches_type): Adjust.
	(collect_file_symbol_completion_matches): Add name_match_type
	parameter, and use lookup_name_info.
	* symtab.h: Include <string> and "common/gdb_optional.h".
	(enum class symbol_name_match_type): New.
	(class ada_lookup_name_info): New.
	(struct demangle_for_lookup_info): New.
	(class lookup_name_info): New.
	(symbol_name_matcher_ftype): New.
	(SYMBOL_MATCHES_SEARCH_NAME): Use symbol_matches_search_name.
	(symbol_matches_search_name): Declare.
	(MSYMBOL_MATCHES_SEARCH_NAME): Delete.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches)
	(collect_file_symbol_completion_matches): Add name_match_type
	parameter.
	(iterate_over_symbols): Use lookup_name_info.
	(completion_list_add_name): Declare.
	* utils.c (enum class strncmp_iw_mode): Moved to utils.h.
	(strncmp_iw_with_mode): Now extern.
	* utils.h (enum class strncmp_iw_mode): Moved from utils.c.
	(strncmp_iw_with_mode): Declare.

gdb/testsuite/ChangeLog:
yyyy-mm-dd   Pedro Alves  <palves@redhat.com>

	* gdb.ada/complete.exp (p <Exported_Capitalized>): New test.
	(p Exported_Capitalized): New test.
	(p exported_capitalized): New test.
---
 gdb/ada-lang.c                     | 726 +++++++++++++++++++------------------
 gdb/ada-lex.l                      |  22 +-
 gdb/block.c                        |  38 +-
 gdb/block.h                        |  33 +-
 gdb/c-lang.c                       |   8 +-
 gdb/completer.c                    |   4 +
 gdb/completer.h                    |  75 ++++
 gdb/cp-support.c                   |  39 +-
 gdb/cp-support.h                   |   3 +
 gdb/d-lang.c                       |   2 +-
 gdb/dictionary.c                   |  66 ++--
 gdb/dictionary.h                   |   6 +-
 gdb/dwarf2read.c                   | 100 ++++-
 gdb/f-lang.c                       |   4 +-
 gdb/go-lang.c                      |   2 +-
 gdb/language.c                     |  39 +-
 gdb/language.h                     |  43 ++-
 gdb/linespec.c                     | 132 ++++---
 gdb/m2-lang.c                      |   2 +-
 gdb/minsyms.c                      | 367 ++++++++++++-------
 gdb/minsyms.h                      |   2 +-
 gdb/objc-lang.c                    |   2 +-
 gdb/objfiles.h                     |   7 +
 gdb/opencl-lang.c                  |   2 +-
 gdb/p-lang.c                       |   2 +-
 gdb/psymtab.c                      |  81 +++--
 gdb/rust-lang.c                    |   2 +-
 gdb/symfile-debug.c                |   6 +-
 gdb/symfile.c                      |   2 +
 gdb/symfile.h                      |   4 +-
 gdb/symmisc.c                      |   1 +
 gdb/symtab.c                       | 200 +++++++---
 gdb/symtab.h                       | 294 ++++++++++++++-
 gdb/testsuite/gdb.ada/complete.exp |  10 +
 gdb/utils.c                        |  16 +-
 gdb/utils.h                        |  27 ++
 36 files changed, 1575 insertions(+), 794 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index b6eb382..58d51d7 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -103,16 +103,16 @@ static int ada_type_match (struct type *, struct type *, int);
 
 static int ada_args_match (struct symbol *, struct value **, int);
 
-static int full_match (const char *, const char *);
-
 static struct value *make_array_descriptor (struct type *, struct value *);
 
 static void ada_add_block_symbols (struct obstack *,
-                                   const struct block *, const char *,
-                                   domain_enum, struct objfile *, int);
+				   const struct block *,
+				   const lookup_name_info &lookup_name,
+				   domain_enum, struct objfile *);
 
 static void ada_add_all_symbols (struct obstack *, const struct block *,
-				 const char *, domain_enum, int, int *);
+				 const lookup_name_info &lookup_name,
+				 domain_enum, int, int *);
 
 static int is_nonfunction (struct block_symbol *, int);
 
@@ -202,7 +202,7 @@ static int is_name_suffix (const char *);
 
 static int advance_wild_match (const char **, const char *, int);
 
-static int wild_match (const char *, const char *);
+static bool wild_match (const char *name, const char *patn);
 
 static struct value *ada_coerce_ref (struct value *);
 
@@ -975,11 +975,13 @@ const struct ada_opname_map ada_opname_table[] = {
   {NULL, NULL}
 };
 
-/* The "encoded" form of DECODED, according to GNAT conventions.
-   The result is valid until the next call to ada_encode.  */
+/* The "encoded" form of DECODED, according to GNAT conventions.  The
+   result is valid until the next call to ada_encode.  If
+   THROW_ERRORS, throw an error if invalid operator name is found.
+   Otherwise, return NULL in that case.  */
 
-char *
-ada_encode (const char *decoded)
+static char *
+ada_encode_1 (const char *decoded, bool throw_errors)
 {
   static char *encoding_buffer = NULL;
   static size_t encoding_buffer_size = 0;
@@ -1009,7 +1011,12 @@ ada_encode (const char *decoded)
                && !startswith (p, mapping->decoded); mapping += 1)
             ;
           if (mapping->encoded == NULL)
-            error (_("invalid Ada operator name: %s"), p);
+	    {
+	      if (throw_errors)
+		error (_("invalid Ada operator name: %s"), p);
+	      else
+		return NULL;
+	    }
           strcpy (encoding_buffer + k, mapping->encoded);
           k += strlen (mapping->encoded);
           break;
@@ -1025,6 +1032,15 @@ ada_encode (const char *decoded)
   return encoding_buffer;
 }
 
+/* The "encoded" form of DECODED, according to GNAT conventions.
+   The result is valid until the next call to ada_encode.  */
+
+char *
+ada_encode (const char *decoded)
+{
+  return ada_encode_1 (decoded, true);
+}
+
 /* Return NAME folded to lower case, or, if surrounded by single
    quotes, unfolded, but with the quotes stripped away.  Result good
    to next call.  */
@@ -1489,31 +1505,6 @@ ada_sniff_from_mangled_name (const char *mangled, char **out)
   return 0;
 }
 
-/* Returns non-zero iff SYM_NAME matches NAME, ignoring any trailing
-   suffixes that encode debugging information or leading _ada_ on
-   SYM_NAME (see is_name_suffix commentary for the debugging
-   information that is ignored).  If WILD, then NAME need only match a
-   suffix of SYM_NAME minus the same suffixes.  Also returns 0 if
-   either argument is NULL.  */
-
-static int
-match_name (const char *sym_name, const char *name, int wild)
-{
-  if (sym_name == NULL || name == NULL)
-    return 0;
-  else if (wild)
-    return wild_match (sym_name, name) == 0;
-  else
-    {
-      int len_name = strlen (name);
-
-      return (strncmp (sym_name, name, len_name) == 0
-              && is_name_suffix (sym_name + len_name))
-        || (startswith (sym_name, "_ada_")
-            && strncmp (sym_name + 5, name, len_name) == 0
-            && is_name_suffix (sym_name + len_name + 5));
-    }
-}
 \f
 
                                 /* Arrays */
@@ -3585,7 +3576,7 @@ resolve_subexp (struct expression **expp, int *pos, int deprocedure_p,
           int n_candidates;
 
           n_candidates =
-            ada_lookup_symbol_list (ada_encode (ada_decoded_op_name (op)),
+            ada_lookup_symbol_list (ada_decoded_op_name (op),
                                     (struct block *) NULL, VAR_DOMAIN,
                                     &candidates);
           i = ada_resolve_function (candidates, n_candidates, argvec, nargs,
@@ -4756,16 +4747,18 @@ cache_symbol (const char *name, domain_enum domain, struct symbol *sym,
 \f
                                 /* Symbol Lookup */
 
-/* Return nonzero if wild matching should be used when searching for
-   all symbols matching LOOKUP_NAME.
+/* Return the symbol name match type that should be used used when
+   searching for all symbols matching LOOKUP_NAME.
 
    LOOKUP_NAME is expected to be a symbol name after transformation
    for Ada lookups (see ada_name_for_lookup).  */
 
-static int
-should_use_wild_match (const char *lookup_name)
+static symbol_name_match_type
+name_match_type_from_name (const char *lookup_name)
 {
-  return (strstr (lookup_name, "__") == NULL);
+  return (strstr (lookup_name, "__") == NULL
+	  ? symbol_name_match_type::WILD
+	  : symbol_name_match_type::FULL);
 }
 
 /* Return the result of a standard (literal, C-like) lookup of NAME in
@@ -4935,23 +4928,19 @@ ada_lookup_simple_minsym (const char *name)
   struct bound_minimal_symbol result;
   struct objfile *objfile;
   struct minimal_symbol *msymbol;
-  const int wild_match_p = should_use_wild_match (name);
 
   memset (&result, 0, sizeof (result));
 
-  /* Special case: If the user specifies a symbol name inside package
-     Standard, do a non-wild matching of the symbol name without
-     the "standard__" prefix.  This was primarily introduced in order
-     to allow the user to specifically access the standard exceptions
-     using, for instance, Standard.Constraint_Error when Constraint_Error
-     is ambiguous (due to the user defining its own Constraint_Error
-     entity inside its program).  */
-  if (startswith (name, "standard__"))
-    name += sizeof ("standard__") - 1;
+  symbol_name_match_type match_type = name_match_type_from_name (name);
+  lookup_name_info lookup_name (name, match_type);
+
+  symbol_name_matcher_ftype *match_name
+    = language_get_symbol_name_matcher (language_def (language_ada),
+					lookup_name);
 
   ALL_MSYMBOLS (objfile, msymbol)
   {
-    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), name, wild_match_p)
+    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), lookup_name, NULL)
         && MSYMBOL_TYPE (msymbol) != mst_solib_trampoline)
       {
 	result.minsym = msymbol;
@@ -4971,8 +4960,8 @@ ada_lookup_simple_minsym (const char *name)
 
 static void
 add_symbols_from_enclosing_procs (struct obstack *obstackp,
-                                  const char *name, domain_enum domain,
-                                  int wild_match_p)
+				  const lookup_name_info &lookup_name,
+				  domain_enum domain)
 {
 }
 
@@ -5424,17 +5413,16 @@ remove_irrelevant_renamings (struct block_symbol *syms,
    Note: This function assumes that OBSTACKP has 0 (zero) element in it.  */
 
 static void
-ada_add_local_symbols (struct obstack *obstackp, const char *name,
-                       const struct block *block, domain_enum domain,
-                       int wild_match_p)
+ada_add_local_symbols (struct obstack *obstackp,
+		       const lookup_name_info &lookup_name,
+		       const struct block *block, domain_enum domain)
 {
   int block_depth = 0;
 
   while (block != NULL)
     {
       block_depth += 1;
-      ada_add_block_symbols (obstackp, block, name, domain, NULL,
-			     wild_match_p);
+      ada_add_block_symbols (obstackp, block, lookup_name, domain, NULL);
 
       /* If we found a non-function match, assume that's the one.  */
       if (is_nonfunction (defns_collected (obstackp, 0),
@@ -5447,7 +5435,7 @@ ada_add_local_symbols (struct obstack *obstackp, const char *name,
   /* If no luck so far, try to find NAME as a local symbol in some lexically
      enclosing subprogram.  */
   if (num_defns_collected (obstackp) == 0 && block_depth > 2)
-    add_symbols_from_enclosing_procs (obstackp, name, domain, wild_match_p);
+    add_symbols_from_enclosing_procs (obstackp, lookup_name, domain);
 }
 
 /* An object of this type is used as the user_data argument when
@@ -5501,28 +5489,28 @@ aux_add_nonlocal_symbols (struct block *block, struct symbol *sym, void *data0)
   return 0;
 }
 
-/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are targetted
-   by renamings matching NAME in BLOCK.  Add these symbols to OBSTACKP.  If
-   WILD_MATCH_P is nonzero, perform the naming matching in "wild" mode (see
-   function "wild_match" for more information).  Return whether we found such
-   symbols.  */
+/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are
+   targetted by renamings matching LOOKUP_NAME in BLOCK.  Add these
+   symbols to OBSTACKP.  Return whether we found such symbols.  */
 
 static int
 ada_add_block_renamings (struct obstack *obstackp,
 			 const struct block *block,
-			 const char *name,
-			 domain_enum domain,
-			 int wild_match_p)
+			 const lookup_name_info &lookup_name,
+			 domain_enum domain)
 {
   struct using_direct *renaming;
   int defns_mark = num_defns_collected (obstackp);
 
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (language_def (language_ada),
+					lookup_name);
+
   for (renaming = block_using (block);
        renaming != NULL;
        renaming = renaming->next)
     {
       const char *r_name;
-      int name_match;
 
       /* Avoid infinite recursions: skip this renaming if we are actually
 	 already traversing it.
@@ -5547,11 +5535,13 @@ ada_add_block_renamings (struct obstack *obstackp,
       r_name = (renaming->alias != NULL
 		? renaming->alias
 		: renaming->declaration);
-      name_match
-	= wild_match_p ? wild_match (r_name, name) : strcmp (r_name, name);
-      if (name_match == 0)
-	ada_add_all_symbols (obstackp, block, renaming->declaration, domain,
-			     1, NULL);
+      if (name_match (r_name, lookup_name, NULL))
+	{
+	  lookup_name_info decl_lookup_name (renaming->declaration,
+					     lookup_name.match_type ());
+	  ada_add_all_symbols (obstackp, block, decl_lookup_name, domain,
+			       1, NULL);
+	}
       renaming->searched = 0;
     }
   return num_defns_collected (obstackp) != defns_mark;
@@ -5642,14 +5632,24 @@ compare_names (const char *string1, const char *string2)
   return result;
 }
 
+/* Convenience function to get at the Ada encoded lookup name for
+   LOOKUP_NAME, as a C string.  */
+
+static const char *
+ada_lookup_name (const lookup_name_info &lookup_name)
+{
+  return lookup_name.ada ().lookup_name ().c_str ();
+}
+
 /* Add to OBSTACKP all non-local symbols whose name and domain match
-   NAME and DOMAIN respectively.  The search is performed on GLOBAL_BLOCK
-   symbols if GLOBAL is non-zero, or on STATIC_BLOCK symbols otherwise.  */
+   LOOKUP_NAME and DOMAIN respectively.  The search is performed on
+   GLOBAL_BLOCK symbols if GLOBAL is non-zero, or on STATIC_BLOCK
+   symbols otherwise.  */
 
 static void
-add_nonlocal_symbols (struct obstack *obstackp, const char *name,
-		      domain_enum domain, int global,
-		      int is_wild_match)
+add_nonlocal_symbols (struct obstack *obstackp,
+		      const lookup_name_info &lookup_name,
+		      domain_enum domain, int global)
 {
   struct objfile *objfile;
   struct compunit_symtab *cu;
@@ -5658,50 +5658,57 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
   memset (&data, 0, sizeof data);
   data.obstackp = obstackp;
 
+  bool is_wild_match = lookup_name.ada ().wild_match_p ();
+
   ALL_OBJFILES (objfile)
     {
       data.objfile = objfile;
 
       if (is_wild_match)
-	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
+	objfile->sf->qf->map_matching_symbols (objfile, lookup_name.name ().c_str (),
+					       domain, global,
 					       aux_add_nonlocal_symbols, &data,
-					       wild_match, NULL);
+					       symbol_name_match_type::WILD,
+					       NULL);
       else
-	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
+	objfile->sf->qf->map_matching_symbols (objfile, lookup_name.name ().c_str (),
+					       domain, global,
 					       aux_add_nonlocal_symbols, &data,
-					       full_match, compare_names);
+					       symbol_name_match_type::FULL,
+					       compare_names);
 
       ALL_OBJFILE_COMPUNITS (objfile, cu)
 	{
 	  const struct block *global_block
 	    = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cu), GLOBAL_BLOCK);
 
-	  if (ada_add_block_renamings (obstackp, global_block , name, domain,
-				       is_wild_match))
+	  if (ada_add_block_renamings (obstackp, global_block, lookup_name,
+				       domain))
 	    data.found_sym = 1;
 	}
     }
 
   if (num_defns_collected (obstackp) == 0 && global && !is_wild_match)
     {
+      const char *name = ada_lookup_name (lookup_name);
+      std::string name1 = std::string ("<_ada_") + name + '>';
+
       ALL_OBJFILES (objfile)
         {
-	  char *name1 = (char *) alloca (strlen (name) + sizeof ("_ada_"));
-	  strcpy (name1, "_ada_");
-	  strcpy (name1 + sizeof ("_ada_") - 1, name);
 	  data.objfile = objfile;
-	  objfile->sf->qf->map_matching_symbols (objfile, name1, domain,
-						 global,
+	  objfile->sf->qf->map_matching_symbols (objfile, name1.c_str (),
+						 domain, global,
 						 aux_add_nonlocal_symbols,
 						 &data,
-						 full_match, compare_names);
+						 symbol_name_match_type::FULL,
+						 compare_names);
 	}
     }      	
 }
 
-/* Find symbols in DOMAIN matching NAME, in BLOCK and, if FULL_SEARCH is
-   non-zero, enclosing scope and in global scopes, returning the number of
-   matches.  Add these to OBSTACKP.
+/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if
+   FULL_SEARCH is non-zero, enclosing scope and in global scopes,
+   returning the number of matches.  Add these to OBSTACKP.
 
    When FULL_SEARCH is non-zero, any non-function/non-enumeral
    symbol match within the nest of blocks whose innermost member is BLOCK,
@@ -5709,8 +5716,9 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
    enclosing blocks is returned).  If there are any matches in or
    surrounding BLOCK, then these alone are returned.
 
-   Names prefixed with "standard__" are handled specially: "standard__"
-   is first stripped off, and only static and global symbols are searched.
+   Names prefixed with "standard__" are handled specially:
+   "standard__" is first stripped off (by the lookup_name
+   constructor), and only static and global symbols are searched.
 
    If MADE_GLOBAL_LOOKUP_P is non-null, set it before return to whether we had
    to lookup global symbols.  */
@@ -5718,13 +5726,12 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
 static void
 ada_add_all_symbols (struct obstack *obstackp,
 		     const struct block *block,
-		     const char *name,
+		     const lookup_name_info &lookup_name,
 		     domain_enum domain,
 		     int full_search,
 		     int *made_global_lookup_p)
 {
   struct symbol *sym;
-  const int wild_match_p = should_use_wild_match (name);
 
   if (made_global_lookup_p)
     *made_global_lookup_p = 0;
@@ -5736,25 +5743,21 @@ ada_add_all_symbols (struct obstack *obstackp,
      using, for instance, Standard.Constraint_Error when Constraint_Error
      is ambiguous (due to the user defining its own Constraint_Error
      entity inside its program).  */
-  if (startswith (name, "standard__"))
-    {
-      block = NULL;
-      name = name + sizeof ("standard__") - 1;
-    }
+  if (lookup_name.ada ().standard_p ())
+    block = NULL;
 
   /* Check the non-global symbols.  If we have ANY match, then we're done.  */
 
   if (block != NULL)
     {
       if (full_search)
-	ada_add_local_symbols (obstackp, name, block, domain, wild_match_p);
+	ada_add_local_symbols (obstackp, lookup_name, block, domain);
       else
 	{
 	  /* In the !full_search case we're are being called by
 	     ada_iterate_over_symbols, and we don't want to search
 	     superblocks.  */
-	  ada_add_block_symbols (obstackp, block, name, domain, NULL,
-				 wild_match_p);
+	  ada_add_block_symbols (obstackp, block, lookup_name, domain, NULL);
 	}
       if (num_defns_collected (obstackp) > 0 || !full_search)
 	return;
@@ -5764,10 +5767,11 @@ ada_add_all_symbols (struct obstack *obstackp,
      already performed this search before.  If we have, then return
      the same result.  */
 
-  if (lookup_cached_symbol (name, domain, &sym, &block))
+  if (lookup_cached_symbol (ada_lookup_name (lookup_name),
+			    domain, &sym, &block))
     {
       if (sym != NULL)
-        add_defn_to_vec (obstackp, sym, block);
+	add_defn_to_vec (obstackp, sym, block);
       return;
     }
 
@@ -5776,17 +5780,17 @@ ada_add_all_symbols (struct obstack *obstackp,
 
   /* Search symbols from all global blocks.  */
  
-  add_nonlocal_symbols (obstackp, name, domain, 1, wild_match_p);
+  add_nonlocal_symbols (obstackp, lookup_name, domain, 1);
 
   /* Now add symbols from all per-file blocks if we've gotten no hits
      (not strictly correct, but perhaps better than an error).  */
 
   if (num_defns_collected (obstackp) == 0)
-    add_nonlocal_symbols (obstackp, name, domain, 0, wild_match_p);
+    add_nonlocal_symbols (obstackp, lookup_name, domain, 0);
 }
 
-/* Find symbols in DOMAIN matching NAME, in BLOCK and, if full_search is
-   non-zero, enclosing scope and in global scopes, returning the number of
+/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if full_search
+   is non-zero, enclosing scope and in global scopes, returning the number of
    matches.
    Sets *RESULTS to point to a vector of (SYM,BLOCK) tuples,
    indicating the symbols found and the blocks and symbol tables (if
@@ -5803,19 +5807,19 @@ ada_add_all_symbols (struct obstack *obstackp,
    is first stripped off, and only static and global symbols are searched.  */
 
 static int
-ada_lookup_symbol_list_worker (const char *name, const struct block *block,
+ada_lookup_symbol_list_worker (const lookup_name_info &lookup_name,
+			       const struct block *block,
 			       domain_enum domain,
 			       struct block_symbol **results,
 			       int full_search)
 {
-  const int wild_match_p = should_use_wild_match (name);
   int syms_from_global_search;
   int ndefns;
 
   obstack_free (&symbol_list_obstack, NULL);
   obstack_init (&symbol_list_obstack);
-  ada_add_all_symbols (&symbol_list_obstack, block, name, domain,
-		       full_search, &syms_from_global_search);
+  ada_add_all_symbols (&symbol_list_obstack, block, lookup_name,
+		       domain, full_search, &syms_from_global_search);
 
   ndefns = num_defns_collected (&symbol_list_obstack);
   *results = defns_collected (&symbol_list_obstack, 1);
@@ -5823,32 +5827,37 @@ ada_lookup_symbol_list_worker (const char *name, const struct block *block,
   ndefns = remove_extra_symbols (*results, ndefns);
 
   if (ndefns == 0 && full_search && syms_from_global_search)
-    cache_symbol (name, domain, NULL, NULL);
+    cache_symbol (ada_lookup_name (lookup_name), domain, NULL, NULL);
 
   if (ndefns == 1 && full_search && syms_from_global_search)
-    cache_symbol (name, domain, (*results)[0].symbol, (*results)[0].block);
+    cache_symbol (ada_lookup_name (lookup_name), domain,
+		  (*results)[0].symbol, (*results)[0].block);
 
   ndefns = remove_irrelevant_renamings (*results, ndefns, block);
   return ndefns;
 }
 
-/* Find symbols in DOMAIN matching NAME0, in BLOCK0 and enclosing scope and
+/* Find symbols in DOMAIN matching NAME, in BLOCK and enclosing scope and
    in global scopes, returning the number of matches, and setting *RESULTS
    to a vector of (SYM,BLOCK) tuples.
    See ada_lookup_symbol_list_worker for further details.  */
 
 int
-ada_lookup_symbol_list (const char *name0, const struct block *block0,
+ada_lookup_symbol_list (const char *name, const struct block *block,
 			domain_enum domain, struct block_symbol **results)
 {
-  return ada_lookup_symbol_list_worker (name0, block0, domain, results, 1);
+  symbol_name_match_type name_match_type = name_match_type_from_name (name);
+  lookup_name_info lookup_name (name, name_match_type);
+
+  return ada_lookup_symbol_list_worker (lookup_name, block, domain, results, 1);
 }
 
 /* Implementation of the la_iterate_over_symbols method.  */
 
 static void
 ada_iterate_over_symbols
-  (const struct block *block, const char *name, domain_enum domain,
+  (const struct block *block, const lookup_name_info &name,
+   domain_enum domain,
    gdb::function_view<symbol_found_callback_ftype> callback)
 {
   int ndefs, i;
@@ -5862,24 +5871,6 @@ ada_iterate_over_symbols
     }
 }
 
-/* If NAME is the name of an entity, return a string that should
-   be used to look that entity up in Ada units.
-
-   NAME can have any form that the "break" or "print" commands might
-   recognize.  In other words, it does not have to be the "natural"
-   name, or the "encoded" name.  */
-
-std::string
-ada_name_for_lookup (const char *name)
-{
-  int nlen = strlen (name);
-
-  if (name[0] == '<' && name[nlen - 1] == '>')
-    return std::string (name + 1, nlen - 2);
-  else
-    return ada_encode (ada_fold_name (name));
-}
-
 /* The result is as for ada_lookup_symbol_list with FULL_SEARCH set
    to 1, but choosing the first symbol found if there are multiple
    choices.
@@ -5895,10 +5886,19 @@ ada_lookup_encoded_symbol (const char *name, const struct block *block,
   struct block_symbol *candidates;
   int n_candidates;
 
+  /* Since we already have an encoded name, wrap it in '<>' to force a
+     verbatim match.  Otherwise, if the name happens to not look like
+     an encoded name (because it doesn't include a "__"),
+     ada_lookup_name_info would re-encode/fold it again, and that
+     would e.g., incorrectly lowercase object renaming names like
+     "R28b" -> "r28b".  */
+  std::string verbatim = std::string ("<") + name + '>';
+
   gdb_assert (info != NULL);
   memset (info, 0, sizeof (struct block_symbol));
 
-  n_candidates = ada_lookup_symbol_list (name, block, domain, &candidates);
+  n_candidates = ada_lookup_symbol_list (verbatim.c_str (), block,
+					 domain, &candidates);
   if (n_candidates == 0)
     return;
 
@@ -6179,11 +6179,12 @@ advance_wild_match (const char **namep, const char *name0, int target0)
   return 1;
 }
 
-/* Return 0 iff NAME encodes a name of the form prefix.PATN.  Ignores any
-   informational suffixes of NAME (i.e., for which is_name_suffix is
-   true).  Assumes that PATN is a lower-cased Ada simple name.  */
+/* Return true iff NAME encodes a name of the form prefix.PATN.
+   Ignores any informational suffixes of NAME (i.e., for which
+   is_name_suffix is true).  Assumes that PATN is a lower-cased Ada
+   simple name.  */
 
-static int
+static bool
 wild_match (const char *name, const char *patn)
 {
   const char *p;
@@ -6199,39 +6200,49 @@ wild_match (const char *name, const char *patn)
 	    if (*p != *name)
 	      break;
 	  if (*p == '\0' && is_name_suffix (name))
-	    return match != name0 && !is_valid_name_for_wild_match (name0);
+	    return match == name0 || is_valid_name_for_wild_match (name0);
 
 	  if (name[-1] == '_')
 	    name -= 1;
 	}
       if (!advance_wild_match (&name, name0, *patn))
-	return 1;
+	return false;
     }
 }
 
-/* Returns 0 iff symbol name SYM_NAME matches SEARCH_NAME, apart from
-   informational suffix.  */
+/* Returns true iff symbol name SYM_NAME matches SEARCH_NAME, ignoring
+   any trailing suffixes that encode debugging information or leading
+   _ada_ on SYM_NAME (see is_name_suffix commentary for the debugging
+   information that is ignored).  */
 
-static int
+static bool
 full_match (const char *sym_name, const char *search_name)
 {
-  return !match_name (sym_name, search_name, 0);
-}
+  size_t search_name_len = strlen (search_name);
 
+  if (strncmp (sym_name, search_name, search_name_len) == 0
+      && is_name_suffix (sym_name + search_name_len))
+    return true;
 
-/* Add symbols from BLOCK matching identifier NAME in DOMAIN to
-   vector *defn_symbols, updating the list of symbols in OBSTACKP 
-   (if necessary).  If WILD, treat as NAME with a wildcard prefix.
-   OBJFILE is the section containing BLOCK.  */
+  if (startswith (sym_name, "_ada_")
+      && strncmp (sym_name + 5, search_name, search_name_len) == 0
+      && is_name_suffix (sym_name + search_name_len + 5))
+    return true;
+
+  return false;
+}
+
+/* Add symbols from BLOCK matching LOOKUP_NAME in DOMAIN to vector
+   *defn_symbols, updating the list of symbols in OBSTACKP (if
+   necessary).  OBJFILE is the section containing BLOCK.  */
 
 static void
 ada_add_block_symbols (struct obstack *obstackp,
-                       const struct block *block, const char *name,
-                       domain_enum domain, struct objfile *objfile,
-                       int wild)
+		       const struct block *block,
+		       const lookup_name_info &lookup_name,
+		       domain_enum domain, struct objfile *objfile)
 {
   struct block_iterator iter;
-  int name_len = strlen (name);
   /* A matching argument symbol, if any.  */
   struct symbol *arg_sym;
   /* Set true when we find a matching non-argument symbol.  */
@@ -6240,56 +6251,31 @@ ada_add_block_symbols (struct obstack *obstackp,
 
   arg_sym = NULL;
   found_sym = 0;
-  if (wild)
+  for (sym = block_iter_match_first (block, lookup_name, &iter);
+       sym != NULL;
+       sym = block_iter_match_next (lookup_name, &iter))
     {
-      for (sym = block_iter_match_first (block, name, wild_match, &iter);
-	   sym != NULL; sym = block_iter_match_next (name, wild_match, &iter))
-      {
-        if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                   SYMBOL_DOMAIN (sym), domain)
-            && wild_match (SYMBOL_LINKAGE_NAME (sym), name) == 0)
-          {
-	    if (SYMBOL_CLASS (sym) == LOC_UNRESOLVED)
-	      continue;
-	    else if (SYMBOL_IS_ARGUMENT (sym))
-	      arg_sym = sym;
-	    else
-	      {
-                found_sym = 1;
-                add_defn_to_vec (obstackp,
-                                 fixup_symbol_section (sym, objfile),
-                                 block);
-              }
-          }
-      }
-    }
-  else
-    {
-     for (sym = block_iter_match_first (block, name, full_match, &iter);
-	  sym != NULL; sym = block_iter_match_next (name, full_match, &iter))
-      {
-        if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                   SYMBOL_DOMAIN (sym), domain))
-          {
-	    if (SYMBOL_CLASS (sym) != LOC_UNRESOLVED)
-	      {
-		if (SYMBOL_IS_ARGUMENT (sym))
-		  arg_sym = sym;
-		else
-		  {
-		    found_sym = 1;
-		    add_defn_to_vec (obstackp,
-				     fixup_symbol_section (sym, objfile),
-				     block);
-		  }
-	      }
-          }
-      }
+      if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
+				 SYMBOL_DOMAIN (sym), domain))
+	{
+	  if (SYMBOL_CLASS (sym) != LOC_UNRESOLVED)
+	    {
+	      if (SYMBOL_IS_ARGUMENT (sym))
+		arg_sym = sym;
+	      else
+		{
+		  found_sym = 1;
+		  add_defn_to_vec (obstackp,
+				   fixup_symbol_section (sym, objfile),
+				   block);
+		}
+	    }
+	}
     }
 
   /* Handle renamings.  */
 
-  if (ada_add_block_renamings (obstackp, block, name, domain, wild))
+  if (ada_add_block_renamings (obstackp, block, lookup_name, domain))
     found_sym = 1;
 
   if (!found_sym && arg_sym != NULL)
@@ -6299,10 +6285,13 @@ ada_add_block_symbols (struct obstack *obstackp,
                        block);
     }
 
-  if (!wild)
+  if (!lookup_name.ada ().wild_match_p ())
     {
       arg_sym = NULL;
       found_sym = 0;
+      const std::string &ada_lookup_name = lookup_name.ada ().lookup_name ();
+      const char *name = ada_lookup_name.c_str ();
+      size_t name_len = ada_lookup_name.size ();
 
       ALL_BLOCK_SYMBOLS (block, iter, sym)
       {
@@ -6353,51 +6342,39 @@ ada_add_block_symbols (struct obstack *obstackp,
 
                                 /* Symbol Completion */
 
-/* If SYM_NAME is a completion candidate for TEXT, return this symbol
-   name in a form that's appropriate for the completion.  The result
-   does not need to be deallocated, but is only good until the next call.
-
-   TEXT_LEN is equal to the length of TEXT.
-   Perform a wild match if WILD_MATCH_P is set.
-   ENCODED_P should be set if TEXT represents the start of a symbol name
-   in its encoded form.  */
+/* See symtab.h.  */
 
-static const char *
-symbol_completion_match (const char *sym_name,
-                         const char *text, int text_len,
-                         int wild_match_p, int encoded_p)
+bool
+ada_lookup_name_info::matches
+  (const char *sym_name,
+   symbol_name_match_type match_type,
+   completion_match *comp_match) const
 {
-  const int verbatim_match = (text[0] == '<');
-  int match = 0;
-
-  if (verbatim_match)
-    {
-      /* Strip the leading angle bracket.  */
-      text = text + 1;
-      text_len--;
-    }
+  bool match = false;
+  const char *text = m_encoded_name.c_str ();
+  size_t text_len = m_encoded_name.size ();
 
   /* First, test against the fully qualified name of the symbol.  */
 
   if (strncmp (sym_name, text, text_len) == 0)
-    match = 1;
+    match = true;
 
-  if (match && !encoded_p)
+  if (match && !m_encoded_p)
     {
       /* One needed check before declaring a positive match is to verify
          that iff we are doing a verbatim match, the decoded version
          of the symbol name starts with '<'.  Otherwise, this symbol name
          is not a suitable completion.  */
       const char *sym_name_copy = sym_name;
-      int has_angle_bracket;
+      bool has_angle_bracket;
 
       sym_name = ada_decode (sym_name);
       has_angle_bracket = (sym_name[0] == '<');
-      match = (has_angle_bracket == verbatim_match);
+      match = (has_angle_bracket == m_verbatim_p);
       sym_name = sym_name_copy;
     }
 
-  if (match && !verbatim_match)
+  if (match && !m_verbatim_p)
     {
       /* When doing non-verbatim match, another check that needs to
          be done is to verify that the potentially matching symbol name
@@ -6408,12 +6385,12 @@ symbol_completion_match (const char *sym_name,
 
       for (tmp = sym_name; *tmp != '\0' && !isupper (*tmp); tmp++);
       if (*tmp != '\0')
-        match = 0;
+	match = false;
     }
 
   /* Second: Try wild matching...  */
 
-  if (!match && wild_match_p)
+  if (!match && m_wild_match_p)
     {
       /* Since we are doing wild matching, this means that TEXT
          may represent an unqualified symbol name.  We therefore must
@@ -6421,91 +6398,48 @@ symbol_completion_match (const char *sym_name,
       sym_name = ada_unqualified_name (ada_decode (sym_name));
 
       if (strncmp (sym_name, text, text_len) == 0)
-        match = 1;
+	match = true;
     }
 
-  /* Finally: If we found a mach, prepare the result to return.  */
+  /* Finally: If we found a match, prepare the result to return.  */
 
   if (!match)
-    return NULL;
-
-  if (verbatim_match)
-    sym_name = add_angle_brackets (sym_name);
-
-  if (!encoded_p)
-    sym_name = ada_decode (sym_name);
-
-  return sym_name;
-}
-
-/* A companion function to ada_collect_symbol_completion_matches().
-   Check if SYM_NAME represents a symbol which name would be suitable
-   to complete TEXT (TEXT_LEN is the length of TEXT), in which case it
-   is added as a completion match to TRACKER.
-
-   ORIG_TEXT is the string original string from the user command
-   that needs to be completed.  WORD is the entire command on which
-   completion should be performed.  These two parameters are used to
-   determine which part of the symbol name should be added to the
-   completion vector.
-   if WILD_MATCH_P is set, then wild matching is performed.
-   ENCODED_P should be set if TEXT represents a symbol name in its
-   encoded formed (in which case the completion should also be
-   encoded).  */
-
-static void
-symbol_completion_add (completion_tracker &tracker,
-		       const char *sym_name,
-                       const char *text, int text_len,
-                       const char *orig_text, const char *word,
-                       int wild_match_p, int encoded_p)
-{
-  const char *match = symbol_completion_match (sym_name, text, text_len,
-                                               wild_match_p, encoded_p);
-  char *completion;
+    return false;
 
-  if (match == NULL)
-    return;
+  if (comp_match != NULL)
+    {
+      std::string &match_str = comp_match->storage ();
 
-  /* We found a match, so add the appropriate completion to the given
-     string vector.  */
+      if (!m_encoded_p)
+	{
+	  match_str = ada_decode (sym_name);
+	  comp_match->set_match (match_str.c_str ());
+	}
+      else
+	{
+	  if (m_verbatim_p)
+	    match_str = add_angle_brackets (sym_name);
+	  else
+	    match_str = sym_name;
 
-  if (word == orig_text)
-    {
-      completion = (char *) xmalloc (strlen (match) + 5);
-      strcpy (completion, match);
-    }
-  else if (word > orig_text)
-    {
-      /* Return some portion of sym_name.  */
-      completion = (char *) xmalloc (strlen (match) + 5);
-      strcpy (completion, match + (word - orig_text));
-    }
-  else
-    {
-      /* Return some of ORIG_TEXT plus sym_name.  */
-      completion = (char *) xmalloc (strlen (match) + (orig_text - word) + 5);
-      strncpy (completion, word, orig_text - word);
-      completion[orig_text - word] = '\0';
-      strcat (completion, match);
+	  comp_match->set_match (match_str.c_str ());
+	}
     }
 
-  tracker.add_completion (gdb::unique_xmalloc_ptr<char> (completion));
+  return true;
 }
 
-/* Add the list of possible symbol names completing TEXT0 to TRACKER.
+/* Add the list of possible symbol names completing TEXT to TRACKER.
    WORD is the entire command on which completion is made.  */
 
 static void
 ada_collect_symbol_completion_matches (completion_tracker &tracker,
 				       complete_symbol_mode mode,
-				       const char *text0, const char *word,
+				       symbol_name_match_type name_match_type,
+				       const char *text, const char *word,
 				       enum type_code code)
 {
-  char *text;
   int text_len;
-  int wild_match_p;
-  int encoded_p;
   struct symbol *sym;
   struct compunit_symtab *s;
   struct minimal_symbol *msymbol;
@@ -6517,39 +6451,15 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
   gdb_assert (code == TYPE_CODE_UNDEF);
 
-  if (text0[0] == '<')
-    {
-      text = xstrdup (text0);
-      make_cleanup (xfree, text);
-      text_len = strlen (text);
-      wild_match_p = 0;
-      encoded_p = 1;
-    }
-  else
-    {
-      text = xstrdup (ada_encode (text0));
-      make_cleanup (xfree, text);
-      text_len = strlen (text);
-      for (i = 0; i < text_len; i++)
-        text[i] = tolower (text[i]);
+  text_len = strlen (text);
 
-      encoded_p = (strstr (text0, "__") != NULL);
-      /* If the name contains a ".", then the user is entering a fully
-         qualified entity name, and the match must not be done in wild
-         mode.  Similarly, if the user wants to complete what looks like
-         an encoded name, the match must not be done in wild mode.  */
-      wild_match_p = (strchr (text0, '.') == NULL && !encoded_p);
-    }
+  lookup_name_info lookup_name (std::string (text, text_len),
+				name_match_type, true);
 
   /* First, look at the partial symtab symbols.  */
   expand_symtabs_matching (NULL,
-			   [&] (const char *symname)
-			   {
-			     return symbol_completion_match (symname,
-							     text, text_len,
-							     wild_match_p,
-							     encoded_p);
-			   },
+			   lookup_name,
+			   NULL,
 			   NULL,
 			   ALL_DOMAIN);
 
@@ -6561,9 +6471,12 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
   ALL_MSYMBOLS (objfile, msymbol)
   {
     QUIT;
-    symbol_completion_add (tracker, MSYMBOL_LINKAGE_NAME (msymbol),
-			   text, text_len, text0, word, wild_match_p,
-			   encoded_p);
+
+    completion_list_add_name (tracker,
+			      MSYMBOL_LANGUAGE (msymbol),
+			      MSYMBOL_LINKAGE_NAME (msymbol),
+			      lookup_name,
+			      text, text_len, text, word);
   }
 
   /* Search upwards from currently selected frame (so that we can
@@ -6576,9 +6489,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
       ALL_BLOCK_SYMBOLS (b, iter, sym)
       {
-	symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                               text, text_len, text0, word,
-                               wild_match_p, encoded_p);
+	completion_list_add_name (tracker,
+				  SYMBOL_LANGUAGE (sym),
+				  SYMBOL_LINKAGE_NAME (sym),
+				  lookup_name,
+				  text, text_len, text, word);
       }
     }
 
@@ -6591,9 +6506,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
     b = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (s), GLOBAL_BLOCK);
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                             text, text_len, text0, word,
-                             wild_match_p, encoded_p);
+      completion_list_add_name (tracker,
+				SYMBOL_LANGUAGE (sym),
+				SYMBOL_LINKAGE_NAME (sym),
+				lookup_name,
+				text, text_len, text, word);
     }
   }
 
@@ -6606,9 +6523,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
       continue;
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                             text, text_len, text0, word,
-                             wild_match_p, encoded_p);
+      completion_list_add_name (tracker,
+				SYMBOL_LANGUAGE (sym),
+				SYMBOL_LINKAGE_NAME (sym),
+				lookup_name,
+				text, text_len, text, word);
     }
   }
 
@@ -11613,11 +11532,12 @@ scan_discrim_bound (const char *str, int k, struct value *dval, LONGEST * px,
 static struct value *
 get_var_value (const char *name, const char *err_msg)
 {
-  struct block_symbol *syms;
-  int nsyms;
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
-  nsyms = ada_lookup_symbol_list (name, get_selected_block (0), VAR_DOMAIN,
-                                  &syms);
+  struct block_symbol *syms;
+  int nsyms = ada_lookup_symbol_list_worker (lookup_name,
+					     get_selected_block (0),
+					     VAR_DOMAIN, &syms, 1);
 
   if (nsyms != 1)
     {
@@ -13312,6 +13232,7 @@ ada_add_global_exceptions (compiled_regex *preg,
      regular expression used to do the matching refers to the natural
      name.  So match against the decoded name.  */
   expand_symtabs_matching (NULL,
+			   lookup_name_info::match_any (),
 			   [&] (const char *search_name)
 			   {
 			     const char *decoded = ada_decode (search_name);
@@ -13920,16 +13841,113 @@ static const struct exp_descriptor ada_exp_descriptor = {
   ada_evaluate_subexp
 };
 
-/* Implement the "la_get_symbol_name_cmp" language_defn method
-   for Ada.  */
+/* symbol_name_matcher_ftype adapter for wild_match.  */
+
+static bool
+do_wild_match (const char *symbol_search_name,
+	       const lookup_name_info &lookup_name,
+	       completion_match *match)
+{
+  return wild_match (symbol_search_name, ada_lookup_name (lookup_name));
+}
+
+/* symbol_name_matcher_ftype adapter for full_match.  */
+
+static bool
+do_full_match (const char *symbol_search_name,
+	       const lookup_name_info &lookup_name,
+	       completion_match *match)
+{
+  return full_match (symbol_search_name, ada_lookup_name (lookup_name));
+}
+
+/* Build the Ada lookup name for LOOKUP_NAME.  */
+
+ada_lookup_name_info::ada_lookup_name_info (const lookup_name_info &lookup_name)
+{
+  const std::string &user_name = lookup_name.name ();
+
+  if (user_name[0] == '<')
+    {
+      if (user_name.back () == '>')
+	m_encoded_name = user_name.substr (1, user_name.size () - 2);
+      else
+	m_encoded_name = user_name.substr (1, user_name.size () - 1);
+      m_encoded_p = true;
+      m_verbatim_p = true;
+      m_wild_match_p = false;
+      m_standard_p = false;
+    }
+  else
+    {
+      m_verbatim_p = false;
+
+      m_encoded_p = user_name.find ("__") != std::string::npos;
 
-static symbol_name_cmp_ftype
-ada_get_symbol_name_cmp (const char *lookup_name)
+      if (!m_encoded_p)
+	{
+	  const char *folded = ada_fold_name (user_name.c_str ());
+	  const char *encoded = ada_encode_1 (folded, false);
+	  if (encoded != NULL)
+	    m_encoded_name = encoded;
+	  else
+	    m_encoded_name = user_name;
+	}
+      else
+	m_encoded_name = user_name;
+
+      /* Handle the 'package Standard' special case.  See description
+	 of m_standard_p.  */
+      if (startswith (m_encoded_name.c_str (), "standard__"))
+	{
+	  m_encoded_name = m_encoded_name.substr (sizeof ("standard__") - 1);
+	  m_standard_p = true;
+	}
+      else
+	m_standard_p = false;
+
+      /* If the name contains a ".", then the user is entering a fully
+	 qualified entity name, and the match must not be done in wild
+	 mode.  Similarly, if the user wants to complete what looks
+	 like an encoded name, the match must not be done in wild
+	 mode.  Also, in the standard__ special case always do
+	 non-wild matching.  */
+      m_wild_match_p
+	= (lookup_name.match_type () != symbol_name_match_type::FULL
+	   && !m_encoded_p
+	   && !m_standard_p
+	   && user_name.find ('.') == std::string::npos);
+    }
+}
+
+/* symbol_name_matcher_ftype method for Ada.  This only handles
+   completion mode.  */
+
+static bool
+ada_symbol_name_matches (const char *symbol_search_name,
+			 const lookup_name_info &lookup_name,
+			 completion_match *match)
 {
-  if (should_use_wild_match (lookup_name))
-    return wild_match;
+  return lookup_name.ada ().matches (symbol_search_name,
+				     lookup_name.match_type (),
+				     match);
+}
+
+/* Implement the "la_get_symbol_name_matcher" language_defn method for
+   Ada.  */
+
+static symbol_name_matcher_ftype *
+ada_get_symbol_name_matcher (const lookup_name_info &lookup_name)
+{
+  if (lookup_name.completion_mode ())
+    return ada_symbol_name_matches;
   else
-    return compare_names;
+    {
+      if (lookup_name.ada ().wild_match_p ())
+	return do_wild_match;
+      else
+	return do_full_match;
+    }
 }
 
 /* Implement the "la_read_var_value" language_defn method for Ada.  */
@@ -14000,7 +14018,7 @@ extern const struct language_defn ada_language_defn = {
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  ada_get_symbol_name_cmp,	/* la_get_symbol_name_cmp */
+  ada_get_symbol_name_matcher,	/* la_get_symbol_name_matcher */
   ada_iterate_over_symbols,
   default_search_name_hash,
   &ada_varobj_ops,
diff --git a/gdb/ada-lex.l b/gdb/ada-lex.l
index fe97352..d30485c 100644
--- a/gdb/ada-lex.l
+++ b/gdb/ada-lex.l
@@ -420,13 +420,12 @@ processReal (struct parser_state *par_state, const char *num0)
 /* Store a canonicalized version of NAME0[0..LEN-1] in yylval.ssym.  The
    resulting string is valid until the next call to ada_parse.  If
    NAME0 contains the substring "___", it is assumed to be already
-   encoded and the resulting name is equal to it.  Otherwise, it differs
+   encoded and the resulting name is equal to it.  Similarly, if the name
+   starts with '<', it is copied verbatim.  Otherwise, it differs
    from NAME0 in that:
-    + Characters between '...' or <...> are transfered verbatim to 
-      yylval.ssym.
-    + <, >, and trailing "'" characters in quoted sequences are removed
-      (a leading quote is preserved to indicate that the name is not to be
-      GNAT-encoded).
+    + Characters between '...' are transfered verbatim to yylval.ssym.
+    + Trailing "'" characters in quoted sequences are removed (a leading quote is
+      preserved to indicate that the name is not to be GNAT-encoded).
     + Unquoted whitespace is removed.
     + Unquoted alphabetic characters are mapped to lower case.
    Result is returned as a struct stoken, but for convenience, the string
@@ -444,7 +443,7 @@ processId (const char *name0, int len)
   while (len > 0 && isspace (name0[len-1]))
     len -= 1;
 
-  if (strstr (name0, "___") != NULL)
+  if (name0[0] == '<' || strstr (name0, "___") != NULL)
     {
       strncpy (name, name0, len);
       name[len] = '\000';
@@ -478,15 +477,6 @@ processId (const char *name0, int len)
 	  while (i0 < len && name0[i0] != '\'');
 	  i0 += 1;
 	  break;
-	case '<':
-	  i0 += 1;
-	  while (i0 < len && name0[i0] != '>')
-	    {
-	      name[i] = name0[i0];
-	      i += 1; i0 += 1;
-	    }
-	  i0 += 1;
-	  break;
 	}
     }
   name[i] = '\000';
diff --git a/gdb/block.c b/gdb/block.c
index 1c343aa..a8075a1 100644
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -595,8 +595,7 @@ block_iterator_next (struct block_iterator *iterator)
 
 static struct symbol *
 block_iter_match_step (struct block_iterator *iterator,
-		       const char *name,
-		       symbol_compare_ftype *compare,
+		       const lookup_name_info &name,
 		       int first)
 {
   struct symbol *sym;
@@ -618,10 +617,10 @@ block_iter_match_step (struct block_iterator *iterator,
 	  block = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cust),
 				     iterator->which);
 	  sym = dict_iter_match_first (BLOCK_DICT (block), name,
-				       compare, &iterator->dict_iter);
+				       &iterator->dict_iter);
 	}
       else
-	sym = dict_iter_match_next (name, compare, &iterator->dict_iter);
+	sym = dict_iter_match_next (name, &iterator->dict_iter);
 
       if (sym != NULL)
 	return sym;
@@ -638,30 +637,27 @@ block_iter_match_step (struct block_iterator *iterator,
 
 struct symbol *
 block_iter_match_first (const struct block *block,
-			const char *name,
-			symbol_compare_ftype *compare,
+			const lookup_name_info &name,
 			struct block_iterator *iterator)
 {
   initialize_block_iterator (block, iterator);
 
   if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_match_first (block->dict, name, compare,
-				  &iterator->dict_iter);
+    return dict_iter_match_first (block->dict, name, &iterator->dict_iter);
 
-  return block_iter_match_step (iterator, name, compare, 1);
+  return block_iter_match_step (iterator, name, 1);
 }
 
 /* See block.h.  */
 
 struct symbol *
-block_iter_match_next (const char *name,
-		       symbol_compare_ftype *compare,
+block_iter_match_next (const lookup_name_info &name,
 		       struct block_iterator *iterator)
 {
   if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_match_next (name, compare, &iterator->dict_iter);
+    return dict_iter_match_next (name, &iterator->dict_iter);
 
-  return block_iter_match_step (iterator, name, compare, 0);
+  return block_iter_match_step (iterator, name, 0);
 }
 
 /* See block.h.
@@ -682,11 +678,13 @@ block_lookup_symbol (const struct block *block, const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   if (!BLOCK_FUNCTION (block))
     {
       struct symbol *other = NULL;
 
-      ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+      ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
 	{
 	  if (SYMBOL_DOMAIN (sym) == domain)
 	    return sym;
@@ -713,7 +711,7 @@ block_lookup_symbol (const struct block *block, const char *name,
 
       struct symbol *sym_found = NULL;
 
-      ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+      ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				     SYMBOL_DOMAIN (sym), domain))
@@ -738,14 +736,16 @@ block_lookup_symbol_primary (const struct block *block, const char *name,
   struct symbol *sym, *other;
   struct dict_iterator dict_iter;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   /* Verify BLOCK is STATIC_BLOCK or GLOBAL_BLOCK.  */
   gdb_assert (BLOCK_SUPERBLOCK (block) == NULL
 	      || BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL);
 
   other = NULL;
-  for (sym = dict_iter_match_first (block->dict, name, strcmp_iw, &dict_iter);
+  for (sym = dict_iter_match_first (block->dict, lookup_name, &dict_iter);
        sym != NULL;
-       sym = dict_iter_match_next (name, strcmp_iw, &dict_iter))
+       sym = dict_iter_match_next (lookup_name, &dict_iter))
     {
       if (SYMBOL_DOMAIN (sym) == domain)
 	return sym;
@@ -772,11 +772,13 @@ block_find_symbol (const struct block *block, const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   /* Verify BLOCK is STATIC_BLOCK or GLOBAL_BLOCK.  */
   gdb_assert (BLOCK_SUPERBLOCK (block) == NULL
 	      || BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL);
 
-  ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+  ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
     {
       /* MATCHER is deliberately called second here so that it never sees
 	 a non-domain-matching symbol.  */
diff --git a/gdb/block.h b/gdb/block.h
index 1741e52..0326c18 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -237,27 +237,22 @@ extern struct symbol *block_iterator_first (const struct block *block,
 extern struct symbol *block_iterator_next (struct block_iterator *iterator);
 
 /* Initialize ITERATOR to point at the first symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (which must use
-   the same conventions as strcmp_iw and be compatible with any
-   block hashing function), and return that first symbol, or NULL
-   if there are no such symbols.  */
+   SYMBOL_SEARCH_NAME matches NAME, and return that first symbol, or
+   NULL if there are no such symbols.  */
 
 extern struct symbol *block_iter_match_first (const struct block *block,
-					      const char *name,
-					      symbol_compare_ftype *compare,
+					      const lookup_name_info &name,
 					      struct block_iterator *iterator);
 
 /* Advance ITERATOR to point at the next symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (see
-   block_iter_match_first), or NULL if there are no more such symbols.
-   Don't call this if you've previously received NULL from 
+   SYMBOL_SEARCH_NAME matches NAME, or NULL if there are no more such
+   symbols.  Don't call this if you've previously received NULL from
    block_iterator_match_first or block_iterator_match_next on this
    iteration.  And don't call it unless ITERATOR was created by a
-   previous call to block_iter_match_first with the same NAME and COMPARE.  */
+   previous call to block_iter_match_first with the same NAME.  */
 
-extern struct symbol *block_iter_match_next (const char *name,
-					     symbol_compare_ftype *compare,
-					     struct block_iterator *iterator);
+extern struct symbol *block_iter_match_next
+  (const lookup_name_info &name, struct block_iterator *iterator);
 
 /* Search BLOCK for symbol NAME in DOMAIN.  */
 
@@ -316,14 +311,14 @@ extern int block_find_non_opaque_type_preferred (struct symbol *sym,
        (sym);						\
        (sym) = block_iterator_next (&(iter)))
 
-/* Macro to loop through all symbols with name NAME in BLOCK,
-   in no particular order.  ITER helps keep track of the iteration, and
-   must be a struct block_iterator.  SYM points to the current symbol.  */
+/* Macro to loop through all symbols in BLOCK with a name that matches
+   NAME, in no particular order.  ITER helps keep track of the
+   iteration, and must be a struct block_iterator.  SYM points to the
+   current symbol.  */
 
 #define ALL_BLOCK_SYMBOLS_WITH_NAME(block, name, iter, sym)		\
-  for ((sym) = block_iter_match_first ((block), (name),			\
-				       strcmp_iw, &(iter));		\
+  for ((sym) = block_iter_match_first ((block), (name), &(iter));	\
        (sym) != NULL;							\
-       (sym) = block_iter_match_next ((name), strcmp_iw, &(iter)))
+       (sym) = block_iter_match_next ((name), &(iter)))
 
 #endif /* BLOCK_H */
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 9749935..49077c7 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -869,7 +869,7 @@ extern const struct language_defn c_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &c_varobj_ops,
@@ -1014,7 +1014,7 @@ extern const struct language_defn cplus_language_defn =
   cp_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  cp_get_symbol_name_matcher,
   iterate_over_symbols,
   default_search_name_hash,
   &cplus_varobj_ops,
@@ -1068,7 +1068,7 @@ extern const struct language_defn asm_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
@@ -1122,7 +1122,7 @@ extern const struct language_defn minimal_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/completer.c b/gdb/completer.c
index a029263..16fc4ae 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -499,6 +499,7 @@ complete_files_symbols (completion_tracker &tracker,
     {
       collect_file_symbol_completion_matches (tracker,
 					      complete_symbol_mode::EXPRESSION,
+					      symbol_name_match_type::EXPRESSION,
 					      symbol_start, word,
 					      file_to_match);
       xfree (file_to_match);
@@ -509,6 +510,7 @@ complete_files_symbols (completion_tracker &tracker,
 
       collect_symbol_completion_matches (tracker,
 					 complete_symbol_mode::EXPRESSION,
+					 symbol_name_match_type::EXPRESSION,
 					 symbol_start, word);
       /* If text includes characters which cannot appear in a file
 	 name, they cannot be asking for completion on files.  */
@@ -551,6 +553,7 @@ complete_files_symbols (completion_tracker &tracker,
 	 on the entire text as a symbol.  */
       collect_symbol_completion_matches (tracker,
 					 complete_symbol_mode::EXPRESSION,
+					 symbol_name_match_type::EXPRESSION,
 					 orig_text, word);
     }
 }
@@ -1104,6 +1107,7 @@ symbol_completer (struct cmd_list_element *ignore,
 		  const char *text, const char *word)
 {
   collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
 				     text, word);
 }
 
diff --git a/gdb/completer.h b/gdb/completer.h
index f68c6dc..1958808 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -68,6 +68,62 @@ struct match_list_displayer
    calls free on each element.  */
 typedef std::vector<gdb::unique_xmalloc_ptr<char>> completion_list;
 
+/* The result of a successful completion match.  When doing symbol
+   comparison, we use the symbol search name for the symbol name match
+   check, but the matched name that is shown to the user may be
+   different.  For example, Ada uses encoded names for lookup, but
+   then wants to decode the symbol name to show to the user, and also
+   in some cases wrap the matched name in "<sym>" (meaning we can't
+   always use the symbol's print name.  */
+
+class completion_match
+{
+public:
+  /* Get the completion match result.  See m_match/m_storage's
+     descriptions.  */
+  const char *match ()
+  { return m_match; }
+
+  /* Set the completion match result.  See m_match/m_storage's
+     descriptions.  */
+  void set_match (const char *match)
+  { m_match = match; }
+
+  /* Get temporary storage for generating a match result, dynamically.
+     The built string is only good until the next clear() call.  I.e.,
+     good until the next symbol comparison.  */
+  std::string &storage ()
+  { return m_storage; }
+
+  /* Prepare for another completion matching sequence.  */
+  void clear ()
+  {
+    m_match = NULL;
+    m_storage.clear ();
+  }
+
+private:
+  /* The completion match result.  This can either be a pointer into
+     M_STORAGE string, or it can be a pointer into the some other
+     string that outlives the completion matching sequence (usually, a
+     pointer to a symbol's name.  */
+  const char *m_match;
+
+  /* Storage a symbol comparison routine can use for generating a
+     match result, dynamically.  The built string is only good until
+     the next clear() call.  I.e., good until the next symbol
+     comparison.  */
+  std::string m_storage;
+};
+
+/* Convenience aggregate holding info returned by the symbol name
+   matching routines (see symbol_name_matcher_ftype).  */
+struct completion_match_result
+{
+  /* The completion match candidate.  */
+  completion_match match;
+};
+
 /* The final result of a completion that is handed over to either
    readline or the "completion" command (which pretends to be
    readline).  Mainly a wrapper for a readline-style match list array,
@@ -207,6 +263,18 @@ public:
      already have.  */
   bool completes_to_completion_word (const char *word);
 
+  /* Get a reference to the shared (between all the multiple symbol
+     name comparison calls) completion_match_result object, ready for
+     another symbol name match sequence.  */
+  completion_match_result &reset_completion_match_result ()
+  {
+    completion_match_result &res = m_completion_match_result;
+
+    /* Clear any previous match.  */
+    res.match.clear ();
+    return m_completion_match_result;
+  }
+
   /* True if we have any completion match recorded.  */
   bool have_completions () const
   { return !m_entries_vec.empty (); }
@@ -232,6 +300,13 @@ private:
      to hand over to readline.  */
   void recompute_lowest_common_denominator (const char *new_match);
 
+  /* Completion match outputs returned by the symbol name matching
+     routines (see symbol_name_matcher_ftype).  These results are only
+     valid for a single match call.  This is here in order to be able
+     to conveniently share the same storage among all the calls to the
+     symbol name matching routines.  */
+  completion_match_result m_completion_match_result;
+
   /* The completion matches found so far, in a vector.  */
   completion_list m_entries_vec;
 
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index df9a563..95e7cb8 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1189,7 +1189,9 @@ make_symbol_overload_list_block (const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
-  ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
+  ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
     overload_list_add_symbol (sym, name);
 }
 
@@ -1592,6 +1594,41 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
   return *demangled != NULL;
 }
 
+/* C++ symbol_name_matcher_ftype implementation.  */
+
+static bool
+cp_fq_symbol_name_matches (const char *symbol_search_name,
+			   const lookup_name_info &lookup_name,
+			   completion_match *match)
+{
+  /* Get the demangled name.  */
+  const std::string &name = lookup_name.cplus ().lookup_name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  if (strncmp_iw_with_mode (symbol_search_name,
+			    name.c_str (), name.size (),
+			    mode) == 0)
+    {
+      if (match != NULL)
+	match->set_match (symbol_search_name);
+      return true;
+    }
+
+  return false;
+}
+
+/* Implement the "la_get_symbol_name_matcher" language_defn method for
+   C++.  */
+
+symbol_name_matcher_ftype *
+cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
+{
+  return cp_fq_symbol_name_matches;
+}
+
 /* Don't allow just "maintenance cplus".  */
 
 static  void
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 37b281f..3a42cd6 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -107,6 +107,9 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
 extern struct type *cp_lookup_rtti_type (const char *name,
 					 struct block *block);
 
+extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
+  (const lookup_name_info &lookup_name);
+
 /* Functions/variables from cp-namespace.c.  */
 
 extern int cp_is_in_anonymous (const char *symbol_name);
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index b89b636..1834ccc 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -245,7 +245,7 @@ extern const struct language_defn d_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index 1ffa4f3..8277c5d 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -115,11 +115,9 @@ struct dict_vector
   struct symbol *(*iterator_next) (struct dict_iterator *iterator);
   /* Functions to iterate over symbols with a given name.  */
   struct symbol *(*iter_match_first) (const struct dictionary *dict,
-				      const char *name,
-				      symbol_compare_ftype *equiv,
+				      const lookup_name_info &name,
 				      struct dict_iterator *iterator);
-  struct symbol *(*iter_match_next) (const char *name,
-				     symbol_compare_ftype *equiv,
+  struct symbol *(*iter_match_next) (const lookup_name_info &name,
 				     struct dict_iterator *iterator);
   /* A size function, for maint print symtabs.  */
   int (*size) (const struct dictionary *dict);
@@ -239,12 +237,10 @@ static struct symbol *iterator_first_hashed (const struct dictionary *dict,
 static struct symbol *iterator_next_hashed (struct dict_iterator *iterator);
 
 static struct symbol *iter_match_first_hashed (const struct dictionary *dict,
-					       const char *name,
-					       symbol_compare_ftype *compare,
+					       const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
-static struct symbol *iter_match_next_hashed (const char *name,
-					      symbol_compare_ftype *compare,
+static struct symbol *iter_match_next_hashed (const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
 /* Functions only for DICT_HASHED.  */
@@ -269,12 +265,10 @@ static struct symbol *iterator_first_linear (const struct dictionary *dict,
 static struct symbol *iterator_next_linear (struct dict_iterator *iterator);
 
 static struct symbol *iter_match_first_linear (const struct dictionary *dict,
-					       const char *name,
-					       symbol_compare_ftype *compare,
+					       const lookup_name_info &name,
 					       struct dict_iterator *iterator);
 
-static struct symbol *iter_match_next_linear (const char *name,
-					      symbol_compare_ftype *compare,
+static struct symbol *iter_match_next_linear (const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
 static int size_linear (const struct dictionary *dict);
@@ -526,19 +520,18 @@ dict_iterator_next (struct dict_iterator *iterator)
 
 struct symbol *
 dict_iter_match_first (const struct dictionary *dict,
-		       const char *name, symbol_compare_ftype *compare,
+		       const lookup_name_info &name,
 		       struct dict_iterator *iterator)
 {
-  return (DICT_VECTOR (dict))->iter_match_first (dict, name,
-						 compare, iterator);
+  return (DICT_VECTOR (dict))->iter_match_first (dict, name, iterator);
 }
 
 struct symbol *
-dict_iter_match_next (const char *name, symbol_compare_ftype *compare,
+dict_iter_match_next (const lookup_name_info &name,
 		      struct dict_iterator *iterator)
 {
   return (DICT_VECTOR (DICT_ITERATOR_DICT (iterator)))
-    ->iter_match_next (name, compare, iterator);
+    ->iter_match_next (name, iterator);
 }
 
 int
@@ -629,13 +622,15 @@ iterator_hashed_advance (struct dict_iterator *iterator)
 }
 
 static struct symbol *
-iter_match_first_hashed (const struct dictionary *dict, const char *name,
-			 symbol_compare_ftype *compare,
+iter_match_first_hashed (const struct dictionary *dict,
+			 const lookup_name_info &name,
 			 struct dict_iterator *iterator)
 {
-  unsigned int hash_index
-    = (search_name_hash (DICT_LANGUAGE (dict)->la_language, name)
-       % DICT_HASHED_NBUCKETS (dict));
+  const language_defn *lang = DICT_LANGUAGE (dict);
+  unsigned int hash_index = (name.search_name_hash (lang->la_language)
+			     % DICT_HASHED_NBUCKETS (dict));
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
   struct symbol *sym;
 
   DICT_ITERATOR_DICT (iterator) = dict;
@@ -649,11 +644,8 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
        sym = sym->hash_next)
     {
       /* Warning: the order of arguments to compare matters!  */
-      if (compare (SYMBOL_SEARCH_NAME (sym), name) == 0)
-	{
-	  break;
-	}
-	
+      if (matches_name (SYMBOL_SEARCH_NAME (sym), name, NULL))
+	break;
     }
 
   DICT_ITERATOR_CURRENT (iterator) = sym;
@@ -661,16 +653,19 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
 }
 
 static struct symbol *
-iter_match_next_hashed (const char *name, symbol_compare_ftype *compare,
+iter_match_next_hashed (const lookup_name_info &name,
 			struct dict_iterator *iterator)
 {
+  const language_defn *lang = DICT_LANGUAGE (DICT_ITERATOR_DICT (iterator));
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
   struct symbol *next;
 
   for (next = DICT_ITERATOR_CURRENT (iterator)->hash_next;
        next != NULL;
        next = next->hash_next)
     {
-      if (compare (SYMBOL_SEARCH_NAME (next), name) == 0)
+      if (matches_name (SYMBOL_SEARCH_NAME (next), name, NULL))
 	break;
     }
 
@@ -863,27 +858,32 @@ iterator_next_linear (struct dict_iterator *iterator)
 
 static struct symbol *
 iter_match_first_linear (const struct dictionary *dict,
-			 const char *name, symbol_compare_ftype *compare,
+			 const lookup_name_info &name,
 			 struct dict_iterator *iterator)
 {
   DICT_ITERATOR_DICT (iterator) = dict;
   DICT_ITERATOR_INDEX (iterator) = -1;
 
-  return iter_match_next_linear (name, compare, iterator);
+  return iter_match_next_linear (name, iterator);
 }
 
 static struct symbol *
-iter_match_next_linear (const char *name, symbol_compare_ftype *compare,
+iter_match_next_linear (const lookup_name_info &name,
 			struct dict_iterator *iterator)
 {
   const struct dictionary *dict = DICT_ITERATOR_DICT (iterator);
+  const language_defn *lang = DICT_LANGUAGE (dict);
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
+
   int i, nsyms = DICT_LINEAR_NSYMS (dict);
   struct symbol *sym, *retval = NULL;
 
   for (i = DICT_ITERATOR_INDEX (iterator) + 1; i < nsyms; ++i)
     {
       sym = DICT_LINEAR_SYM (dict, i);
-      if (compare (SYMBOL_SEARCH_NAME (sym), name) == 0)
+
+      if (matches_name (SYMBOL_SEARCH_NAME (sym), name, NULL))
 	{
 	  retval = sym;
 	  break;
diff --git a/gdb/dictionary.h b/gdb/dictionary.h
index e4a9315..e65026b 100644
--- a/gdb/dictionary.h
+++ b/gdb/dictionary.h
@@ -133,8 +133,7 @@ extern struct symbol *dict_iterator_next (struct dict_iterator *iterator);
    if there are no such symbols.  */
 
 extern struct symbol *dict_iter_match_first (const struct dictionary *dict,
-					     const char *name,
-					     symbol_compare_ftype *compare,
+					     const lookup_name_info &name,
 					     struct dict_iterator *iterator);
 
 /* Advance ITERATOR to point at the next symbol in DICT whose
@@ -145,8 +144,7 @@ extern struct symbol *dict_iter_match_first (const struct dictionary *dict,
    iteration.  And don't call it unless ITERATOR was created by a
    previous call to dict_iter_match_first with the same NAME and COMPARE.  */
 
-extern struct symbol *dict_iter_match_next (const char *name,
-					    symbol_compare_ftype *compare,
+extern struct symbol *dict_iter_match_next (const lookup_name_info &name,
 					    struct dict_iterator *iterator);
 
 /* Return some notion of the size of the dictionary: the number of
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 3c547c1..28236aa 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -3881,6 +3881,8 @@ dw2_lookup_symbol (struct objfile *objfile, int block_index,
 
   dw2_setup (objfile);
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   index = dwarf2_per_objfile->index_table;
 
   /* index is NULL if OBJF_READNOW.  */
@@ -3907,10 +3909,10 @@ dw2_lookup_symbol (struct objfile *objfile, int block_index,
 	     information (but NAME might contain it).  */
 
 	  if (sym != NULL
-	      && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	    return stab;
 	  if (with_opaque != NULL
-	      && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, lookup_name))
 	    stab_best = stab;
 
 	  /* Keep looking through other CUs.  */
@@ -4055,7 +4057,7 @@ dw2_map_matching_symbols (struct objfile *objfile,
 			  int global,
 			  int (*callback) (struct block *,
 					   struct symbol *, void *),
-			  void *data, symbol_compare_ftype *match,
+			  void *data, symbol_name_match_type match,
 			  symbol_compare_ftype *ordered_compare)
 {
   /* Currently unimplemented; used for Ada.  The function can be called if the
@@ -4063,10 +4065,97 @@ dw2_map_matching_symbols (struct objfile *objfile,
      does not look for non-Ada symbols this function should just return.  */
 }
 
+/* Symbol name matcher for .gdb_index names.
+
+   Symbol names in .gdb_index have a few particularities:
+
+   - There's no indication of which is the language of each symbol.
+
+     Since each language has its own symbol name matching algorithm,
+     and we don't know which language is the right one, we must match
+     each symbol against all languages.
+
+   - Symbol names in the index have no overload (parameter)
+     information.  I.e., in C++, "foo(int)" and "foo(long)" both
+     appear as "foo" in the index, for example.
+
+     This means that the lookup names passed to the symbol name
+     matcher functions must have no parameter information either
+     because (e.g.) symbol search name "foo" does not match
+     lookup-name "foo(int)" [while swapping search name for lookup
+     name would match].
+*/
+class gdb_index_symbol_name_matcher
+{
+public:
+  /* Prepares the vector of comparison functions for LOOKUP_NAME.  */
+  gdb_index_symbol_name_matcher (const lookup_name_info &lookup_name);
+
+  /* Walk all the matcher routines and match SYMBOL_NAME against them.
+     Returns true if any matcher matches.  */
+  bool matches (const char *symbol_name);
+
+private:
+  /* A reference to the lookup name we're matching against.  */
+  const lookup_name_info &m_lookup_name;
+
+  /* A vector holding all the different symbol name matchers, for all
+     languages.  */
+  std::vector<symbol_name_matcher_ftype *> m_symbol_name_matcher_funcs;
+};
+
+gdb_index_symbol_name_matcher::gdb_index_symbol_name_matcher
+  (const lookup_name_info &lookup_name)
+    : m_lookup_name (lookup_name)
+{
+  /* Prepare the vector of comparison functions upfront, to avoid
+     doing the same work for each symbol.  Care is taken to avoid
+     matching with the same matcher more than once if/when multiple
+     languages use the same matcher function.  */
+  auto &matchers = m_symbol_name_matcher_funcs;
+  matchers.reserve (nr_languages);
+
+  for (int i = 0; i < nr_languages; i++)
+    {
+      const language_defn *lang = language_def ((enum language) i);
+      if (lang->la_get_symbol_name_matcher != NULL)
+	{
+	  symbol_name_matcher_ftype *name_matcher
+	    = lang->la_get_symbol_name_matcher (m_lookup_name);
+
+	  /* Don't insert the same comparison routine more than once.
+	     Note that we do this linear walk instead of a cheaper
+	     sorted insert, or use a std::set or something like that,
+	     because relative order of function addresses is not
+	     stable.  This is not a problem in practice because the
+	     number of supported languages is low, and the cost here
+	     is tiny compared to the number of searches we'll do
+	     afterwards using this object.  */
+	  if (std::find (matchers.begin (), matchers.end (), name_matcher)
+	      == matchers.end ())
+	    matchers.push_back (name_matcher);
+	}
+    }
+  if (std::find (matchers.begin (), matchers.end (),
+		 default_symbol_name_matcher) == matchers.end ())
+    matchers.push_back (default_symbol_name_matcher);
+}
+
+bool
+gdb_index_symbol_name_matcher::matches (const char *symbol_name)
+{
+  for (auto matches_name : m_symbol_name_matcher_funcs)
+    if (matches_name (symbol_name, m_lookup_name, NULL))
+      return true;
+
+  return false;
+}
+
 static void
 dw2_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -4154,6 +4243,8 @@ dw2_expand_symtabs_matching
 	}
     }
 
+  gdb_index_symbol_name_matcher lookup_name_matcher (lookup_name);
+
   for (iter = 0; iter < index->symbol_table_slots; ++iter)
     {
       offset_type idx = 2 * iter;
@@ -4168,7 +4259,8 @@ dw2_expand_symtabs_matching
 
       name = index->constant_pool + MAYBE_SWAP (index->symbol_table[idx]);
 
-      if (!symbol_matcher (name))
+      if (!lookup_name_matcher.matches (name)
+	  || (symbol_matcher != NULL && !symbol_matcher (name)))
 	continue;
 
       /* The name was matched, now expand corresponding CUs that were
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index cfef64f..bbda645 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -231,10 +231,12 @@ f_word_break_characters (void)
 static void
 f_collect_symbol_completion_matches (completion_tracker &tracker,
 				     complete_symbol_mode mode,
+				     symbol_name_match_type compare_name,
 				     const char *text, const char *word,
 				     enum type_code code)
 {
   default_collect_symbol_completion_matches_break_on (tracker, mode,
+						      compare_name,
 						      text, word, ":", code);
 }
 
@@ -291,7 +293,7 @@ extern const struct language_defn f_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index befd937..9c6ae549 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -606,7 +606,7 @@ extern const struct language_defn go_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/language.c b/gdb/language.c
index 8f52b73..9ed444e 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -703,6 +703,41 @@ default_get_string (struct value *value, gdb_byte **buffer, int *length,
   error (_("Getting a string is unsupported in this language."));
 }
 
+/* See language.h.  */
+
+bool
+default_symbol_name_matcher (const char *symbol_search_name,
+			     const lookup_name_info &lookup_name,
+			     completion_match *match)
+{
+  const std::string &name = lookup_name.name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
+			    mode) == 0)
+    {
+      if (match != NULL)
+	match->set_match (symbol_search_name);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* See language.h.  */
+
+symbol_name_matcher_ftype *
+language_get_symbol_name_matcher (const language_defn *lang,
+				  const lookup_name_info &lookup_name)
+{
+  if (lang->la_get_symbol_name_matcher != nullptr)
+    return lang->la_get_symbol_name_matcher (lookup_name);
+  return default_symbol_name_matcher;
+}
+
 /* Define the language that is no language.  */
 
 static int
@@ -841,7 +876,7 @@ const struct language_defn unknown_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
@@ -892,7 +927,7 @@ const struct language_defn auto_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/language.h b/gdb/language.h
index cc8ec46..54452c9 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -126,16 +126,6 @@ struct language_arch_info
   struct type *bool_type_default;
 };
 
-/* A pointer to a function expected to return nonzero if
-   SYMBOL_SEARCH_NAME matches the given LOOKUP_NAME.
-
-   SYMBOL_SEARCH_NAME should be a symbol's "search" name.
-   LOOKUP_NAME should be the name of an entity after it has been
-   transformed for lookup.  */
-
-typedef int (*symbol_name_cmp_ftype) (const char *symbol_search_name,
-				      const char *lookup_name);
-
 /* Structure tying together assorted information about a language.  */
 
 struct language_defn
@@ -331,6 +321,7 @@ struct language_defn
     void (*la_collect_symbol_completion_matches)
       (completion_tracker &tracker,
        complete_symbol_mode mode,
+       symbol_name_match_type match_type,
        const char *text,
        const char *word,
        enum type_code code);
@@ -367,13 +358,18 @@ struct language_defn
     gdb::unique_xmalloc_ptr<char> (*la_watch_location_expression)
          (struct type *type, CORE_ADDR addr);
 
-    /* Return a pointer to the function that should be used to match
-       a symbol name against LOOKUP_NAME. This is mostly for languages
-       such as Ada where the matching algorithm depends on LOOKUP_NAME.
+    /* Return a pointer to the function that should be used to match a
+       symbol name against LOOKUP_NAME, according to this language's
+       rules.  The matching algorithm depends on LOOKUP_NAME.  For
+       example, on Ada, the matching algorithm depends on the symbol
+       name (wild/full/verbatim matching), and on whether we're doing
+       a normal lookup or a completion match lookup.
 
-       This field may be NULL, in which case strcmp_iw will be used
-       to perform the matching.  */
-    symbol_name_cmp_ftype (*la_get_symbol_name_cmp) (const char *lookup_name);
+       This field may be NULL, in which case
+       default_symbol_name_matcher is used to perform the
+       matching.  */
+    symbol_name_matcher_ftype *(*la_get_symbol_name_matcher)
+      (const lookup_name_info &);
 
     /* Find all symbols in the current program space matching NAME in
        DOMAIN, according to this language's rules.
@@ -389,7 +385,8 @@ struct language_defn
        special processing here, 'iterate_over_symbols' should be
        used as the definition.  */
     void (*la_iterate_over_symbols)
-      (const struct block *block, const char *name, domain_enum domain,
+      (const struct block *block, const lookup_name_info &name,
+       domain_enum domain,
        gdb::function_view<symbol_found_callback_ftype> callback);
 
     /* Hash the given symbol search name.  Use
@@ -627,6 +624,18 @@ extern unsigned int default_search_name_hash (const char *search_name);
 void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 		   struct type **char_type, const char **charset);
 
+/* The default implementation of la_symbol_name_matcher.  Matches with
+   strncmp_iw.  */
+extern bool default_symbol_name_matcher
+  (const char *symbol_search_name,
+   const lookup_name_info &lookup_name,
+   completion_match *match);
+
+/* Get LANG's symbol_name_matcher method for LOOKUP_NAME.  Returns
+   default_symbol_name_matcher if not set.  */
+symbol_name_matcher_ftype *language_get_symbol_name_matcher
+  (const language_defn *lang, const lookup_name_info &lookup_name);
+
 /* The languages supported by GDB.  */
 
 extern const struct language_defn auto_language_defn;
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 136cb65..a241419 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -333,7 +333,8 @@ typedef struct ls_parser linespec_parser;
 /* Prototypes for local functions.  */
 
 static void iterate_over_file_blocks
-  (struct symtab *symtab, const char *name, domain_enum domain,
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain,
    gdb::function_view<symbol_found_callback_ftype> callback);
 
 static void initialize_defaults (struct symtab **default_symtab,
@@ -368,6 +369,7 @@ static int symbol_to_sal (struct symtab_and_line *result,
 			  int funfirstline, struct symbol *sym);
 
 static void add_matching_symbols_to_info (const char *name,
+					  symbol_name_match_type name_match_type,
 					  struct collect_info *info,
 					  struct program_space *pspace);
 
@@ -1110,19 +1112,15 @@ maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
 
 static void
 iterate_over_all_matching_symtabs
-  (struct linespec_state *state, const char *name, const domain_enum domain,
+  (struct linespec_state *state,
+   const lookup_name_info &lookup_name,
+   const domain_enum name_domain,
    struct program_space *search_pspace, bool include_inline,
    gdb::function_view<symbol_found_callback_ftype> callback)
 {
   struct objfile *objfile;
   struct program_space *pspace;
 
-  /* The routine to be used for comparison.  */
-  symbol_name_cmp_ftype symbol_name_cmp
-    = (state->language->la_get_symbol_name_cmp != NULL
-       ? state->language->la_get_symbol_name_cmp (name)
-       : strcmp_iw);
-
   ALL_PSPACES (pspace)
   {
     if (search_pspace != NULL && search_pspace != pspace)
@@ -1137,21 +1135,17 @@ iterate_over_all_matching_symtabs
       struct compunit_symtab *cu;
 
       if (objfile->sf)
-	objfile->sf->qf->expand_symtabs_matching
-	  (objfile,
-	   NULL,
-	   [&] (const char *symbol_name)
-	   {
-	     return symbol_name_cmp (symbol_name, name) == 0;
-	   },
-	   NULL,
-	   ALL_DOMAIN);
+	objfile->sf->qf->expand_symtabs_matching (objfile,
+						  NULL,
+						  lookup_name,
+						  NULL, NULL,
+						  ALL_DOMAIN);
 
       ALL_OBJFILE_COMPUNITS (objfile, cu)
 	{
 	  struct symtab *symtab = COMPUNIT_FILETABS (cu);
 
-	  iterate_over_file_blocks (symtab, name, domain, callback);
+	  iterate_over_file_blocks (symtab, lookup_name, name_domain, callback);
 
 	  if (include_inline)
 	    {
@@ -1164,7 +1158,7 @@ iterate_over_all_matching_symtabs
 		{
 		  block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), i);
 		  state->language->la_iterate_over_symbols
-		    (block, name, domain, [&] (symbol *sym)
+		    (block, lookup_name, name_domain, [&] (symbol *sym)
 		     {
 		       /* Restrict calls to CALLBACK to symbols
 			  representing inline symbols only.  */
@@ -1201,8 +1195,8 @@ get_current_search_block (void)
 
 static void
 iterate_over_file_blocks
-  (struct symtab *symtab, const char *name, domain_enum domain,
-   gdb::function_view<symbol_found_callback_ftype> callback)
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain, gdb::function_view<symbol_found_callback_ftype> callback)
 {
   struct block *block;
 
@@ -1212,12 +1206,12 @@ iterate_over_file_blocks
     LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback);
 }
 
-/* A helper for find_method.  This finds all methods in type T which
-   match NAME.  It adds matching symbol names to RESULT_NAMES, and
-   adds T's direct superclasses to SUPERCLASSES.  */
+/* A helper for find_method.  This finds all methods in type T of
+   language T_LANG which match NAME.  It adds matching symbol names to
+   RESULT_NAMES, and adds T's direct superclasses to SUPERCLASSES.  */
 
 static void
-find_methods (struct type *t, const char *name,
+find_methods (struct type *t, enum language t_lang, const char *name,
 	      VEC (const_char_ptr) **result_names,
 	      VEC (typep) **superclasses)
 {
@@ -1230,6 +1224,9 @@ find_methods (struct type *t, const char *name,
   if (class_name)
     {
       int method_counter;
+      lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+      symbol_name_matcher_ftype *symbol_name_compare
+	= language_get_symbol_name_matcher (language_def (t_lang), lookup_name);
 
       t = check_typedef (t);
 
@@ -1254,7 +1251,7 @@ find_methods (struct type *t, const char *name,
 		method_name = dem_opname;
 	    }
 
-	  if (strcmp_iw (method_name, name) == 0)
+	  if (symbol_name_compare (method_name, lookup_name, NULL))
 	    {
 	      int field_counter;
 
@@ -2863,15 +2860,19 @@ linespec_complete_function (completion_tracker &tracker,
 			    const char *source_filename)
 {
   complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
+  symbol_name_match_type func_match_type = symbol_name_match_type::WILD;
 
   if (source_filename != NULL)
     {
-      collect_file_symbol_completion_matches (tracker, mode,
-					      function, function,
-					      source_filename);
+      collect_file_symbol_completion_matches (tracker, mode, func_match_type,
+					      function, function, source_filename);
     }
   else
-    collect_symbol_completion_matches (tracker, mode, function, function);
+    {
+      collect_symbol_completion_matches (tracker, mode, func_match_type,
+					 function, function);
+
+    }
 }
 
 /* Helper for complete_linespec to simplify it.  SOURCE_FILENAME is
@@ -3613,14 +3614,18 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
   struct symtab *elt;
   decode_compound_collector collector;
 
+  lookup_name_info lookup_name (class_name, symbol_name_match_type::FULL);
+
   for (ix = 0; VEC_iterate (symtab_ptr, file_symtabs, ix, elt); ++ix)
     {
       if (elt == NULL)
 	{
-	  iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN,
-					     NULL, false, collector);
-	  iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN,
-					     NULL, false, collector);
+	  iterate_over_all_matching_symtabs (state, lookup_name,
+					     STRUCT_DOMAIN, NULL, false,
+					     collector);
+	  iterate_over_all_matching_symtabs (state, lookup_name,
+					     VAR_DOMAIN, NULL, false,
+					     collector);
 	}
       else
 	{
@@ -3628,8 +3633,8 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
 	     been filtered out earlier.  */
 	  gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
 	  set_current_program_space (SYMTAB_PSPACE (elt));
-	  iterate_over_file_blocks (elt, class_name, STRUCT_DOMAIN, collector);
-	  iterate_over_file_blocks (elt, class_name, VAR_DOMAIN, collector);
+	  iterate_over_file_blocks (elt, lookup_name, STRUCT_DOMAIN, collector);
+	  iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN, collector);
 	}
     }
 
@@ -3710,12 +3715,14 @@ add_all_symbol_names_from_pspace (struct collect_info *info,
   const char *iter;
 
   for (ix = 0; VEC_iterate (const_char_ptr, names, ix, iter); ++ix)
-    add_matching_symbols_to_info (iter, info, pspace);
+    add_matching_symbols_to_info (iter,
+				  symbol_name_match_type::FULL,
+				  info, pspace);
 }
 
 static void
 find_superclass_methods (VEC (typep) *superclasses,
-			 const char *name,
+			 const char *name, enum language name_lang,
 			 VEC (const_char_ptr) **result_names)
 {
   int old_len = VEC_length (const_char_ptr, *result_names);
@@ -3731,7 +3738,7 @@ find_superclass_methods (VEC (typep) *superclasses,
 
       make_cleanup (VEC_cleanup (typep), &new_supers);
       for (ix = 0; VEC_iterate (typep, iter_classes, ix, t); ++ix)
-	find_methods (t, name, result_names, &new_supers);
+	find_methods (t, name_lang, name, result_names, &new_supers);
 
       if (VEC_length (const_char_ptr, *result_names) != old_len
 	  || VEC_empty (typep, new_supers))
@@ -3798,7 +3805,8 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs,
       gdb_assert (!pspace->executing_startup);
       set_current_program_space (pspace);
       t = check_typedef (SYMBOL_TYPE (sym));
-      find_methods (t, method_name, &result_names, &superclass_vec);
+      find_methods (t, SYMBOL_LANGUAGE (sym),
+		    method_name, &result_names, &superclass_vec);
 
       /* Handle all items from a single program space at once; and be
 	 sure not to miss the last batch.  */
@@ -3811,7 +3819,7 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs,
 	     this program space, consider superclasses.  */
 	  if (VEC_length (const_char_ptr, result_names) == last_result_len)
 	    find_superclass_methods (superclass_vec, method_name,
-				     &result_names);
+				     SYMBOL_LANGUAGE (sym), &result_names);
 
 	  /* We have a list of candidate symbol names, so now we
 	     iterate over the symbol tables looking for all
@@ -3978,7 +3986,8 @@ find_function_symbols (struct linespec_state *state,
     add_all_symbol_names_from_pspace (&info, state->search_pspace,
 				      symbol_names);
   else
-    add_matching_symbols_to_info (name, &info, state->search_pspace);
+    add_matching_symbols_to_info (name, symbol_name_match_type::WILD,
+				  &info, state->search_pspace);
 
   do_cleanups (cleanup);
 
@@ -4005,28 +4014,10 @@ find_function_symbols (struct linespec_state *state,
 static void
 find_linespec_symbols (struct linespec_state *state,
 		       VEC (symtab_ptr) *file_symtabs,
-		       const char *name,
+		       const char *lookup_name,
 		       VEC (symbolp) **symbols,
 		       VEC (bound_minimal_symbol_d) **minsyms)
 {
-  demangle_result_storage demangle_storage;
-  std::string ada_lookup_storage;
-  const char *lookup_name;
-
-  if (state->language->la_language == language_ada)
-    {
-      /* In Ada, the symbol lookups are performed using the encoded
-         name rather than the demangled name.  */
-      ada_lookup_storage = ada_name_for_lookup (name);
-      lookup_name = ada_lookup_storage.c_str ();
-    }
-  else
-    {
-      lookup_name = demangle_for_lookup (name,
-					 state->language->la_language,
-					 demangle_storage);
-    }
-
   std::string canon = cp_canonicalize_string_no_typedefs (lookup_name);
   if (!canon.empty ())
     lookup_name = canon.c_str ();
@@ -4506,7 +4497,8 @@ add_minsym (struct minimal_symbol *minsym, void *d)
    restrict results to the given SYMTAB.  */
 
 static void
-search_minsyms_for_name (struct collect_info *info, const char *name,
+search_minsyms_for_name (struct collect_info *info,
+			 const lookup_name_info &name,
 			 struct program_space *search_pspace,
 			 struct symtab *symtab)
 {
@@ -4548,8 +4540,7 @@ search_minsyms_for_name (struct collect_info *info, const char *name,
 	{
 	  set_current_program_space (SYMTAB_PSPACE (symtab));
 	  local.objfile = SYMTAB_OBJFILE(symtab);
-	  iterate_over_minimal_symbols (local.objfile, name, add_minsym,
-					&local);
+	  iterate_over_minimal_symbols (local.objfile, name, add_minsym, &local);
 	}
     }
 
@@ -4591,20 +4582,24 @@ search_minsyms_for_name (struct collect_info *info, const char *name,
 
 static void
 add_matching_symbols_to_info (const char *name,
+			      symbol_name_match_type name_match_type,
 			      struct collect_info *info,
 			      struct program_space *pspace)
 {
   int ix;
   struct symtab *elt;
 
+  lookup_name_info lookup_name (name, name_match_type);
+
   for (ix = 0; VEC_iterate (symtab_ptr, info->file_symtabs, ix, elt); ++ix)
     {
       if (elt == NULL)
 	{
-	  iterate_over_all_matching_symtabs (info->state, name, VAR_DOMAIN,
+	  iterate_over_all_matching_symtabs (info->state, lookup_name,
+					     VAR_DOMAIN,
 					     pspace, true, [&] (symbol *sym)
 	    { return info->add_symbol (sym); });
-	  search_minsyms_for_name (info, name, pspace, NULL);
+	  search_minsyms_for_name (info, lookup_name, pspace, NULL);
 	}
       else if (pspace == NULL || pspace == SYMTAB_PSPACE (elt))
 	{
@@ -4614,7 +4609,8 @@ add_matching_symbols_to_info (const char *name,
 	     been filtered out earlier.  */
 	  gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
 	  set_current_program_space (SYMTAB_PSPACE (elt));
-	  iterate_over_file_blocks (elt, name, VAR_DOMAIN, [&] (symbol *sym)
+	  iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN,
+				    [&] (symbol *sym)
 	    { return info->add_symbol (sym); });
 
 	  /* If no new symbols were found in this iteration and this symtab
@@ -4623,7 +4619,7 @@ add_matching_symbols_to_info (const char *name,
 	     this case.  */
 	  if (prev_len == VEC_length (symbolp, info->result.symbols)
 	      && elt->language == language_asm)
-	    search_minsyms_for_name (info, name, pspace, elt);
+	    search_minsyms_for_name (info, lookup_name, pspace, elt);
 	}
     }
 }
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 66ac34c..fbda2be 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -394,7 +394,7 @@ extern const struct language_defn m2_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index c93eaa3..56fb5b4 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -51,6 +51,7 @@
 #include "language.h"
 #include "cli/cli-utils.h"
 #include "symbol.h"
+#include <algorithm>
 
 /* Accumulate the minimal symbols for each objfile in bunches of BUNCH_SIZE.
    At the end, copy them all into one newly allocated location on an objfile's
@@ -114,15 +115,137 @@ add_minsym_to_hash_table (struct minimal_symbol *sym,
    TABLE.  */
 static void
 add_minsym_to_demangled_hash_table (struct minimal_symbol *sym,
-                                  struct minimal_symbol **table)
+				    struct objfile *objfile)
 {
   if (sym->demangled_hash_next == NULL)
     {
-      unsigned int hash = msymbol_hash_iw (MSYMBOL_SEARCH_NAME (sym))
-	% MINIMAL_SYMBOL_HASH_SIZE;
+      unsigned int hash = search_name_hash (MSYMBOL_LANGUAGE (sym),
+					    MSYMBOL_SEARCH_NAME (sym));
+
+      auto &vec = objfile->per_bfd->demangled_hash_languages;
+      auto it = std::lower_bound (vec.begin (), vec.end (),
+				  MSYMBOL_LANGUAGE (sym));
+      if (it == vec.end () || *it != MSYMBOL_LANGUAGE (sym))
+	vec.insert (it, MSYMBOL_LANGUAGE (sym));
+
+      struct minimal_symbol **table
+	= objfile->per_bfd->msymbol_demangled_hash;
+      unsigned int hash_index = hash % MINIMAL_SYMBOL_HASH_SIZE;
+      sym->demangled_hash_next = table[hash_index];
+      table[hash_index] = sym;
+    }
+}
 
-      sym->demangled_hash_next = table[hash];
-      table[hash] = sym;
+/* Worker object for lookup_minimal_symbol.  Stores temporary results
+   while walking the symbol tables.  */
+
+struct found_minimal_symbols
+{
+  /* External symbols are best.  */
+  bound_minimal_symbol external_symbol {};
+
+  /* File-local symbols are next best.  */
+  bound_minimal_symbol file_symbol {};
+
+  /* Symbols for shared library trampolines are next best.  */
+  bound_minimal_symbol trampoline_symbol {};
+
+  /* Called when a symbol name matches.  Check if the minsym is a
+     better type than what we had already found, and record it in one
+     of the members fields if so.  Returns true if we already have the
+     real symbol.  */
+  bool maybe_collect (const char *sfile, objfile *objf,
+		      minimal_symbol *msymbol);
+};
+
+bool
+found_minimal_symbols::maybe_collect (const char *sfile,
+				      struct objfile *objfile,
+				      minimal_symbol *msymbol)
+{
+  switch (MSYMBOL_TYPE (msymbol))
+    {
+    case mst_file_text:
+    case mst_file_data:
+    case mst_file_bss:
+      if (sfile == NULL
+	  || filename_cmp (msymbol->filename, sfile) == 0)
+	{
+	  file_symbol.minsym = msymbol;
+	  file_symbol.objfile = objfile;
+	}
+      break;
+
+    case mst_solib_trampoline:
+
+      /* If a trampoline symbol is found, we prefer to keep
+	 looking for the *real* symbol.  If the actual symbol
+	 is not found, then we'll use the trampoline
+	 entry.  */
+      if (trampoline_symbol.minsym == NULL)
+	{
+	  trampoline_symbol.minsym = msymbol;
+	  trampoline_symbol.objfile = objfile;
+	}
+      break;
+
+    case mst_unknown:
+    default:
+      external_symbol.minsym = msymbol;
+      external_symbol.objfile = objfile;
+      /* We have the real symbol.  No use looking further.  */
+      return true;
+    }
+
+  /* Keep looking.  */
+  return false;
+}
+
+/* Walk the mangled name hash table, and pass each symbol whose name
+   matches LOOKUP_NAME according to NAMECMP to FOUND.  */
+
+static void
+lookup_minimal_symbol_mangled (const char *lookup_name,
+			       const char *sfile,
+			       struct objfile *objfile,
+			       struct minimal_symbol **table,
+			       unsigned int hash,
+			       int (*namecmp) (const char *, const char *),
+			       found_minimal_symbols &found)
+{
+  for (minimal_symbol *msymbol = table[hash];
+       msymbol != NULL;
+       msymbol = msymbol->hash_next)
+    {
+      const char *symbol_name = MSYMBOL_LINKAGE_NAME (msymbol);
+
+      if (namecmp (symbol_name, lookup_name) == 0
+	  && found.maybe_collect (sfile, objfile, msymbol))
+	return;
+    }
+}
+
+/* Walk the demangled name hash table, and pass each symbol whose name
+   matches LOOKUP_NAME according to MATCHER to FOUND.  */
+
+static void
+lookup_minimal_symbol_demangled (const lookup_name_info &lookup_name,
+				 const char *sfile,
+				 struct objfile *objfile,
+				 struct minimal_symbol **table,
+				 unsigned int hash,
+				 symbol_name_matcher_ftype *matcher,
+				 found_minimal_symbols &found)
+{
+  for (minimal_symbol *msymbol = table[hash];
+       msymbol != NULL;
+       msymbol = msymbol->demangled_hash_next)
+    {
+      const char *symbol_name = MSYMBOL_SEARCH_NAME (msymbol);
+
+      if (matcher (symbol_name, lookup_name, NULL)
+	  && found.maybe_collect (sfile, objfile, msymbol))
+	return;
     }
 }
 
@@ -151,32 +274,22 @@ lookup_minimal_symbol (const char *name, const char *sfile,
 		       struct objfile *objf)
 {
   struct objfile *objfile;
-  struct bound_minimal_symbol found_symbol = { NULL, NULL };
-  struct bound_minimal_symbol found_file_symbol = { NULL, NULL };
-  struct bound_minimal_symbol trampoline_symbol = { NULL, NULL };
+  found_minimal_symbols found;
 
-  unsigned int hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  unsigned int dem_hash = msymbol_hash_iw (name) % MINIMAL_SYMBOL_HASH_SIZE;
+  unsigned int mangled_hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
 
-  const char *modified_name = name;
+  auto *mangled_cmp
+    = (case_sensitivity == case_sensitive_on
+       ? strcmp
+       : strcasecmp);
 
   if (sfile != NULL)
     sfile = lbasename (sfile);
 
-  /* For C++, canonicalize the input name.  */
-  std::string modified_name_storage;
-  if (current_language->la_language == language_cplus)
-    {
-      std::string cname = cp_canonicalize_string (name);
-      if (!cname.empty ())
-	{
-	  std::swap (modified_name_storage, cname);
-	  modified_name = modified_name_storage.c_str ();
-	}
-    }
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
   for (objfile = object_files;
-       objfile != NULL && found_symbol.minsym == NULL;
+       objfile != NULL && found.external_symbol.minsym == NULL;
        objfile = objfile->next)
     {
       struct minimal_symbol *msymbol;
@@ -184,131 +297,95 @@ lookup_minimal_symbol (const char *name, const char *sfile,
       if (objf == NULL || objf == objfile
 	  || objf == objfile->separate_debug_objfile_backlink)
 	{
+	  if (symbol_lookup_debug)
+	    {
+	      fprintf_unfiltered (gdb_stdlog,
+				  "lookup_minimal_symbol (%s, %s, %s)\n",
+				  name, sfile != NULL ? sfile : "NULL",
+				  objfile_debug_name (objfile));
+	    }
+
 	  /* Do two passes: the first over the ordinary hash table,
 	     and the second over the demangled hash table.  */
-        int pass;
-
-	if (symbol_lookup_debug)
-	  {
-	    fprintf_unfiltered (gdb_stdlog,
-				"lookup_minimal_symbol (%s, %s, %s)\n",
-				name, sfile != NULL ? sfile : "NULL",
-				objfile_debug_name (objfile));
-	  }
+	  lookup_minimal_symbol_mangled (name, sfile, objfile,
+					 objfile->per_bfd->msymbol_hash,
+					 mangled_hash, mangled_cmp, found);
 
-        for (pass = 1; pass <= 2 && found_symbol.minsym == NULL; pass++)
+	  /* If not found, try the demangled hash table.  */
+	  if (found.external_symbol.minsym == NULL)
 	    {
-            /* Select hash list according to pass.  */
-            if (pass == 1)
-              msymbol = objfile->per_bfd->msymbol_hash[hash];
-            else
-              msymbol = objfile->per_bfd->msymbol_demangled_hash[dem_hash];
-
-            while (msymbol != NULL && found_symbol.minsym == NULL)
+	      /* Once for each language in the demangled hash names
+		 table (usually just zero or one languages).  */
+	      for (auto lang : objfile->per_bfd->demangled_hash_languages)
 		{
-		  int match;
-
-		  if (pass == 1)
-		    {
-		      int (*cmp) (const char *, const char *);
-
-		      cmp = (case_sensitivity == case_sensitive_on
-		             ? strcmp : strcasecmp);
-		      match = cmp (MSYMBOL_LINKAGE_NAME (msymbol),
-				   modified_name) == 0;
-		    }
-		  else
-		    {
-		      /* The function respects CASE_SENSITIVITY.  */
-		      match = MSYMBOL_MATCHES_SEARCH_NAME (msymbol,
-							  modified_name);
-		    }
-
-		  if (match)
-		    {
-                    switch (MSYMBOL_TYPE (msymbol))
-                      {
-                      case mst_file_text:
-                      case mst_file_data:
-                      case mst_file_bss:
-                        if (sfile == NULL
-			    || filename_cmp (msymbol->filename, sfile) == 0)
-			  {
-			    found_file_symbol.minsym = msymbol;
-			    found_file_symbol.objfile = objfile;
-			  }
-                        break;
-
-                      case mst_solib_trampoline:
-
-                        /* If a trampoline symbol is found, we prefer to
-                           keep looking for the *real* symbol.  If the
-                           actual symbol is not found, then we'll use the
-                           trampoline entry.  */
-                        if (trampoline_symbol.minsym == NULL)
-			  {
-			    trampoline_symbol.minsym = msymbol;
-			    trampoline_symbol.objfile = objfile;
-			  }
-                        break;
-
-                      case mst_unknown:
-                      default:
-                        found_symbol.minsym = msymbol;
-			found_symbol.objfile = objfile;
-                        break;
-                      }
-		    }
-
-                /* Find the next symbol on the hash chain.  */
-                if (pass == 1)
-                  msymbol = msymbol->hash_next;
-                else
-                  msymbol = msymbol->demangled_hash_next;
+		  unsigned int hash
+		    = (lookup_name.search_name_hash (lang)
+		       % MINIMAL_SYMBOL_HASH_SIZE);
+
+		  symbol_name_matcher_ftype *match
+		    = language_get_symbol_name_matcher (language_def (lang),
+							lookup_name);
+		  struct minimal_symbol **msymbol_demangled_hash
+		    = objfile->per_bfd->msymbol_demangled_hash;
+
+		  lookup_minimal_symbol_demangled (lookup_name, sfile, objfile,
+						   msymbol_demangled_hash,
+						   hash, match, found);
+
+		  if (found.external_symbol.minsym != NULL)
+		    break;
 		}
 	    }
 	}
     }
 
   /* External symbols are best.  */
-  if (found_symbol.minsym != NULL)
+  if (found.external_symbol.minsym != NULL)
     {
       if (symbol_lookup_debug)
 	{
+	  minimal_symbol *minsym = found.external_symbol.minsym;
+
 	  fprintf_unfiltered (gdb_stdlog,
-			      "lookup_minimal_symbol (...) = %s"
-			      " (external)\n",
-			      host_address_to_string (found_symbol.minsym));
+			      "lookup_minimal_symbol (...) = %s (external)\n",
+			      host_address_to_string (minsym));
 	}
-      return found_symbol;
+      return found.external_symbol;
     }
 
   /* File-local symbols are next best.  */
-  if (found_file_symbol.minsym != NULL)
+  if (found.file_symbol.minsym != NULL)
     {
       if (symbol_lookup_debug)
 	{
+	  minimal_symbol *minsym = found.file_symbol.minsym;
+
 	  fprintf_unfiltered (gdb_stdlog,
-			      "lookup_minimal_symbol (...) = %s"
-			      " (file-local)\n",
-			      host_address_to_string
-			        (found_file_symbol.minsym));
+			      "lookup_minimal_symbol (...) = %s (file-local)\n",
+			      host_address_to_string (minsym));
 	}
-      return found_file_symbol;
+      return found.file_symbol;
     }
 
   /* Symbols for shared library trampolines are next best.  */
-  if (symbol_lookup_debug)
+  if (found.trampoline_symbol.minsym != NULL)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "lookup_minimal_symbol (...) = %s%s\n",
-			  trampoline_symbol.minsym != NULL
-			  ? host_address_to_string (trampoline_symbol.minsym)
-			  : "NULL",
-			  trampoline_symbol.minsym != NULL
-			  ? " (trampoline)" : "");
+      if (symbol_lookup_debug)
+	{
+	  minimal_symbol *minsym = found.trampoline_symbol.minsym;
+
+	  fprintf_unfiltered (gdb_stdlog,
+			      "lookup_minimal_symbol (...) = %s (trampoline)\n",
+			      host_address_to_string (minsym));
+	}
+
+      return found.trampoline_symbol;
     }
-  return trampoline_symbol;
+
+  /* Not found.  */
+  if (symbol_lookup_debug)
+    fprintf_unfiltered (gdb_stdlog, "lookup_minimal_symbol (...) = NULL\n");
+  return {};
 }
 
 /* See minsyms.h.  */
@@ -337,34 +414,47 @@ find_minimal_symbol_address (const char *name, CORE_ADDR *addr,
 /* See minsyms.h.  */
 
 void
-iterate_over_minimal_symbols (struct objfile *objf, const char *name,
+iterate_over_minimal_symbols (struct objfile *objf,
+			      const lookup_name_info &lookup_name,
 			      void (*callback) (struct minimal_symbol *,
 						void *),
 			      void *user_data)
 {
-  unsigned int hash;
-  struct minimal_symbol *iter;
-  int (*cmp) (const char *, const char *);
 
   /* The first pass is over the ordinary hash table.  */
-  hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  iter = objf->per_bfd->msymbol_hash[hash];
-  cmp = (case_sensitivity == case_sensitive_on ? strcmp : strcasecmp);
-  while (iter)
     {
-      if (cmp (MSYMBOL_LINKAGE_NAME (iter), name) == 0)
-	(*callback) (iter, user_data);
-      iter = iter->hash_next;
+      const char *name = lookup_name.name ().c_str ();
+      unsigned int hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
+      auto *mangled_cmp
+	= (case_sensitivity == case_sensitive_on
+	   ? strcmp
+	   : strcasecmp);
+
+      for (minimal_symbol *iter = objf->per_bfd->msymbol_hash[hash];
+	   iter != NULL;
+	   iter = iter->hash_next)
+	{
+	  if (mangled_cmp (MSYMBOL_LINKAGE_NAME (iter), name) == 0)
+	    (*callback) (iter, user_data);
+	}
     }
 
-  /* The second pass is over the demangled table.  */
-  hash = msymbol_hash_iw (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  iter = objf->per_bfd->msymbol_demangled_hash[hash];
-  while (iter)
+  /* The second pass is over the demangled table.  Once for each
+     language in the demangled hash names table (usually just zero or
+     one).  */
+  for (auto lang : objf->per_bfd->demangled_hash_languages)
     {
-      if (MSYMBOL_MATCHES_SEARCH_NAME (iter, name))
-	(*callback) (iter, user_data);
-      iter = iter->demangled_hash_next;
+      const language_defn *lang_def = language_def (lang);
+      symbol_name_matcher_ftype *name_match
+	= language_get_symbol_name_matcher (lang_def, lookup_name);
+
+      unsigned int hash
+	= lookup_name.search_name_hash (lang) % MINIMAL_SYMBOL_HASH_SIZE;
+      for (minimal_symbol *iter = objf->per_bfd->msymbol_demangled_hash[hash];
+	   iter != NULL;
+	   iter = iter->demangled_hash_next)
+	if (name_match (MSYMBOL_SEARCH_NAME (iter), lookup_name, NULL))
+	  (*callback) (iter, user_data);
     }
 }
 
@@ -1170,8 +1260,7 @@ build_minimal_symbol_hash_tables (struct objfile *objfile)
 
       msym->demangled_hash_next = 0;
       if (MSYMBOL_SEARCH_NAME (msym) != MSYMBOL_LINKAGE_NAME (msym))
-	add_minsym_to_demangled_hash_table (msym,
-                                            objfile->per_bfd->msymbol_demangled_hash);
+	add_minsym_to_demangled_hash_table (msym, objfile);
     }
 }
 
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index b82a22a..c4a1d21 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -258,7 +258,7 @@ struct bound_minimal_symbol lookup_minimal_symbol_by_pc (CORE_ADDR);
    USER_DATA as arguments.  */
 
 void iterate_over_minimal_symbols (struct objfile *objf,
-				   const char *name,
+				   const lookup_name_info &name,
 				   void (*callback) (struct minimal_symbol *,
 						     void *),
 				   void *user_data);
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 4dbc7f2..22ae16e 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -403,7 +403,7 @@ extern const struct language_defn objc_language_defn = {
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 3260425..a69c408 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -27,6 +27,7 @@
 #include "progspace.h"
 #include "registry.h"
 #include "gdb_bfd.h"
+#include <vector>
 
 struct bcache;
 struct htab;
@@ -265,6 +266,12 @@ struct objfile_per_bfd_storage
      demangled names.  */
 
   minimal_symbol *msymbol_demangled_hash[MINIMAL_SYMBOL_HASH_SIZE] {};
+
+  /* All the different languages of symbols found in the demangled
+     hash table.  A flat/vector-based map is more efficient than a map
+     or hash table here, since this will only usually contain zero or
+     one entries.  */
+  std::vector<enum language> demangled_hash_languages;
 };
 
 /* Master structure for keeping track of each file from which
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index e5aff0d..a60726c 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1083,7 +1083,7 @@ extern const struct language_defn opencl_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 2dca923..e93c15b 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -454,7 +454,7 @@ extern const struct language_defn pascal_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_compare_symbol_for_completion */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 4077fb3..fb0a55d 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -45,7 +45,7 @@ static struct partial_symbol *match_partial_symbol (struct objfile *,
 						    struct partial_symtab *,
 						    int,
 						    const char *, domain_enum,
-						    symbol_compare_ftype *,
+						    symbol_name_match_type,
 						    symbol_compare_ftype *);
 
 static struct partial_symbol *lookup_partial_symbol (struct objfile *,
@@ -507,6 +507,8 @@ psym_lookup_symbol (struct objfile *objfile,
   const int psymtab_index = (block_index == GLOBAL_BLOCK ? 1 : 0);
   struct compunit_symtab *stab_best = NULL;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
   {
     if (!ps->readin && lookup_partial_symbol (objfile, ps, name,
@@ -529,10 +531,10 @@ psym_lookup_symbol (struct objfile *objfile,
 	   information (but NAME might contain it).  */
 
 	if (sym != NULL
-	    && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
+	    && SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	  return stab;
 	if (with_opaque != NULL
-	    && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
+	    && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, lookup_name))
 	  stab_best = stab;
 
 	/* Keep looking through other psymtabs.  */
@@ -542,6 +544,18 @@ psym_lookup_symbol (struct objfile *objfile,
   return stab_best;
 }
 
+/* Returns true if PSYM matches LOOKUP_NAME.  */
+
+static bool
+psymbol_name_matches (partial_symbol *psym,
+		      const lookup_name_info &lookup_name)
+{
+  const language_defn *lang = language_def (SYMBOL_LANGUAGE (psym));
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (lang, lookup_name);
+  return name_match (SYMBOL_SEARCH_NAME (psym), lookup_name, NULL);
+}
+
 /* Look in PST for a symbol in DOMAIN whose name matches NAME.  Search
    the global block of PST if GLOBAL, and otherwise the static block.
    MATCH is the comparison operation that returns true iff MATCH (s,
@@ -554,7 +568,7 @@ static struct partial_symbol *
 match_partial_symbol (struct objfile *objfile,
 		      struct partial_symtab *pst, int global,
 		      const char *name, domain_enum domain,
-		      symbol_compare_ftype *match,
+		      symbol_name_match_type match_type,
 		      symbol_compare_ftype *ordered_compare)
 {
   struct partial_symbol **start, **psym;
@@ -563,7 +577,10 @@ match_partial_symbol (struct objfile *objfile,
   int do_linear_search = 1;
 
   if (length == 0)
-      return NULL;
+    return NULL;
+
+  lookup_name_info lookup_name (name, match_type);
+
   start = (global ?
 	   objfile->global_psymbols.list + pst->globals_offset :
 	   objfile->static_psymbols.list + pst->statics_offset);
@@ -585,7 +602,12 @@ match_partial_symbol (struct objfile *objfile,
 	{
 	  center = bottom + (top - bottom) / 2;
 	  gdb_assert (center < top);
-	  if (ordered_compare (SYMBOL_SEARCH_NAME (*center), name) >= 0)
+
+	  enum language lang = SYMBOL_LANGUAGE (*center);
+	  const char *lang_ln
+	    = lookup_name.language_lookup_name (lang).c_str ();
+
+	  if (ordered_compare (SYMBOL_SEARCH_NAME (*center), lang_ln) >= 0)
 	    top = center;
 	  else
 	    bottom = center + 1;
@@ -593,7 +615,7 @@ match_partial_symbol (struct objfile *objfile,
       gdb_assert (top == bottom);
 
       while (top <= real_top
-	     && match (SYMBOL_SEARCH_NAME (*top), name) == 0)
+	     && psymbol_name_matches (*top, lookup_name))
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
 				     SYMBOL_DOMAIN (*top), domain))
@@ -611,7 +633,7 @@ match_partial_symbol (struct objfile *objfile,
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*psym),
 				     SYMBOL_DOMAIN (*psym), domain)
-	      && match (SYMBOL_SEARCH_NAME (*psym), name) == 0)
+	      && psymbol_name_matches (*psym, lookup_name))
 	    return *psym;
 	}
     }
@@ -671,6 +693,9 @@ lookup_partial_symbol (struct objfile *objfile,
 
   search_name = psymtab_search_name (name);
   cleanup = make_cleanup (xfree, search_name);
+
+  lookup_name_info lookup_name (search_name, symbol_name_match_type::FULL);
+
   start = (global ?
 	   objfile->global_psymbols.list + pst->globals_offset :
 	   objfile->static_psymbols.list + pst->statics_offset);
@@ -710,13 +735,13 @@ lookup_partial_symbol (struct objfile *objfile,
 
       /* For `case_sensitivity == case_sensitive_off' strcmp_iw_ordered will
 	 search more exactly than what matches SYMBOL_MATCHES_SEARCH_NAME.  */
-      while (top >= start && SYMBOL_MATCHES_SEARCH_NAME (*top, search_name))
+      while (top >= start && SYMBOL_MATCHES_SEARCH_NAME (*top, lookup_name))
 	top--;
 
       /* Fixup to have a symbol which matches SYMBOL_MATCHES_SEARCH_NAME.  */
       top++;
 
-      while (top <= real_top && SYMBOL_MATCHES_SEARCH_NAME (*top, search_name))
+      while (top <= real_top && SYMBOL_MATCHES_SEARCH_NAME (*top, lookup_name))
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
 				     SYMBOL_DOMAIN (*top), domain))
@@ -737,7 +762,7 @@ lookup_partial_symbol (struct objfile *objfile,
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*psym),
 				     SYMBOL_DOMAIN (*psym), domain)
-	      && SYMBOL_MATCHES_SEARCH_NAME (*psym, search_name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (*psym, lookup_name))
 	    {
 	      do_cleanups (cleanup);
 	      return *psym;
@@ -1231,13 +1256,16 @@ static int
 map_block (const char *name, domain_enum domain, struct objfile *objfile,
 	   struct block *block,
 	   int (*callback) (struct block *, struct symbol *, void *),
-	   void *data, symbol_compare_ftype *match)
+	   void *data, symbol_name_match_type match)
 {
   struct block_iterator iter;
   struct symbol *sym;
 
-  for (sym = block_iter_match_first (block, name, match, &iter);
-       sym != NULL; sym = block_iter_match_next (name, match, &iter))
+  lookup_name_info lookup_name (name, match);
+
+  for (sym = block_iter_match_first (block, lookup_name, &iter);
+       sym != NULL;
+       sym = block_iter_match_next (lookup_name, &iter))
     {
       if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				 SYMBOL_DOMAIN (sym), domain))
@@ -1260,7 +1288,7 @@ psym_map_matching_symbols (struct objfile *objfile,
 			   int (*callback) (struct block *,
 					    struct symbol *, void *),
 			   void *data,
-			   symbol_compare_ftype *match,
+			   symbol_name_match_type match,
 			   symbol_compare_ftype *ordered_compare)
 {
   const int block_kind = global ? GLOBAL_BLOCK : STATIC_BLOCK;
@@ -1295,7 +1323,8 @@ psym_map_matching_symbols (struct objfile *objfile,
 
 static bool
 recursively_search_psymtabs
-  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain kind,
+  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain domain,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> sym_matcher)
 {
   struct partial_symbol **psym;
@@ -1318,7 +1347,8 @@ recursively_search_psymtabs
 	continue;
 
       r = recursively_search_psymtabs (ps->dependencies[i],
-				       objfile, kind, sym_matcher);
+				       objfile, domain, lookup_name,
+				       sym_matcher);
       if (r != 0)
 	{
 	  ps->searched_flag = PST_SEARCHED_AND_FOUND;
@@ -1352,15 +1382,16 @@ recursively_search_psymtabs
 	{
 	  QUIT;
 
-	  if ((kind == ALL_DOMAIN
-	       || (kind == VARIABLES_DOMAIN
+	  if ((domain == ALL_DOMAIN
+	       || (domain == VARIABLES_DOMAIN
 		   && PSYMBOL_CLASS (*psym) != LOC_TYPEDEF
 		   && PSYMBOL_CLASS (*psym) != LOC_BLOCK)
-	       || (kind == FUNCTIONS_DOMAIN
+	       || (domain == FUNCTIONS_DOMAIN
 		   && PSYMBOL_CLASS (*psym) == LOC_BLOCK)
-	       || (kind == TYPES_DOMAIN
+	       || (domain == TYPES_DOMAIN
 		   && PSYMBOL_CLASS (*psym) == LOC_TYPEDEF))
-	      && sym_matcher (SYMBOL_SEARCH_NAME (*psym)))
+	      && psymbol_name_matches (*psym, lookup_name)
+	      && (sym_matcher == NULL || sym_matcher (SYMBOL_SEARCH_NAME (*psym))))
 	    {
 	      /* Found a match, so notify our caller.  */
 	      result = PST_SEARCHED_AND_FOUND;
@@ -1381,9 +1412,10 @@ static void
 psym_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
-   enum search_domain kind)
+   enum search_domain domain)
 {
   struct partial_symtab *ps;
 
@@ -1425,7 +1457,8 @@ psym_expand_symtabs_matching
 	    continue;
 	}
 
-      if (recursively_search_psymtabs (ps, objfile, kind, symbol_matcher))
+      if (recursively_search_psymtabs (ps, objfile, domain,
+				       lookup_name, symbol_matcher))
 	{
 	  struct compunit_symtab *symtab =
 	    psymtab_to_symtab (objfile, ps);
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index f0d9968..04a83de 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2188,7 +2188,7 @@ extern const struct language_defn rust_language_defn =
   default_pass_by_reference,
   c_get_string,
   rust_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
index 5ca1fa7..7533c32 100644
--- a/gdb/symfile-debug.c
+++ b/gdb/symfile-debug.c
@@ -260,7 +260,7 @@ debug_qf_map_matching_symbols (struct objfile *objfile,
 			       int (*callback) (struct block *,
 						struct symbol *, void *),
 			       void *data,
-			       symbol_compare_ftype *match,
+			       symbol_name_match_type match,
 			       symbol_compare_ftype *ordered_compare)
 {
   const struct debug_sym_fns_data *debug_data
@@ -273,7 +273,7 @@ debug_qf_map_matching_symbols (struct objfile *objfile,
 		    domain_name (domain), global,
 		    host_address_to_string (callback),
 		    host_address_to_string (data),
-		    host_address_to_string (match),
+		    plongest ((LONGEST) match),
 		    host_address_to_string (ordered_compare));
 
   debug_data->real_sf->qf->map_matching_symbols (objfile, name,
@@ -287,6 +287,7 @@ static void
 debug_qf_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -305,6 +306,7 @@ debug_qf_expand_symtabs_matching
 
   debug_data->real_sf->qf->expand_symtabs_matching (objfile,
 						    file_matcher,
+						    lookup_name,
 						    symbol_matcher,
 						    expansion_notify,
 						    kind);
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 9cbd6e5..8ebca38 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -3866,6 +3866,7 @@ symfile_free_objfile (struct objfile *objfile)
 void
 expand_symtabs_matching
   (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -3876,6 +3877,7 @@ expand_symtabs_matching
   {
     if (objfile->sf)
       objfile->sf->qf->expand_symtabs_matching (objfile, file_matcher,
+						lookup_name,
 						symbol_matcher,
 						expansion_notify, kind);
   }
diff --git a/gdb/symfile.h b/gdb/symfile.h
index bb47fdf..7c3c298 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -262,7 +262,7 @@ struct quick_symbol_functions
 				int (*callback) (struct block *,
 						 struct symbol *, void *),
 				void *data,
-				symbol_compare_ftype *match,
+				symbol_name_match_type match,
 				symbol_compare_ftype *ordered_compare);
 
   /* Expand all symbol tables in OBJFILE matching some criteria.
@@ -286,6 +286,7 @@ struct quick_symbol_functions
   void (*expand_symtabs_matching)
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info &lookup_name,
      gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
      gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
      enum search_domain kind);
@@ -560,6 +561,7 @@ extern scoped_restore_tmpl<int> increment_reading_symtab (void);
 
 void expand_symtabs_matching
   (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind);
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index 32a5331..ad829bf 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -979,6 +979,7 @@ maintenance_expand_symtabs (char *args, int from_tty)
 	       return (!basenames
 		       && (regexp == NULL || re_exec (filename)));
 	     },
+	     lookup_name_info::match_any (),
 	     [] (const char *symname)
 	     {
 	       /* Since we're not searching on symbols, just return true.  */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index ba2c559..6914f69 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -947,6 +947,18 @@ symbol_search_name (const struct general_symbol_info *gsymbol)
     return symbol_natural_name (gsymbol);
 }
 
+/* See symtab.h.  */
+
+bool
+symbol_matches_search_name (const struct general_symbol_info *gsymbol,
+			    const lookup_name_info &name)
+{
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (language_def (gsymbol->language),
+					name);
+  return name_match (symbol_search_name (gsymbol), name, NULL);
+}
+
 /* Initialize the structure fields to zero values.  */
 
 void
@@ -1111,11 +1123,12 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
     }
   else if (slot_name != NULL && name != NULL)
     {
-      /* It's important that we use the same comparison that was done the
-	 first time through.  If the slot records a found symbol, then this
-	 means using strcmp_iw on SYMBOL_SEARCH_NAME.  See dictionary.c.
-	 It also means using symbol_matches_domain for found symbols.
-	 See block.c.
+      /* It's important that we use the same comparison that was done
+	 the first time through.  If the slot records a found symbol,
+	 then this means using the symbol name comparison function of
+	 the symbol's language with SYMBOL_SEARCH_NAME.  See
+	 dictionary.c.  It also means using symbol_matches_domain for
+	 found symbols.  See block.c.
 
 	 If the slot records a not-found symbol, then require a precise match.
 	 We could still be lax with whitespace like strcmp_iw though.  */
@@ -1130,9 +1143,11 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
       else
 	{
 	  struct symbol *sym = slot->value.found.symbol;
+	  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
-	  if (strcmp_iw (slot_name, name) != 0)
+	  if (!SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	    return 0;
+
 	  if (!symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				      slot_domain, domain))
 	    return 0;
@@ -1751,6 +1766,30 @@ fixup_symbol_section (struct symbol *sym, struct objfile *objfile)
   return sym;
 }
 
+/* See symtab.h.  */
+
+demangle_for_lookup_info::demangle_for_lookup_info (const lookup_name_info &lookup_name,
+						    language lang)
+{
+  demangle_result_storage storage;
+
+  m_demangled_name = demangle_for_lookup (lookup_name.name ().c_str (),
+					  lang, storage);
+}
+
+/* See symtab.h.  */
+
+const lookup_name_info &
+lookup_name_info::match_any ()
+{
+  /* Lookup any symbol that "" would complete.  I.e., this matches all
+     symbol names.  */
+  static const lookup_name_info lookup_name ({}, symbol_name_match_type::FULL,
+					     true);
+
+  return lookup_name;
+}
+
 /* Compute the demangled form of NAME as used by the various symbol
    lookup functions.  The result can either be the input NAME
    directly, or a pointer to a buffer owned by the STORAGE object.
@@ -2775,7 +2814,8 @@ basic_lookup_transparent_type (const char *name)
    search continues.  */
 
 void
-iterate_over_symbols (const struct block *block, const char *name,
+iterate_over_symbols (const struct block *block,
+		      const lookup_name_info &name,
 		      const domain_enum domain,
 		      gdb::function_view<symbol_found_callback_ftype> callback)
 {
@@ -4305,6 +4345,7 @@ search_symbols (const char *regexp, enum search_domain kind,
 			     return file_matches (filename, files, nfiles,
 						  basenames);
 			   },
+			   lookup_name_info::match_any (),
 			   [&] (const char *symname)
 			   {
 			     return (!preg || preg->exec (symname,
@@ -4731,13 +4772,33 @@ rbreak_command (char *regexp, int from_tty)
    information.  */
 
 static int
-compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
-{
-  int (*ncmp) (const char *, const char *, size_t);
+compare_symbol_name (const char *name,
+		     language symbol_language,
+		     const lookup_name_info &lookup_name,
+		     const char *sym_text, int sym_text_len,
+		     completion_match_result &match_res)
+{
+  const language_defn *lang;
+
+  /* If we're completing for an expression and the symbol doesn't have
+     an explicit language set, fallback to the current language.  Ada
+     minimal symbols won't have their language set to Ada, for
+     example, and if we compared using the default/C-like matcher,
+     then when completing e.g., symbols in a package named "pck", we'd
+     match internal Ada symbols like "pckS", which are invalid in an
+     Ada expression, unless you wrap them in '<' '>' to request a
+     verbatim match.  */
+  if (symbol_language == language_auto
+      && lookup_name.match_type () == symbol_name_match_type::EXPRESSION)
+    lang = current_language;
+  else
+    lang = language_def (symbol_language);
 
-  ncmp = (case_sensitivity == case_sensitive_on ? strncmp : strncasecmp);
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (lang, lookup_name);
 
-  if (ncmp (name, sym_text, sym_text_len) != 0)
+  /* Clip symbols that cannot match.  */
+  if (!name_match (name, lookup_name, &match_res.match))
     return 0;
 
   if (sym_text[sym_text_len] == '(')
@@ -4755,20 +4816,35 @@ compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
   return 1;
 }
 
-/*  Test to see if the symbol specified by SYMNAME (which is already
-   demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
-   characters.  If so, add it to the current completion list.  */
+/*  Test to see if the symbol of language SYMBOL_LANGUAGE specified by
+    SYMNAME (which is already demangled for C++ symbols) matches
+    SYM_TEXT in the first SYM_TEXT_LEN characters.  If so, add it to
+    the current completion list.  */
 
-static void
+void
 completion_list_add_name (completion_tracker &tracker,
+			  language symbol_language,
 			  const char *symname,
+			  const lookup_name_info &lookup_name,
 			  const char *sym_text, int sym_text_len,
 			  const char *text, const char *word)
 {
+  completion_match_result &match_res
+    = tracker.reset_completion_match_result ();
+
   /* Clip symbols that cannot match.  */
-  if (!compare_symbol_name (symname, sym_text, sym_text_len))
+  if (!compare_symbol_name (symname, symbol_language,
+			    lookup_name,
+			    sym_text, sym_text_len,
+			    match_res))
     return;
 
+  /* Refresh SYMNAME from the match string.  It's potentially
+     different depending on language.  (E.g., on Ada, the match may be
+     the encoded symbol name wrapped in "<>").  */
+  symname = match_res.match.match ();
+  gdb_assert (symname != NULL);
+
   /* We have a match for a completion, so add SYMNAME to the current list
      of matches.  Note that the name is moved to freshly malloc'd space.  */
 
@@ -4806,11 +4882,13 @@ completion_list_add_name (completion_tracker &tracker,
 static void
 completion_list_add_symbol (completion_tracker &tracker,
 			    symbol *sym,
+			    const lookup_name_info &lookup_name,
 			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
-  completion_list_add_name (tracker, SYMBOL_NATURAL_NAME (sym),
-			    sym_text, sym_text_len, text, word);
+  completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+			    SYMBOL_NATURAL_NAME (sym),
+			    lookup_name, sym_text, sym_text_len, text, word);
 }
 
 /* completion_list_add_name wrapper for struct minimal_symbol.  */
@@ -4818,19 +4896,23 @@ completion_list_add_symbol (completion_tracker &tracker,
 static void
 completion_list_add_msymbol (completion_tracker &tracker,
 			     minimal_symbol *sym,
+			     const lookup_name_info &lookup_name,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
-  completion_list_add_name (tracker, MSYMBOL_NATURAL_NAME (sym),
-			    sym_text, sym_text_len, text, word);
+  completion_list_add_name (tracker, MSYMBOL_LANGUAGE (sym),
+			    MSYMBOL_NATURAL_NAME (sym),
+			    lookup_name, sym_text, sym_text_len, text, word);
 }
 
+
 /* ObjC: In case we are completing on a selector, look as the msymbol
    again and feed all the selectors into the mill.  */
 
 static void
 completion_list_objc_symbol (completion_tracker &tracker,
 			     struct minimal_symbol *msymbol,
+			     const lookup_name_info &lookup_name,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
@@ -4848,7 +4930,9 @@ completion_list_objc_symbol (completion_tracker &tracker,
 
   if (sym_text[0] == '[')
     /* Complete on shortened method method.  */
-    completion_list_add_name (tracker, method + 1,
+    completion_list_add_name (tracker, language_objc,
+			      method + 1,
+			      lookup_name,
 			      sym_text, sym_text_len, text, word);
 
   while ((strlen (method) + 1) >= tmplen)
@@ -4870,10 +4954,12 @@ completion_list_objc_symbol (completion_tracker &tracker,
       memcpy (tmp, method, (category - method));
       tmp[category - method] = ' ';
       memcpy (tmp + (category - method) + 1, selector, strlen (selector) + 1);
-      completion_list_add_name (tracker, tmp,
+      completion_list_add_name (tracker, language_objc, tmp,
+				lookup_name,
 				sym_text, sym_text_len, text, word);
       if (sym_text[0] == '[')
-	completion_list_add_name (tracker, tmp + 1,
+	completion_list_add_name (tracker, language_objc, tmp + 1,
+				  lookup_name,
 				  sym_text, sym_text_len, text, word);
     }
 
@@ -4885,7 +4971,8 @@ completion_list_objc_symbol (completion_tracker &tracker,
       if (tmp2 != NULL)
 	*tmp2 = '\0';
 
-      completion_list_add_name (tracker, tmp,
+      completion_list_add_name (tracker, language_objc, tmp,
+				lookup_name,
 				sym_text, sym_text_len, text, word);
     }
 }
@@ -4939,6 +5026,7 @@ language_search_unquoted_string (const char *text, const char *p)
 static void
 completion_list_add_fields (completion_tracker &tracker,
 			    struct symbol *sym,
+			    const lookup_name_info &lookup_name,
 			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
@@ -4951,7 +5039,9 @@ completion_list_add_fields (completion_tracker &tracker,
       if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
 	for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
 	  if (TYPE_FIELD_NAME (t, j))
-	    completion_list_add_name (tracker, TYPE_FIELD_NAME (t, j),
+	    completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+				      TYPE_FIELD_NAME (t, j),
+				      lookup_name,
 				      sym_text, sym_text_len, text, word);
     }
 }
@@ -4961,6 +5051,7 @@ completion_list_add_fields (completion_tracker &tracker,
 static void
 add_symtab_completions (struct compunit_symtab *cust,
 			completion_tracker &tracker,
+			const lookup_name_info &lookup_name,
 			const char *sym_text, int sym_text_len,
 			const char *text, const char *word,
 			enum type_code code)
@@ -4983,6 +5074,7 @@ add_symtab_completions (struct compunit_symtab *cust,
 	      || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
 	    completion_list_add_symbol (tracker, sym,
+					lookup_name,
 					sym_text, sym_text_len,
 					text, word);
 	}
@@ -4991,8 +5083,8 @@ add_symtab_completions (struct compunit_symtab *cust,
 
 void
 default_collect_symbol_completion_matches_break_on
-  (completion_tracker &tracker,
-   complete_symbol_mode mode,
+  (completion_tracker &tracker, complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
    const char *text, const char *word,
    const char *break_on, enum type_code code)
 {
@@ -5083,6 +5175,9 @@ default_collect_symbol_completion_matches_break_on
     }
   gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
 
+  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
+				name_match_type, true);
+
   /* At this point scan through the misc symbol vectors and add each
      symbol you find to the list.  Eventually we want to ignore
      anything that isn't a text symbol (everything else will be
@@ -5094,34 +5189,30 @@ default_collect_symbol_completion_matches_break_on
 	{
 	  QUIT;
 
-	  completion_list_add_msymbol (tracker,
-				       msymbol, sym_text, sym_text_len,
+	  completion_list_add_msymbol (tracker, msymbol, lookup_name,
+				       sym_text, sym_text_len,
 				       text, word);
 
-	  completion_list_objc_symbol (tracker,
-				       msymbol, sym_text, sym_text_len,
-				       text, word);
+	  completion_list_objc_symbol (tracker, msymbol, lookup_name,
+				       sym_text, sym_text_len, text,
+				       word);
 	}
     }
 
   /* Add completions for all currently loaded symbol tables.  */
   ALL_COMPUNITS (objfile, cust)
-    add_symtab_completions (cust, tracker,
+    add_symtab_completions (cust, tracker, lookup_name,
 			    sym_text, sym_text_len, text, word, code);
 
   /* Look through the partial symtabs for all symbols which begin by
      matching SYM_TEXT.  Expand all CUs that you find to the list.  */
   expand_symtabs_matching (NULL,
-			   [&] (const char *name) /* symbol matcher */
-			     {
-			       return compare_symbol_name (name,
-							   sym_text,
-							   sym_text_len);
-			     },
+			   lookup_name,
+			   NULL,
 			   [&] (compunit_symtab *symtab) /* expansion notify */
 			     {
 			       add_symtab_completions (symtab,
-						       tracker,
+						       tracker, lookup_name,
 						       sym_text, sym_text_len,
 						       text, word, code);
 			     },
@@ -5144,16 +5235,16 @@ default_collect_symbol_completion_matches_break_on
 	  {
 	    if (code == TYPE_CODE_UNDEF)
 	      {
-		completion_list_add_symbol (tracker, sym,
+		completion_list_add_symbol (tracker, sym, lookup_name,
 					    sym_text, sym_text_len, text,
 					    word);
-		completion_list_add_fields (tracker, sym,
+		completion_list_add_fields (tracker, sym, lookup_name,
 					    sym_text, sym_text_len, text,
 					    word);
 	      }
 	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
-	      completion_list_add_symbol (tracker, sym,
+	      completion_list_add_symbol (tracker, sym, lookup_name,
 					  sym_text, sym_text_len, text,
 					  word);
 	  }
@@ -5172,12 +5263,12 @@ default_collect_symbol_completion_matches_break_on
     {
       if (surrounding_static_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
-	  completion_list_add_fields (tracker, sym,
+	  completion_list_add_fields (tracker, sym, lookup_name,
 				      sym_text, sym_text_len, text, word);
 
       if (surrounding_global_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
-	  completion_list_add_fields (tracker, sym,
+	  completion_list_add_fields (tracker, sym, lookup_name,
 				      sym_text, sym_text_len, text, word);
     }
 
@@ -5194,7 +5285,10 @@ default_collect_symbol_completion_matches_break_on
 				 macro_source_file *,
 				 int)
 	{
-	  completion_list_add_name (tracker, macro_name,
+	  completion_list_add_name (tracker,
+				    language_c,
+				    macro_name,
+				    lookup_name,
 				    sym_text, sym_text_len,
 				    text, word);
 	};
@@ -5222,10 +5316,12 @@ default_collect_symbol_completion_matches_break_on
 void
 default_collect_symbol_completion_matches (completion_tracker &tracker,
 					   complete_symbol_mode mode,
+					   symbol_name_match_type name_match_type,
 					   const char *text, const char *word,
 					   enum type_code code)
 {
   return default_collect_symbol_completion_matches_break_on (tracker, mode,
+							     name_match_type,
 							     text, word, "",
 							     code);
 }
@@ -5236,9 +5332,11 @@ default_collect_symbol_completion_matches (completion_tracker &tracker,
 void
 collect_symbol_completion_matches (completion_tracker &tracker,
 				   complete_symbol_mode mode,
+				   symbol_name_match_type name_match_type,
 				   const char *text, const char *word)
 {
   current_language->la_collect_symbol_completion_matches (tracker, mode,
+							  name_match_type,
 							  text, word,
 							  TYPE_CODE_UNDEF);
 }
@@ -5252,11 +5350,13 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 					enum type_code code)
 {
   complete_symbol_mode mode = complete_symbol_mode::EXPRESSION;
+  symbol_name_match_type name_match_type = symbol_name_match_type::EXPRESSION;
 
   gdb_assert (code == TYPE_CODE_UNION
 	      || code == TYPE_CODE_STRUCT
 	      || code == TYPE_CODE_ENUM);
   current_language->la_collect_symbol_completion_matches (tracker, mode,
+							  name_match_type,
 							  text, word, code);
 }
 
@@ -5266,6 +5366,7 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 void
 collect_file_symbol_completion_matches (completion_tracker &tracker,
 					complete_symbol_mode mode,
+					symbol_name_match_type name_match_type,
 					const char *text, const char *word,
 					const char *srcfile)
 {
@@ -5322,12 +5423,15 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
 
   sym_text_len = strlen (sym_text);
 
+  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
+				name_match_type, true);
+
   /* Go through symtabs for SRCFILE and check the externs and statics
      for symbols which match.  */
   iterate_over_symtabs (srcfile, [&] (symtab *s)
     {
       add_symtab_completions (SYMTAB_COMPUNIT (s),
-			      tracker,
+			      tracker, lookup_name,
 			      sym_text, sym_text_len,
 			      text, word, TYPE_CODE_UNDEF);
       return false;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index da5cf25..fe7c1c4 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -21,10 +21,12 @@
 #define SYMTAB_H 1
 
 #include <vector>
+#include <string>
 #include "gdb_vecs.h"
 #include "gdbtypes.h"
 #include "common/enum-flags.h"
 #include "common/function-view.h"
+#include "common/gdb_optional.h"
 #include "completer.h"
 
 /* Opaque declarations.  */
@@ -43,6 +45,249 @@ struct probe;
 struct common_block;
 struct obj_section;
 struct cmd_list_element;
+struct lookup_name_info;
+
+/* How to match a lookup name against a symbol search name.  */
+enum class symbol_name_match_type
+{
+  /* Wild matching.  Matches unqualified symbol names in all
+     namespace/module/packages, etc.  */
+  WILD,
+
+  /* Full matching.  The lookup name indicates a fully-qualified name,
+     and only matches symbol search names in the specified
+     namespace/module/package.  */
+  FULL,
+
+  /* Expression matching.  The same as FULL matching in most
+     languages.  The same as WILD matching in Ada.  */
+  EXPRESSION,
+};
+
+extern unsigned int search_name_hash (enum language language,
+				      const char *search_name);
+
+/* Ada-specific bits of a lookup_name_info object.  This is lazily
+   constructed on demand.  */
+
+class ada_lookup_name_info final
+{
+ public:
+  /* Construct.  */
+  explicit ada_lookup_name_info (const lookup_name_info &lookup_name);
+
+  /* Compare SYMBOL_SEARCH_NAME with our lookup name, using MATCH_TYPE
+     as name match type.  Returns true if there's a match, false
+     otherwise.  If non-NULL, store the matching results in MATCH.  */
+  bool matches (const char *symbol_search_name,
+		symbol_name_match_type match_type,
+		completion_match *match) const;
+
+  /* The Ada-encoded lookup name.  */
+  const std::string &lookup_name () const
+  { return m_encoded_name; }
+
+  /* Return true if we're supposed to be doing a wild match look
+     up.  */
+  bool wild_match_p () const
+  { return m_wild_match_p; }
+
+  /* Return true if we're looking up a name inside package
+     Standard.  */
+  bool standard_p () const
+  { return m_standard_p; }
+
+ private:
+  /* The Ada-encoded lookup name.  */
+  std::string m_encoded_name;
+
+  /* Whether the user-provided lookup name was Ada encoded.  If so,
+     then return encoded names in the 'matches' method's 'completion
+     match result' output.  */
+  bool m_encoded_p : 1;
+
+  /* True if really doing wild matching.  Even if the user requests
+     wild matching, some cases require full matching.  */
+  bool m_wild_match_p : 1;
+
+  /* True if doing a verbatim match.  This is true if the decoded
+     version of the symbol name is wrapped in '<'/'>'.  This is an
+     escape hatch users can use to look up symbols the Ada encoding
+     does not understand.  */
+  bool m_verbatim_p : 1;
+
+   /* True if the user specified a symbol name that is inside package
+      Standard.  Symbol names inside package Standard are handled
+      specially.  We always do a non-wild match of the symbol name
+      without the "standard__" prefix, and only search static and
+      global symbols.  This was primarily introduced in order to allow
+      the user to specifically access the standard exceptions using,
+      for instance, Standard.Constraint_Error when Constraint_Error is
+      ambiguous (due to the user defining its own Constraint_Error
+      entity inside its program).  */
+  bool m_standard_p : 1;
+};
+
+/* Language-specific bits of a lookup_name_info object, for languages
+   that do name searching using demangled names (C++/D/Go).  This is
+   lazily constructed on demand.  */
+
+struct demangle_for_lookup_info final
+{
+public:
+  demangle_for_lookup_info (const lookup_name_info &lookup_name,
+			    language lang);
+
+  /* The demangled lookup name.  */
+  const std::string &lookup_name () const
+  { return m_demangled_name; }
+
+private:
+  /* The demangled lookup name.  */
+  std::string m_demangled_name;
+};
+
+/* Object that aggregates all information relative to a symbol lookup
+   name.  I.e., the name that is matched against the symbol's search
+   name.  Caches per-language information so that it doesn't require
+   recomputing it for every symbol comparison, like for example the
+   Ada encoded name and the symbol's name hash for a given language.
+   The object is conceptually immutable once constructed, and thus has
+   no setters.  This is to avoid the case of a code path tweaking some
+   property of the lookup name for some local reason, and accidentally
+   causing other parts of symbol search that continue searching using
+   the same lookup name be affected.  lookup_name_info objects are
+   generally passed around as a const reference to reinforce that.
+   (They're not passed around by value because they're not small.)  */
+class lookup_name_info final
+{
+ public:
+  /* Create a new object.  */
+  lookup_name_info (std::string name,
+		    symbol_name_match_type match_type,
+		    bool completion_mode = false)
+    : m_match_type (match_type),
+      m_completion_mode (completion_mode),
+      m_name (std::move (name))
+  {}
+
+  /* Getters.  See description of each corresponding field.  */
+  symbol_name_match_type match_type () const { return m_match_type; }
+  bool completion_mode () const { return m_completion_mode; }
+  const std::string &name () const { return m_name; }
+
+  /* Get the search name hash for searches in language LANG.  */
+  unsigned int search_name_hash (language lang) const
+  {
+    /* Only compute each language's hash once.  */
+    if (!m_demangled_hashes_p[lang])
+      {
+	m_demangled_hashes[lang]
+	  = ::search_name_hash (lang, language_lookup_name (lang).c_str ());
+	m_demangled_hashes_p[lang] = true;
+      }
+    return m_demangled_hashes[lang];
+  }
+
+  /* Get the search name for searches in language LANG.  */
+  const std::string &language_lookup_name (language lang) const
+  {
+    switch (lang)
+      {
+      case language_ada:
+	return ada ().lookup_name ();
+      case language_cplus:
+	return cplus ().lookup_name ();
+      case language_d:
+	return d ().lookup_name ();
+      case language_go:
+	return go ().lookup_name ();
+      default:
+	return m_name;
+      }
+  }
+
+  /* Get the Ada-specific lookup info.  */
+  const ada_lookup_name_info &ada () const
+  {
+    maybe_init (m_ada);
+    return *m_ada;
+  }
+
+  /* Get the C++-specific lookup info.  */
+  const demangle_for_lookup_info &cplus () const
+  {
+    maybe_init (m_cplus, language_cplus);
+    return *m_cplus;
+  }
+
+  /* Get the D-specific lookup info.  */
+  const demangle_for_lookup_info &d () const
+  {
+    maybe_init (m_d, language_d);
+    return *m_d;
+  }
+
+  /* Get the Go-specific lookup info.  */
+  const demangle_for_lookup_info &go () const
+  {
+    maybe_init (m_go, language_go);
+    return *m_go;
+  }
+
+  /* Get a reference to a lookup_name_info object that matches any
+     symbol name.  */
+  static const lookup_name_info &match_any ();
+
+private:
+  /* Initialize FIELD, if not initialized yet.  */
+  template<typename Field, typename... Args>
+  void maybe_init (Field &field, Args&&... args) const
+  {
+    if (!field)
+      field.emplace (*this, std::forward<Args> (args)...);
+  }
+
+  /* The lookup info as passed to the ctor.  */
+  symbol_name_match_type m_match_type;
+  bool m_completion_mode;
+  std::string m_name;
+
+  /* Language-specific info.  These fields are filled lazily the first
+     time a lookup is done in the corresponding language.  They're
+     mutable because lookup_name_info objects are typically passed
+     around by const reference (see intro), and they're conceptually
+     "cache" that can always be reconstructed from the non-mutable
+     fields.  */
+  mutable gdb::optional<ada_lookup_name_info> m_ada;
+  mutable gdb::optional<demangle_for_lookup_info> m_cplus;
+  mutable gdb::optional<demangle_for_lookup_info> m_d;
+  mutable gdb::optional<demangle_for_lookup_info> m_go;
+
+  /* The demangled hashes.  Stored in an array with one entry for each
+     possible language.  The second array records whether we've
+     already computed the each language's hash.  (These are separate
+     arrays instead of a single array of optional<unsigned> to avoid
+     alignment padding).  */
+  mutable std::array<unsigned int, nr_languages> m_demangled_hashes;
+  mutable std::array<bool, nr_languages> m_demangled_hashes_p {};
+};
+
+/* Comparison function for completion symbol lookup.
+
+   Returns true if the symbol name matches against LOOKUP_NAME.
+
+   SYMBOL_SEARCH_NAME should be a symbol's "search" name.
+
+   On success and if non-NULL, MATCH is set to point to the symbol
+   name as should be presented to the user as a completion match list
+   element.  In most languages, this is the same as the symbol's
+   search name, but in some, like Ada, the display name is dynamically
+   computed within the comparison routine.  */
+typedef bool (symbol_name_matcher_ftype)
+  (const char *symbol_search_name,
+   const lookup_name_info &lookup_name,
+   completion_match *match);
 
 /* Some of the structures in this file are space critical.
    The space-critical structures are:
@@ -269,13 +514,18 @@ extern int demangle;
    returns the same value (same pointer) as SYMBOL_LINKAGE_NAME.  */
 #define SYMBOL_SEARCH_NAME(symbol)					 \
    (symbol_search_name (&(symbol)->ginfo))
-extern const char *symbol_search_name (const struct general_symbol_info *);
+extern const char *symbol_search_name (const struct general_symbol_info *ginfo);
 
-/* Return non-zero if NAME matches the "search" name of SYMBOL.
-   Whitespace and trailing parentheses are ignored.
-   See strcmp_iw for details about its behavior.  */
-#define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
-  (strcmp_iw (SYMBOL_SEARCH_NAME (symbol), (name)) == 0)
+/* Return true if NAME matches the "search" name of SYMBOL, according
+   to the symbol's language.  */
+#define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)                       \
+  symbol_matches_search_name (&(symbol)->ginfo, (name))
+
+/* Helper for SYMBOL_MATCHES_SEARCH_NAME that works with both symbols
+   and psymbols.  */
+extern bool symbol_matches_search_name
+  (const struct general_symbol_info *gsymbol,
+   const lookup_name_info &name);
 
 /* Compute the hash of the given symbol search name of a symbol of
    language LANGUAGE.  */
@@ -427,8 +677,6 @@ struct minimal_symbol
   (symbol_set_language (&(symbol)->mginfo, (language), (obstack)))
 #define MSYMBOL_SEARCH_NAME(symbol)					 \
    (symbol_search_name (&(symbol)->mginfo))
-#define MSYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
-  (strcmp_iw (MSYMBOL_SEARCH_NAME (symbol), (name)) == 0)
 #define MSYMBOL_SET_NAMES(symbol,linkage_name,len,copy_name,objfile)	\
   symbol_set_names (&(symbol)->mginfo, linkage_name, len, copy_name, objfile)
 
@@ -1518,26 +1766,30 @@ enum class complete_symbol_mode
 extern void default_collect_symbol_completion_matches_break_on
   (completion_tracker &tracker,
    complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
    const char *text, const char *word, const char *break_on,
    enum type_code code);
 extern void default_collect_symbol_completion_matches
   (completion_tracker &tracker,
    complete_symbol_mode,
+   symbol_name_match_type name_match_type,
    const char *,
    const char *,
    enum type_code);
-extern void collect_symbol_completion_matches (completion_tracker &tracker,
-					       complete_symbol_mode,
-					       const char *, const char *);
+extern void collect_symbol_completion_matches
+  (completion_tracker &tracker,
+   complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
+   const char *, const char *);
 extern void collect_symbol_completion_matches_type (completion_tracker &tracker,
 						    const char *, const char *,
 						    enum type_code);
 
-extern void collect_file_symbol_completion_matches (completion_tracker &tracker,
-						    complete_symbol_mode,
-						    const char *,
-						    const char *,
-						    const char *);
+extern void collect_file_symbol_completion_matches
+  (completion_tracker &tracker,
+   complete_symbol_mode,
+   symbol_name_match_type name_match_type,
+   const char *, const char *, const char *);
 
 extern completion_list
   make_source_files_completion_list (const char *, const char *);
@@ -1652,7 +1904,8 @@ std::vector<CORE_ADDR> find_pcs_for_symtab_line
 
 typedef bool (symbol_found_callback_ftype) (symbol *sym);
 
-void iterate_over_symbols (const struct block *block, const char *name,
+void iterate_over_symbols (const struct block *block,
+			   const lookup_name_info &name,
 			   const domain_enum domain,
 			   gdb::function_view<symbol_found_callback_ftype> callback);
 
@@ -1700,4 +1953,11 @@ void initialize_objfile_symbol (struct symbol *);
 
 struct template_symbol *allocate_template_symbol (struct objfile *);
 
+void completion_list_add_name (completion_tracker &tracker,
+			       language symbol_language,
+			       const char *symname,
+			       const lookup_name_info &lookup_name,
+			       const char *sym_text, int sym_text_len,
+			       const char *text, const char *word);
+
 #endif /* !defined(SYMTAB_H) */
diff --git a/gdb/testsuite/gdb.ada/complete.exp b/gdb/testsuite/gdb.ada/complete.exp
index 906c85a..c3631c7 100644
--- a/gdb/testsuite/gdb.ada/complete.exp
+++ b/gdb/testsuite/gdb.ada/complete.exp
@@ -83,6 +83,16 @@ test_gdb_no_completion "exported"
 test_gdb_complete "<Exported" \
                   "p <Exported_Capitalized>"
 
+# While at it, make sure we can print the symbol too, using the '<'
+# notation.
+gdb_test "p <Exported_Capitalized>" " = 2"
+
+# Confirm that we can't print the symbol without the '<' notation.
+gdb_test "p Exported_Capitalized" \
+    "No definition of \"exported_capitalized\" in current context."
+gdb_test "p exported_capitalized" \
+    "No definition of \"exported_capitalized\" in current context."
+
 # A global symbol, created by the binder, that starts with __gnat...
 test_gdb_complete "__gnat_ada_main_progra" \
                   "p __gnat_ada_main_program_name"
diff --git a/gdb/utils.c b/gdb/utils.c
index 43e1827..bde1eef 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2363,21 +2363,9 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
     }
 }
 
-/* Modes of operation for strncmp_iw_with_mode.  */
-
-enum class strncmp_iw_mode
-{
-  /* Work like strncmp, while ignoring whitespace.  */
-  NORMAL,
-
-  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
-     string1=="FOO(PARAMS)" matches string2=="FOO".  */
-  MATCH_PARAMS,
-};
-
-/* Helper for strncmp_iw and strcmp_iw.  */
+/* See utils.h.  */
 
-static int
+int
 strncmp_iw_with_mode (const char *string1, const char *string2,
 		      size_t string2_len, strncmp_iw_mode mode)
 {
diff --git a/gdb/utils.h b/gdb/utils.h
index 48330a1..c3d707c 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -31,6 +31,33 @@ extern void initialize_utils (void);
 
 extern int sevenbit_strings;
 
+/* Modes of operation for strncmp_iw_with_mode.  */
+
+enum class strncmp_iw_mode
+{
+/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
+   differences in whitespace.  Returns 0 if they match, non-zero if they
+   don't (slightly different than strcmp()'s range of return values).
+
+   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
+   This "feature" is useful when searching for matching C++ function names
+   (such as if the user types 'break FOO', where FOO is a mangled C++
+   function).  */
+  NORMAL,
+
+  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
+     string1=="FOO(PARAMS)" matches string2=="FOO".  */
+  MATCH_PARAMS,
+};
+
+/* Helper for strcmp_iw and strncmp_iw.  Exported so that languages
+   can implement both NORMAL and MATCH_PARAMS variants in a single
+   function and defer part of the work to strncmp_iw_with_mode.  */
+extern int strncmp_iw_with_mode (const char *string1,
+				 const char *string2,
+				 size_t string2_len,
+				 strncmp_iw_mode mode);
+
 /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
    differences in whitespace.  STRING2_LEN is STRING2's length.
    Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
-- 
2.5.5


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

* Re: Get rid of "set language local"? (was: Re: [PATCH 23/40] Make language_def O(1))
  2017-07-20 18:12       ` Get rid of "set language local"? (was: Re: [PATCH 23/40] Make language_def O(1)) Pedro Alves
@ 2017-07-20 23:44         ` Matt Rice
  0 siblings, 0 replies; 183+ messages in thread
From: Matt Rice @ 2017-07-20 23:44 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Keith Seitz, gdb-patches

On Thu, Jul 20, 2017 at 11:11 AM, Pedro Alves <palves@redhat.com> wrote:
> On 07/20/2017 06:40 PM, Pedro Alves wrote:
>> On 07/18/2017 12:03 AM, Keith Seitz wrote:
>>> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>>
>>>> Note that "local_language_defn" is gone along the way.  AFAICT, it's
>>>> just a copy of "auto", so the new code simply maps one to the other.
>>>> One fewer place to update when we need to change the language
>>>> vector...
>>>
>>> I've searched the manual, and the "local" language is mentioned only in two places, once in the description of "set language" (where it explains that "local" and "auto" are the same) and once when we describe -data-evaluate-expression's --language flag.
>>>
>>> Honestly, is there any reason to keep it at all given it is a synonym of auto? [I realize that it doesn't cost much to maintain with this patch.] I'm not asking for any changes in this regard, just throwing the possibility out there. TBH, I didn't even know "local" existed.
>>
>> I didn't know about "local" until I stumbled on it working on this
>> series, either.  :-)
>>
>> I'll send a follow up reply with a different subject
>> to see if anyone else has comments on that.
>
> Does anyone here ever use "set language local" instead
> of "set language auto"?  Do we need to keep "set language local" ?
> Seems odd to have two ways to do the exact same thing.

Never knew about "local", and if it is a straight copy of auto don't
much see the point of keeping it.  if local came with some other flag,
that says "when the language inference fails, fall back to the local
language instead of minimal"

however that would probably be better achieved by adding python/guile
hooks called by the language inference mechanism for adding e.g. file
extensions, or even per-symbol heuristics if that would be possible.

my $0.02 from someone who has debugged some stuff where failure to
infer language leads to a pretty terrible experience.

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-07-20 19:06         ` Pedro Alves
@ 2017-08-08 20:29           ` Keith Seitz
  2017-10-19 17:36             ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 20:29 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 07/20/2017 12:06 PM, Pedro Alves wrote:
> On 07/20/2017 08:00 PM, Pedro Alves wrote:
> 
>> I'll send the updated patch #25 as a reply.
> 
> Here's the updated patch.  I've also force-pushed this to
> the users/palves/cxx-breakpoint-improvements branch, meanwhile
> rebased to current master.

Awesome. I've pulled your changes to retest, and I found no regressions
this time. :-)

> 	* ada-lang.c (ada_encode): Rename to ..
> 	(ada_encode_1): ... this.  Add throw_errors parameter and handle
> 	it.
> 	(ada_encode): Reimplement.
> 	(match_name): Delete, folded into full_name.
> 	(ada_lookup_simple_minsym): Use lookup_name_info and the
> 	language's symbol_name_matcher_ftype.
> 	(add_symbols_from_enclosing_procs, ada_add_local_symbols)
> 	(ada_add_block_symbols, ada_add_block_symbols): Adjust to use
> 	lookup_name_info.

ada_add_block_symbols is listed twice.

> 	* linespec.h (linespec_complete_function)
> 	(linespec_complete_label): Add name_match_type parameter.

I don't actually see any linespec.h diffs...

> 	* psymtab.c (psym_lookup_symbol): Use lookup_name_info.
> 	(match_partial_symbol): Use symbol_name_match_type and
> 	lookup_name_info.

Mention psymbol_name_matches (since you've mentioned it in other entries).

> 	* symfile-debug.c (debug_qf_map_matching_symbols)
> 	(debug_qf_map_matching_symbols): Use symbol_name_match_type.
> 	(debug_qf_expand_symtabs_matching): use lookup_name_info.
                                            ^
typo (lowercase letter)

> diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
> index b6eb382..58d51d7 100644
> --- a/gdb/ada-lang.c
> +++ b/gdb/ada-lang.c
> @@ -4935,23 +4928,19 @@ ada_lookup_simple_minsym (const char *name)
>    struct bound_minimal_symbol result;
>    struct objfile *objfile;
>    struct minimal_symbol *msymbol;
> -  const int wild_match_p = should_use_wild_match (name);
>  
>    memset (&result, 0, sizeof (result));
>  
> -  /* Special case: If the user specifies a symbol name inside package
> -     Standard, do a non-wild matching of the symbol name without
> -     the "standard__" prefix.  This was primarily introduced in order
> -     to allow the user to specifically access the standard exceptions
> -     using, for instance, Standard.Constraint_Error when Constraint_Error
> -     is ambiguous (due to the user defining its own Constraint_Error
> -     entity inside its program).  */
> -  if (startswith (name, "standard__"))
> -    name += sizeof ("standard__") - 1;
> +  symbol_name_match_type match_type = name_match_type_from_name (name);
> +  lookup_name_info lookup_name (name, match_type);
> +
> +  symbol_name_matcher_ftype *match_name
> +    = language_get_symbol_name_matcher (language_def (language_ada),
> +					lookup_name);

Okay, I understand that you did this to be consistent with generic code, but
it seems odd to use all that machinery to access ada_get_symbol_name_mathcer,
which is defined in this file. Just sayin' it kinda surprised me.

>  
>    ALL_MSYMBOLS (objfile, msymbol)
>    {
> -    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), name, wild_match_p)
> +    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), lookup_name, NULL)
>          && MSYMBOL_TYPE (msymbol) != mst_solib_trampoline)
>        {
>  	result.minsym = msymbol;
> @@ -5501,28 +5489,28 @@ aux_add_nonlocal_symbols (struct block *block, struct symbol *sym, void *data0)
>    return 0;
>  }
>  
> -/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are targetted
> -   by renamings matching NAME in BLOCK.  Add these symbols to OBSTACKP.  If
> -   WILD_MATCH_P is nonzero, perform the naming matching in "wild" mode (see
> -   function "wild_match" for more information).  Return whether we found such
> -   symbols.  */
> +/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are
> +   targetted by renamings matching LOOKUP_NAME in BLOCK.  Add these

*targeted

> +   symbols to OBSTACKP.  Return whether we found such symbols.  */
>  
>  static int
>  ada_add_block_renamings (struct obstack *obstackp,
>  			 const struct block *block,
> -			 const char *name,
> -			 domain_enum domain,
> -			 int wild_match_p)
> +			 const lookup_name_info &lookup_name,
> +			 domain_enum domain)
>  {
>    struct using_direct *renaming;
>    int defns_mark = num_defns_collected (obstackp);
>  
> +  symbol_name_matcher_ftype *name_match
> +    = language_get_symbol_name_matcher (language_def (language_ada),
> +					lookup_name);
> +

[/me again slightly surprised]

>    for (renaming = block_using (block);
>         renaming != NULL;
>         renaming = renaming->next)
>      {
>        const char *r_name;
> -      int name_match;
>  
>        /* Avoid infinite recursions: skip this renaming if we are actually
>  	 already traversing it.
>      }      	
>  }
>  
> -/* Find symbols in DOMAIN matching NAME, in BLOCK and, if FULL_SEARCH is
> -   non-zero, enclosing scope and in global scopes, returning the number of
> -   matches.  Add these to OBSTACKP.
> +/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if
> +   FULL_SEARCH is non-zero, enclosing scope and in global scopes,
> +   returning the number of matches.  Add these to OBSTACKP.
>  
>     When FULL_SEARCH is non-zero, any non-function/non-enumeral
>     symbol match within the nest of blocks whose innermost member is BLOCK,
> @@ -5776,17 +5780,17 @@ ada_add_all_symbols (struct obstack *obstackp,
>  
>    /* Search symbols from all global blocks.  */
>   
> -  add_nonlocal_symbols (obstackp, name, domain, 1, wild_match_p);
> +  add_nonlocal_symbols (obstackp, lookup_name, domain, 1);
>  
>    /* Now add symbols from all per-file blocks if we've gotten no hits
>       (not strictly correct, but perhaps better than an error).  */
>  
>    if (num_defns_collected (obstackp) == 0)
> -    add_nonlocal_symbols (obstackp, name, domain, 0, wild_match_p);
> +    add_nonlocal_symbols (obstackp, lookup_name, domain, 0);
>  }
>  
> -/* Find symbols in DOMAIN matching NAME, in BLOCK and, if full_search is
> -   non-zero, enclosing scope and in global scopes, returning the number of
> +/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if full_search
                                                                    ^^^^^^^^^^^
FULL_SEARCH (as for ada_add_all_symbols)?

> +   is non-zero, enclosing scope and in global scopes, returning the number of
>     matches.
>     Sets *RESULTS to point to a vector of (SYM,BLOCK) tuples,
>     indicating the symbols found and the blocks and symbol tables (if
> @@ -13920,16 +13841,113 @@ static const struct exp_descriptor ada_exp_descriptor = {
[snip]
>  
> -static symbol_name_cmp_ftype
> -ada_get_symbol_name_cmp (const char *lookup_name)

The removal of this function is not mentioned in the ChangeLog.


Wow, that was a lot of Ada. I hope that Joel can run this through Ada's
internal tests before this goes live (or he gives his approval). While I've
tried to follow along, I am desperately unversed in Ada.


> diff --git a/gdb/completer.h b/gdb/completer.h
> index f68c6dc..1958808 100644
> --- a/gdb/completer.h
> +++ b/gdb/completer.h
> @@ -68,6 +68,62 @@ struct match_list_displayer
>     calls free on each element.  */
>  typedef std::vector<gdb::unique_xmalloc_ptr<char>> completion_list;
>  
> +/* The result of a successful completion match.  When doing symbol
> +   comparison, we use the symbol search name for the symbol name match
> +   check, but the matched name that is shown to the user may be
> +   different.  For example, Ada uses encoded names for lookup, but
> +   then wants to decode the symbol name to show to the user, and also
> +   in some cases wrap the matched name in "<sym>" (meaning we can't
> +   always use the symbol's print name.  */
                                        ^

Missing closing parenthesis.

> +private:
> +  /* The completion match result.  This can either be a pointer into
> +     M_STORAGE string, or it can be a pointer into the some other
> +     string that outlives the completion matching sequence (usually, a
> +     pointer to a symbol's name.  */
                                  ^
Another missing close parenthesis.

> +  const char *m_match;
> +
> +  /* Storage a symbol comparison routine can use for generating a
> +     match result, dynamically.  The built string is only good until
> +     the next clear() call.  I.e., good until the next symbol
> +     comparison.  */
> +  std::string m_storage;
> +};
> +
> +/* Convenience aggregate holding info returned by the symbol name
> +   matching routines (see symbol_name_matcher_ftype).  */

> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
> index df9a563..95e7cb8 100644
> --- a/gdb/cp-support.c
> +++ b/gdb/cp-support.c
> @@ -1592,6 +1594,41 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
>    return *demangled != NULL;
>  }
>  
[snip]
> +
> +/* Implement the "la_get_symbol_name_matcher" language_defn method for
> +   C++.  */
> +

This comment should be moved to cp-support.h.

> +symbol_name_matcher_ftype *
> +cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
> +{
> +  return cp_fq_symbol_name_matches;
> +}
> +
>  /* Don't allow just "maintenance cplus".  */
>  
>  static  void
> diff --git a/gdb/cp-support.h b/gdb/cp-support.h
> index 37b281f..3a42cd6 100644
> --- a/gdb/cp-support.h
> +++ b/gdb/cp-support.h
> @@ -107,6 +107,9 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
>  extern struct type *cp_lookup_rtti_type (const char *name,
>  					 struct block *block);
>  
> +extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
> +  (const lookup_name_info &lookup_name);
> +

[insert comment from cp-support.c]

> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
> index 3c547c1..28236aa 100644
> --- a/gdb/dwarf2read.c
> +++ b/gdb/dwarf2read.c
> @@ -4063,10 +4065,97 @@ dw2_map_matching_symbols (struct objfile *objfile,
[snip]
> +
> +gdb_index_symbol_name_matcher::gdb_index_symbol_name_matcher
> +  (const lookup_name_info &lookup_name)
> +    : m_lookup_name (lookup_name)

While I don't object to this formatting, IIRC we are following GCC's lead,
and their coding standard says,

  If a C++ function name is long enough to cause the first function parameter
  with its type to exceed 80 characters, it should appear on the next line
  indented four spaces.

  void
  very_long_class_name::very_long_function_name (
      very_long_type_name arg)
  {
  [https://gcc.gnu.org/codingconventions.html#Member_Form]

Have I mentioned that I think we really need to create/publish our own
coding standard (or at least a C++ cookbook)? ;-)

> +{
> +  /* Prepare the vector of comparison functions upfront, to avoid
> +     doing the same work for each symbol.  Care is taken to avoid
> +     matching with the same matcher more than once if/when multiple
> +     languages use the same matcher function.  */
> +  auto &matchers = m_symbol_name_matcher_funcs;
> +  matchers.reserve (nr_languages);
> +
> +  for (int i = 0; i < nr_languages; i++)
> +    {
> +      const language_defn *lang = language_def ((enum language) i);
> +      if (lang->la_get_symbol_name_matcher != NULL)
> +	{
> +	  symbol_name_matcher_ftype *name_matcher
> +	    = lang->la_get_symbol_name_matcher (m_lookup_name);
> +
> +	  /* Don't insert the same comparison routine more than once.
> +	     Note that we do this linear walk instead of a cheaper
> +	     sorted insert, or use a std::set or something like that,
> +	     because relative order of function addresses is not
> +	     stable.  This is not a problem in practice because the
> +	     number of supported languages is low, and the cost here
> +	     is tiny compared to the number of searches we'll do
> +	     afterwards using this object.  */
> +	  if (std::find (matchers.begin (), matchers.end (), name_matcher)
> +	      == matchers.end ())
> +	    matchers.push_back (name_matcher);
> +	}
> +    }
> +  if (std::find (matchers.begin (), matchers.end (),
> +		 default_symbol_name_matcher) == matchers.end ())
> +    matchers.push_back (default_symbol_name_matcher);

Is there a reason not to do this before looping since the loop already does
duplicate elimination? [I realize the list of languages is tiny.]

> +}
> +
> +bool
> +gdb_index_symbol_name_matcher::matches (const char *symbol_name)
> +{
> +  for (auto matches_name : m_symbol_name_matcher_funcs)
> +    if (matches_name (symbol_name, m_lookup_name, NULL))
> +      return true;
> +
> +  return false;
> +}
> +
>  static void
>  dw2_expand_symtabs_matching
>    (struct objfile *objfile,
>     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +   const lookup_name_info &lookup_name,
>     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
>     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
>     enum search_domain kind)
> diff --git a/gdb/linespec.c b/gdb/linespec.c
> index 136cb65..a241419 100644
> --- a/gdb/linespec.c
> +++ b/gdb/linespec.c
> @@ -3613,14 +3614,18 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
>    struct symtab *elt;
>    decode_compound_collector collector;
>  
> +  lookup_name_info lookup_name (class_name, symbol_name_match_type::FULL);
> +
>    for (ix = 0; VEC_iterate (symtab_ptr, file_symtabs, ix, elt); ++ix)
>      {
>        if (elt == NULL)
>  	{
> -	  iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN,
> -					     NULL, false, collector);
> -	  iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN,
> -					     NULL, false, collector);
> +	  iterate_over_all_matching_symtabs (state, lookup_name,
> +					     STRUCT_DOMAIN, NULL, false,
> +					     collector);
> +	  iterate_over_all_matching_symtabs (state, lookup_name,
> +					     VAR_DOMAIN, NULL, false,
> +					     collector);
>  	}
>        else
>  	{

Side note (for myself, really): I think we might be able to  avoid doing both
STRUCT_DOMAIN and VAR_DOMAIN searches for several languages, including C++,
because of the (vile) behavior of symbol_matches_domain. It might be a
little optimization (which would need careful documenting if I should ever
manage to delete symbol_matches_domain).

> diff --git a/gdb/minsyms.c b/gdb/minsyms.c
> index c93eaa3..56fb5b4 100644
> --- a/gdb/minsyms.c
> +++ b/gdb/minsyms.c

> +/* Worker object for lookup_minimal_symbol.  Stores temporary results
> +   while walking the symbol tables.  */
> +
> +struct found_minimal_symbols
> +{
> +  /* External symbols are best.  */
> +  bound_minimal_symbol external_symbol {};
> +
> +  /* File-local symbols are next best.  */
> +  bound_minimal_symbol file_symbol {};
> +
> +  /* Symbols for shared library trampolines are next best.  */
> +  bound_minimal_symbol trampoline_symbol {};
> +
> +  /* Called when a symbol name matches.  Check if the minsym is a
> +     better type than what we had already found, and record it in one
> +     of the members fields if so.  Returns true if we already have the
> +     real symbol.  */
> +  bool maybe_collect (const char *sfile, objfile *objf,
> +		      minimal_symbol *msymbol);

I apologize, but this is a nit; don't feel obligated to change anything.
The return value to this function does not obviously map to its purpose.

Could the comment be amended/changed to explicitly mention that it means one
should stop searching for better alternatives?

> +};
> +
> +bool
> +found_minimal_symbols::maybe_collect (const char *sfile,
> +                                     struct objfile *objfile,
> +                                     minimal_symbol *msymbol)

Serious question: Do we need to put "See minsyms.h" here or are we going to
start assuming that all member definitions are documented in the
corresponding declaration? I wouldn't mind that simplification, but I thought
I'd mention it.

> diff --git a/gdb/psymtab.c b/gdb/psymtab.c
> index 4077fb3..fb0a55d 100644
> --- a/gdb/psymtab.c
> +++ b/gdb/psymtab.c
> @@ -542,6 +544,18 @@ psym_lookup_symbol (struct objfile *objfile,
>    return stab_best;
>  }
>  
> +/* Returns true if PSYM matches LOOKUP_NAME.  */
> +
> +static bool
> +psymbol_name_matches (partial_symbol *psym,
> +		      const lookup_name_info &lookup_name)
> +{
> +  const language_defn *lang = language_def (SYMBOL_LANGUAGE (psym));
> +  symbol_name_matcher_ftype *name_match
> +    = language_get_symbol_name_matcher (lang, lookup_name);
> +  return name_match (SYMBOL_SEARCH_NAME (psym), lookup_name, NULL);
> +}
> +

This is called in tight loops while searching for matching psymbols.
If there is some merit to the assumption (?!) that large amounts of psymbols
within any given objfile are the same language, this seems ripe for some
sort of trivial caching optimization. Perhaps, though, this is an
"optimization" that will have no impact on performance. [Or maybe you've
addressed this already in a future patch. Or I've missed a use case.]

>  static bool
>  recursively_search_psymtabs
> -  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain kind,
> +  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain domain,
> +   const lookup_name_info &lookup_name,
>     gdb::function_view<expand_symtabs_symbol_matcher_ftype> sym_matcher)

I am not bothered by renaming parameters for consistency, but it does, IMO,
deserve mention in the ChangeLog when it is not necessitated by some other
change, e.g., "name" -> "lookup_name".

>  {
>    struct partial_symbol **psym;
> @@ -1381,9 +1412,10 @@ static void
>  psym_expand_symtabs_matching
>    (struct objfile *objfile,
>     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +   const lookup_name_info &lookup_name,
>     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
>     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> -   enum search_domain kind)
> +   enum search_domain domain)
>  {
>    struct partial_symtab *ps;
>  

Likewise

> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index ba2c559..6914f69 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -1751,6 +1766,30 @@ fixup_symbol_section (struct symbol *sym, struct objfile *objfile)
>    return sym;
>  }
>  
> +/* See symtab.h.  */
> +
> +demangle_for_lookup_info::demangle_for_lookup_info (const lookup_name_info &lookup_name,
> +						    language lang)

line length == 88

[snip]

> @@ -4755,20 +4816,35 @@ compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
>    return 1;
>  }
>  
> -/*  Test to see if the symbol specified by SYMNAME (which is already
> -   demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
> -   characters.  If so, add it to the current completion list.  */
> +/*  Test to see if the symbol of language SYMBOL_LANGUAGE specified by
> +    SYMNAME (which is already demangled for C++ symbols) matches
> +    SYM_TEXT in the first SYM_TEXT_LEN characters.  If so, add it to
> +    the current completion list.  */
>  
> -static void
> +void
>  completion_list_add_name (completion_tracker &tracker,

This comment should have been moved to symtab.h along with the decl.

> +			  language symbol_language,
>  			  const char *symname,
> +			  const lookup_name_info &lookup_name,
>  			  const char *sym_text, int sym_text_len,
>  			  const char *text, const char *word)
>  {
> +  completion_match_result &match_res
> +    = tracker.reset_completion_match_result ();
> +
>    /* Clip symbols that cannot match.  */
> -  if (!compare_symbol_name (symname, sym_text, sym_text_len))
> +  if (!compare_symbol_name (symname, symbol_language,
> +			    lookup_name,
> +			    sym_text, sym_text_len,
> +			    match_res))
>      return;
>  
> +  /* Refresh SYMNAME from the match string.  It's potentially
> +     different depending on language.  (E.g., on Ada, the match may be
> +     the encoded symbol name wrapped in "<>").  */
> +  symname = match_res.match.match ();
> +  gdb_assert (symname != NULL);
> +
>    /* We have a match for a completion, so add SYMNAME to the current list
>       of matches.  Note that the name is moved to freshly malloc'd space.  */
>  
> +

I think you meant to clear the previous line (which is needlessly indented),
no?

[snip]

> diff --git a/gdb/symtab.h b/gdb/symtab.h
> index da5cf25..fe7c1c4 100644
> --- a/gdb/symtab.h
> +++ b/gdb/symtab.h
> @@ -43,6 +45,249 @@ struct probe;
[snip]
> +
> +extern unsigned int search_name_hash (enum language language,
> +				      const char *search_name);
> +

symtab.c says, "See symtab.h." The above is missing that comment.

> +/* Ada-specific bits of a lookup_name_info object.  This is lazily
> +   constructed on demand.  */
> +
> +class ada_lookup_name_info final
> +{
> + public:
> +  /* Construct.  */

Just a side question... Are these types of comments particularly useful? I
realize we (used to?) have a rather strict "document every function" policy, but
(nearly) trivial ctors and (especially) dtors?

> +  explicit ada_lookup_name_info (const lookup_name_info &lookup_name);
> +

> +/* Object that aggregates all information relative to a symbol lookup
                                             ^^^^^^^^
I think you mean "relevant" here?

> +   name.  I.e., the name that is matched against the symbol's search
> +   name.  Caches per-language information so that it doesn't require
> +   recomputing it for every symbol comparison, like for example the
> +   Ada encoded name and the symbol's name hash for a given language.
> +   The object is conceptually immutable once constructed, and thus has
> +   no setters.  This is to avoid the case of a code path tweaking some
> +   property of the lookup name for some local reason, and accidentally
> +   causing other parts of symbol search that continue searching using
> +   the same lookup name be affected.  lookup_name_info objects are
> +   generally passed around as a const reference to reinforce that.
> +   (They're not passed around by value because they're not small.)  */

May I suggest rewording that one sentence:

  This is to prevent some code path from tweaking some property of the
  lookup name for some local reason and accidentally altering the results
  of any continuing search(es).

Or something like that. The fragment "... using the same lookup name be
affected" doesn't make sense (grammatically).

> +class lookup_name_info final
> +{
> + public:
> +  /* Create a new object.  */
> +  lookup_name_info (std::string name,
> +		    symbol_name_match_type match_type,
> +		    bool completion_mode = false)
> +    : m_match_type (match_type),
> +      m_completion_mode (completion_mode),
> +      m_name (std::move (name))
> +  {}
> +
> +  /* Getters.  See description of each corresponding field.  */
> +  symbol_name_match_type match_type () const { return m_match_type; }
> +  bool completion_mode () const { return m_completion_mode; }
> +  const std::string &name () const { return m_name; }

For the record, I don't have a problem with this style of commenting multiple
simplistic methods. I hope we codify this (at least) for simple
getter/setter-type methods.

[snip]

> @@ -1700,4 +1953,11 @@ void initialize_objfile_symbol (struct symbol *);
>  
>  struct template_symbol *allocate_template_symbol (struct objfile *);
>  
> +void completion_list_add_name (completion_tracker &tracker,
> +			       language symbol_language,
> +			       const char *symname,
> +			       const lookup_name_info &lookup_name,
> +			       const char *sym_text, int sym_text_len,
> +			       const char *text, const char *word);
> +

[comment from symtab.c goes here]

> diff --git a/gdb/utils.h b/gdb/utils.h
> index 48330a1..c3d707c 100644
> --- a/gdb/utils.h
> +++ b/gdb/utils.h
> @@ -31,6 +31,33 @@ extern void initialize_utils (void);
>  
>  extern int sevenbit_strings;
>  
> +/* Modes of operation for strncmp_iw_with_mode.  */
> +
> +enum class strncmp_iw_mode
> +{
> +/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
> +   differences in whitespace.  Returns 0 if they match, non-zero if they
> +   don't (slightly different than strcmp()'s range of return values).
> +
> +   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
> +   This "feature" is useful when searching for matching C++ function names
> +   (such as if the user types 'break FOO', where FOO is a mangled C++
> +   function).  */
> +  NORMAL,

Isn't this described "hack" the same as the hack for MATCH_PARAMS:

> +
> +  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
> +     string1=="FOO(PARAMS)" matches string2=="FOO".  */
> +  MATCH_PARAMS,
> +};

Maybe it's a cut-n-paste-o?

Keith

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

* Re: [PATCH 26/40] Optimize .gdb_index symbol name searching
  2017-06-02 12:39 ` [PATCH 26/40] Optimize .gdb_index symbol name searching Pedro Alves
@ 2017-08-08 20:32   ` Keith Seitz
  2017-11-08 16:14     ` Pedro Alves
  2017-11-18  5:23   ` [PATCH 26/40] Optimize .gdb_index symbol name searching Simon Marchi
  1 sibling, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 20:32 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:

> I got, before the previous patch (-O2, x86-64):
> 
>  real    0m1.773s
>  user    0m1.737s
>  sys     0m0.040s
> 
> and after this patch:
> 
>  real    0m1.361s
>  user    0m1.315s
>  sys     0m0.040s

The results on my computer are slightly more dramatic, running with no
optimization, your test case (using Fedora 21 system gdb w/index debuginfo)
goes from about 15 seconds down to about 2.5 seconds. Very nice!

> That resulted in 1351355 name_components.  Each entry takes 8 bytes,
> so that's 10810840 bytes (ignoring std::vector overhead), or ~10.3 MB.
> That's IMO too small to worry about, given GDB was using over 7400MB
> total at that point.  I.e., we're talking about 0.1% increase.

Indeed. I'd sacrifice that kind of memory for the kind of speed increase
you've achieved -- in a heartbeat!

> with only 8-bit and 32-bit tables, that'd be:
> 
>  1349057 * 1 + 2298 * 4 + 4 * 1351355 = 6763669 bytes, or ~6.5MB.
> 
> I don't think we need to bother though.

I'm all for memory usage optimization and whatnot, but since the benefit is
so small (55% of these new tables saved but only 0.06% of total memory),
I prefer simplicity. So you won't get anything but agreement from me on this.

> I also timed:
> 
>  $ time gdb --batch -q -p `pidof firefox`
>  $ time gdb --batch -q -p `pidof firefox` -ex "b main"
>  $ time gdb --batch -q -p `pidof firefox` -ex "set max-completion unlimited" -ex "complete b "

I'd like to reproduce this, but my computer is incapable of running this test.
I'll take your word for it. ;-)

> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* dwarf2read.c 
> 	(mapped_index::name_components): New field.
> 	(mapped_index::symbol_name_at): New method.

Silly nit: Isn't the form most are using "(tag name) <field>: New field."?
I know I've relied on this several times to find changes in the ChangeLog.

> 	(create_addrmap_from_index): Call mapped_index ctor.

I don't see any changes to this function in the patch -- attributed to wrong
function?

> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
> index f523326..e955131 100644
> --- a/gdb/dwarf2read.c
> +++ b/gdb/dwarf2read.c
> @@ -178,6 +178,51 @@ DEF_VEC_I (offset_type);
[snip]
> +
> +/* An index into a (C++) symbol name component in a symbol name as
> +   recorded in the mapped_index's symbol table.  For each C++ symbol
> +   in the symbol table, we record one entry for the start of each
> +   component in the symbol in a table of name components, and then
> +   sort the table, in order to be able to binary search symbol names,
> +   ignoring leading namespaces, both completion and regular look up.
> +   For example, for symbol "A::B::C", we'll have an entry that points
> +   to "A::B::C", another that points to "B::C", and another for "C".
> +   Note that function symbols in GDB index have no parameter
> +   information, just the function/method names.  You can convert a
> +   name_component to a "const char *" using the
> +   'mapped_index::symbol_name_at(offset_type)' method.  */

missing nl?

> +struct name_component
> +{
> +  /* Offset in the symbol name where the component starts.  Stored as
> +     a (32-bit) offset instead of a pointer to save memory and improve
> +     locality on 64-bit architectures.  */
> +  offset_type name_offset;
> +
> +  /* The symbol's index in the symbol and constant pool tables of a
> +     mapped_index.  */
> +  offset_type idx;
> +};
> +
>  /* A description of the mapped index.  The file format is described in
>     a comment by the code that writes the index.  */
>  struct mapped_index
> @@ -3390,6 +3424,7 @@ dwarf2_read_index (struct objfile *objfile)
>    create_addrmap_from_index (objfile, &local_map);
>  
>    map = XOBNEW (&objfile->objfile_obstack, struct mapped_index);
> +  map = new (map) mapped_index ();
>    *map = local_map;
>  
>    dwarf2_per_objfile->index_table = map;

This function (dwarf2_read_index) is not mentioned in the ChangeLog. Could
this actually be incorrectly listed in the ChangeLog under
create_addrmap_from_index?

> @@ -4095,6 +4134,22 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
>  }
>  
>  static void
> +dw2_expand_marked_cus
> +  (mapped_index &index, offset_type idx,
> +   struct objfile *objfile,
> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> +   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
> +   search_domain kind);
> +
> +static void
> +dw2_expand_symtabs_matching_symbol
> +  (mapped_index &index,
> +   const lookup_name_info &lookup_name_in,
> +   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +   enum search_domain kind,
> +   gdb::function_view<void (offset_type)> on_match);

Isn't it rather unusual for us to have forward decls in the middle of a file?

> +
> +static void
>  dw2_expand_symtabs_matching
>    (struct objfile *objfile,
>     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
> @@ -4186,30 +4239,214 @@ dw2_expand_symtabs_matching
[snip]
> +static void
> +dw2_expand_symtabs_matching_symbol
> +  (mapped_index &index,
> +   const lookup_name_info &lookup_name,
> +   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +   enum search_domain kind,
> +   gdb::function_view<void (offset_type)> match_callback)
> +{
[snip]
> +
> +      /* Sort name_comp elements by name.   */

I presume that "name_comp" is really "name_components"?

[snip]

> +  std::vector<offset_type> matches;
> +  matches.reserve (std::distance (lower, upper));
> +
> +  for (;lower != upper; ++lower)
> +    {
> +      const char *qualified = index.symbol_name_at (lower->idx);
> +
> +      if (!lookup_name_matcher.matches (qualified)
> +	  || (symbol_matcher != NULL && !symbol_matcher (qualified)))
>  	continue;
>  
> -      /* The name was matched, now expand corresponding CUs that were
> -	 marked.  */
> -      vec = (offset_type *) (index->constant_pool
> -			     + MAYBE_SWAP (index->symbol_table[idx + 1]));
> +      matches.push_back (lower->idx);
> +    }
> +
> +  std::sort (matches.begin (), matches.end ());
> +
> +  /* Finally call the callback, once per match.  */
> +  ULONGEST prev = -1;
> +  for (offset_type idx : matches)
> +    {
> +      if (prev != idx)
> +	{
> +	  match_callback (idx);
> +	  prev = idx;
> +	}
> +    }

I admit, I'm a little surprised by the number of steps involved here: push
every element in the range into a vector, sort, then de-dup & perform callback.
Had I implemented this, my initial attempt would have been to use a htab_up
and take the sorting time on insertion.

I can imagine that for very large ranges, your approach could outperform
an htab; do you expect these ranges to be that large? Have I overlooked
something? [I'm just curious. Not suggesting any changes need to be made.]

> +
> +  /* Above we use a type wider than idx's for 'prev', since 0 and
> +     (offset_type)-1 are both possible values.  */
> +  static_assert (sizeof (prev) > sizeof (offset_type), "");
> +}
> +
[snip]

Keith

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

* Re: [PATCH 27/40] Make cp_remove_params return a unique_ptr
  2017-06-02 12:23 ` [PATCH 27/40] Make cp_remove_params return a unique_ptr Pedro Alves
@ 2017-08-08 20:35   ` Keith Seitz
  2017-10-09 15:13     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 20:35 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	(overload_list_add_symbol): Adjust to use gdb::unique_xmalloc_ptr.
> 	* cp-support.h (cp_remove_params): Now returns a
> 	gdb::unique_xmalloc_ptr.
> 	* dwarf2read.c (find_slot_in_mapped_hash): Now returns bool.
> 	Adjust to use gdb::unique_xmalloc_ptr.
> 	(dw2_expand_symtabs_matching_symbol): Adjust to use
> 	gdb::unique_xmalloc_ptr.
> 	* psymtab.c (psymtab_search_name): Now returns a
> 	gdb::unique_xmalloc_ptr.
> 	(lookup_partial_symbol): Adjust to use gdb::unique_xmalloc_ptr.
> 	* stack.c (find_frame_funname): Adjust to use
> 	gdb::unique_xmalloc_ptr.

LGTM

Keith

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

* Re: [PATCH 28/40] lookup_name_info::make_ignore_params
  2017-06-02 12:23 ` [PATCH 28/40] lookup_name_info::make_ignore_params Pedro Alves
@ 2017-08-08 20:55   ` Keith Seitz
  2017-11-08 16:18     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 20:55 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
> 	unittests/lookup_name_info-selftests.c.
> 	(SUBDIR_UNITTESTS_OBS): Add lookup_name_info-selftests.o.
> 	* cp-support.c: Include "selftest.h".
> 	(cp_remove_params_1): Rename from cp_remove_params.  Add
> 	'require_param's parameter, and handle it.
                      ^^
typo

> 	(cp_remove_params): Reimplement.
> 	(cp_remove_params_if_any): New.
> 	(selftests::quote): New.
> 	(selftests::check_remove_params): New.
> 	(selftests::test_cp_remove_params): New.
> 	(_initialize_cp_support): Install
> 	selftests::test_cp_remove_params.
> 	* cp-support.h (cp_remove_params_if_any): Declare.
> 	* dwarf2read.c :Include "selftest.h".
> 	(dw2_expand_symtabs_matching_symbol): Use
> 	lookup_name_info::make_ignore_params.
> 	(selftests::dw2_expand_symtabs_matching::mock_mapped_index)
> 	(selftests::dw2_expand_symtabs_matching::string_or_null)
> 	(selftests::dw2_expand_symtabs_matching::check_match)
> 	(selftests::dw2_expand_symtabs_matching::test_symbols)
> 	(selftests::dw2_expand_symtabs_matching::run_test): New.
> 	(_initialize_dwarf2_read): Register
> 	selftests::dw2_expand_symtabs_matching::run_test.
> 	* psymtab.c (psym_expand_symtabs_matching): Use
> 	lookup_name_info::make_ignore_params.
> 	* symtab.c (demangle_for_lookup_info::demangle_for_lookup_info):
> 	If the lookup name wants to ignore parameters, strip them.
> 	(compare_symbol_name): Remove sym_text/sym_text_len parameters and
> 	code handling '('.
> 	(completion_list_add_name): Don't pass down sym_text/sym_text_len.
> 	(default_collect_symbol_completion_matches_break_on): Don't try to
> 	strip parameters.
> 	* symtab.h (lookup_name_info::lookup_name_info): Add
> 	'ignore_parameters' parameter.
> 	(lookup_name_info::ignore_parameters)
> 	(lookup_name_info::make_ignore_params): New methods.
> 	(lookup_name_info::m_ignore_parameters): New field.

(class lookup_name_info) <ingore_parameters>: ?
(class lookup_name_into) <make_ignore_params>: ?
(class lookup_name_info) <m_ignore_params>: ?

> 	* unittests/lookup_name_info-selftests.c: New file.


> 	* dwarf2read.c :
> 	(selftests::dw2_expand_symtabs_matching::mock_mapped_index)
> 	(selftests::dw2_expand_symtabs_matching::string_or_null)
> 	(selftests::dw2_expand_symtabs_matching::check_match)
> 	(selftests::dw2_expand_symtabs_matching::test_symbols)
> 	(selftests::dw2_expand_symtabs_matching::run_test): New.
> 	(_initialize_dwarf2_read): Register
> 	selftests::dw2_expand_symtabs_matching::run_test.

Just some trivial things that I noticed (inline).

> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
> index 0095c6d..064a45b 100644
> --- a/gdb/cp-support.c
> +++ b/gdb/cp-support.c
> @@ -833,12 +834,14 @@ cp_func_name (const char *full_name)
>    return ret;
>  }
>  
> -/* DEMANGLED_NAME is the name of a function, including parameters and
> -   (optionally) a return type.  Return the name of the function without
> -   parameters or return type, or NULL if we can not parse the name.  */
> +/* Helper for cp_remove_params.  DEMANGLED_NAME is the name of a
> +   function, including parameters and (optionally) a return type.
> +   Return the name of the function without parameters or return type,
> +   or NULL if we can not parse the name.  If COMPLETION_MODE is true,
> +   then tolerate a non-existing or unbalanced parameter list.  */

s/COMPLETION_MODE/REQUIRE_PARAMS/ ?

> -gdb::unique_xmalloc_ptr<char>
> -cp_remove_params (const char *demangled_name)
> +static gdb::unique_xmalloc_ptr<char>
> +cp_remove_params_1 (const char *demangled_name, bool require_params)
>  {
>    bool done = false;
>    struct demangle_component *ret_comp;
> @@ -874,10 +877,60 @@ cp_remove_params (const char *demangled_name)
[snip]
> +/* DEMANGLED_NAME is the name of a function, (optionally) including
> +   parameters and (optionally) a return type.  Return the name of the
> +   function without parameters or return type, or NULL if we can not
> +   parse the name.  If COMPLETION_MODE is true, then tolerate a
> +   non-existing or unbalanced parameter list.  */
> +

Shouldn't this comment be in cp-support.h?

> +gdb::unique_xmalloc_ptr<char>
> +cp_remove_params_if_any (const char *qualified, bool completion_mode)
> +{
> +  /* Trying to remove parameters from the empty string fails.  If
> +     we're completing / matching everything, avoid returning NULL
[snip]
> +  /* Shouldn't this parse?  Looks like a bug in
> +     cp_demangled_name_to_comp.  */

Is there a bugzilla (or some other tracking) for this?

> +#if 0
> +  CHECK ("A::foo<void(int)>::func(int)",
> +	 "A::foo<void(int)>::func");
> +#else
> +  CHECK_INCOMPL ("A::foo<void(int)>::func(int)",
> +		 "A::foo");
> +#endif
> +
> +  CHECK_INCOMPL ("A::foo<void(int",
> +		 "A::foo");
> +
> +#undef CHECK
> +#undef CHECK_INCOMPL
> +}
> +
> +} // namespace selftests
> +
> +#endif /* GDB_SELF_CHECK */
> +
>  /* Don't allow just "maintenance cplus".  */
>  
>  static  void
> diff --git a/gdb/cp-support.h b/gdb/cp-support.h
> index dd42415..1cef3f7 100644
> --- a/gdb/cp-support.h
> +++ b/gdb/cp-support.h
> @@ -97,6 +97,9 @@ extern char *cp_func_name (const char *full_name);
>  
>  extern gdb::unique_xmalloc_ptr<char> cp_remove_params (const char *qualified);
>  
> +extern gdb::unique_xmalloc_ptr<char> cp_remove_params_if_any
> +  (const char *qualified, bool completion_mode);

[Add comment from cp-support.c?]

> +
>  extern struct symbol **make_symbol_overload_list (const char *,
>  						  const char *);
>  
> diff --git a/gdb/symtab.h b/gdb/symtab.h
> index 4c8b1f6..a452804 100644
> --- a/gdb/symtab.h
> +++ b/gdb/symtab.h
> @@ -176,6 +178,16 @@ class lookup_name_info final
>    symbol_name_match_type match_type () const { return m_match_type; }
>    bool completion_mode () const { return m_completion_mode; }
>    const std::string &name () const { return m_name; }
> +  const bool ignore_parameters () const { return m_ignore_parameters; }
> +
> +  /* Return a version of this lookup name that is usable with
> +     comparisons against symbols have have no parameter info, such as
                                    ^^^^^^^^^
typo

> +     psymbols and GDB index symbols.  */
> +  lookup_name_info make_ignore_params () const
> +  {
> +    return lookup_name_info (m_name, m_match_type, m_completion_mode,
> +			     true /* ignore params */);
> +  }
>  
>    /* Get the search name hash for searches in language LANG.  */
>    unsigned int search_name_hash (language lang) const

Keith

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

* Re: [PATCH 29/40] Simplify completion_list_add_name | remove sym_text / sym_text_len
  2017-06-02 12:31 ` [PATCH 29/40] Simplify completion_list_add_name | remove sym_text / sym_text_len Pedro Alves
@ 2017-08-08 20:59   ` Keith Seitz
  2017-11-08 16:19     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 20:59 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* ada-lang.c (ada_make_symbol_completion_list): Remove text and
> 	text_len locals and don't pass them down.
> 	* symtab.c (completion_list_add_name): Remove
> 	sym_text/sym_text_len parameters and adjust.
> 	(completion_list_add_symbol, completion_list_add_msymbol)
> 	(completion_list_objc_symbol, completion_list_add_fields)
> 	(add_symtab_completions): Likewise.
> 	(default_collect_symbol_completion_matches_break_on)
> 	(collect_file_symbol_completion_matches): Remove sym_text_len
> 	local and don't pass it down.
> 	* symtab.h (completion_list_add_name): Remove
> 	sym_text/sym_text_len parameters.

LGTM

Keith

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

* Re: [PATCH 30/40] Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints
  2017-06-02 12:30 ` [PATCH 30/40] Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints Pedro Alves
@ 2017-08-08 21:07   ` Keith Seitz
  2017-11-08 16:20     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 21:07 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:

> however, we're currently passing down search_domain::ALL_DOMAIN when
> we know we're looking for functions, for no good reason.  This patch
> fixes that.

I have a similar patch on the compile branch.

> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* linespec.c (iterate_over_all_matching_symtabs): Add
> 	search_domain parameter.  Pass it down to expand_symtabs_matching.
> 	(decode_objc): Request FUNCTIONS_DOMAIN symbols only.
> 	(lookup_prefix_sym): Adjust by passing ALL_DOMAIN as
> 	search_domain.
> 	(add_all_symbol_names_from_pspace): Add search_domain parameter.
> 	Pass it down.
> 	(find_method, find_function_symbols): Request FUNCTIONS_DOMAIN
> 	symbols.
> 	(add_matching_symbols_to_info): Add search_domain parameter.  Pass
> 	it down.

LGTM

Keith

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

* Re: [PATCH 31/40] Handle custom completion match prefix / LCD
  2017-06-02 12:33 ` [PATCH 31/40] Handle custom completion match prefix / LCD Pedro Alves
@ 2017-08-08 21:28   ` Keith Seitz
  2017-11-27 17:11     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 21:28 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:

> diff --git a/gdb/completer.h b/gdb/completer.h
> index df90822..db781ab 100644
> --- a/gdb/completer.h
> +++ b/gdb/completer.h
> @@ -116,12 +116,44 @@ private:
>    std::string m_storage;
>  };
>  
> +/* The result of a successful completion match, but for least common
> +   denominator (LCD) computation.  Some completers provide matches
> +   that don't start with the completion "word".  E.g., completing on
> +   "b push_ba" on a C++ program usually completes to
> +   std::vector<...>::push_back, std::string::push_back etc.  In such
> +   case, the symbol comparison routine will set the LCD match to point
> +   into the "push_back" substring within the symbol's name string.  */

[missing newline?]

> +class completion_match_for_lcd
> +{
> +public:
> +  /* Set the match for LCD.  See m_match's description.  */
> +  void set_match (const char *match)
> +  { m_match = match; }
> +
> +  /* Get the resulting LCD, after a successful match.  */
> +  const char *finish ()
> +  { return m_match; }
> +
> +  /* Prepare for another completion matching sequence.  */
> +  void clear ()
> +  { m_match = NULL; }
> +
> +private:
> +  /* The completion match result for LCD.  This is usually either a
> +     pointer into to a substring within a symbol's name, or to the
> +     storage of the pairing completion_match object.  */
> +  const char *m_match;
> +};
> +
>  /* Convenience aggregate holding info returned by the symbol name
>     matching routines (see symbol_name_matcher_ftype).  */
>  struct completion_match_result
>  {
>    /* The completion match candidate.  */
>    completion_match match;
> +
> +  /* The completion match, for LCD computation purposes.  */
> +  completion_match_for_lcd match_for_lcd;
>  };
>  
>  /* The final result of a completion that is handed over to either
> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
> index 064a45b..397c738 100644
> --- a/gdb/cp-support.c
> +++ b/gdb/cp-support.c
> @@ -1660,8 +1660,11 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
>  			    name.c_str (), name.size (),
>  			    mode) == 0)
>      {
> -      if (match != NULL)
> -	match->set_match (symbol_search_name);
> +      if (comp_match_res != NULL)
> +	{
> +	  comp_match_res->match.set_match (symbol_search_name);
> +	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
> +	}
>        return true;
>      }
>  
> diff --git a/gdb/language.c b/gdb/language.c
> index 6705a3b..511a86f 100644
> --- a/gdb/language.c
> +++ b/gdb/language.c
> @@ -725,8 +725,11 @@ default_symbol_name_matcher (const char *symbol_search_name,
>    if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
>  			    mode) == 0)
>      {
> -      if (match != NULL)
> -	match->set_match (symbol_search_name);
> +      if (comp_match_res != NULL)
> +	{
> +	  comp_match_res->match.set_match (symbol_search_name);
> +	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
> +	}
>        return true;
>      }
>    else

In the above two hunks, the code calls result->match.set_match (A)
and result->match_for_lcd.set_match (A), i.e., both these calls take the
same argument and are called at the same time.

Furthermore, on the final series of the patch,

$ grep -n set_match\ \( *.c
ada-lang.c:6416:	  comp_match_res->match.set_match (match_str.c_str ());
ada-lang.c:6417:	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
ada-lang.c:6426:	  comp_match_res->match.set_match (match_str.c_str ());
ada-lang.c:6427:	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
cp-support.c:1734:	      comp_match_res->match.set_match (symbol_search_name);
cp-support.c:1735:	      comp_match_res->match_for_lcd.set_match (sname);
cp-support.c:1773:	  comp_match_res->match.set_match (symbol_search_name);
cp-support.c:1774:	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
language.c:725:	  comp_match_res->match.set_match (symbol_search_name);
language.c:726:	  comp_match_res->match_for_lcd.set_match (symbol_search_name);

It appears that these two are /always/ called together, suggesting that
completion_match_result might "benefit" from a convenience method to set both
of these parameters at the same time. [Unless there is a specific reason
not to do this, of course.] WDYT?

> diff --git a/gdb/symtab.h b/gdb/symtab.h
> index d68eed8..736fea0 100644
> --- a/gdb/symtab.h
> +++ b/gdb/symtab.h
> @@ -292,15 +292,21 @@ private:
>  
>     SYMBOL_SEARCH_NAME should be a symbol's "search" name.
>  
> -   On success and if non-NULL, MATCH is set to point to the symbol
> -   name as should be presented to the user as a completion match list
> -   element.  In most languages, this is the same as the symbol's
> -   search name, but in some, like Ada, the display name is dynamically
> -   computed within the comparison routine.  */
> +   On success and if non-NULL, COMP_MATCH_RES->match is set to point
> +   to the symbol name as should be presented to the user as a
> +   completion match list element.  In most languages, this is the same
> +   as the symbol's search name, but in some, like Ada, the display
> +   name is dynamically computed within the comparison routine.
> +
> +   Also, on success and if non-NULL, COMP_MATCH_RES->match_for_lcd
> +   points the part of SYMBOL_SEARCH_NAME that was considered to match
            ^
missing "to"

> +   LOOKUP_NAME.  E.g., in C++, in linespec/wild mode, if the symbol is
> +   "foo::function()" and LOOKUP_NAME is "function(", MATCH_FOR_LCD
> +   points to "function()" inside SYMBOL_SEARCH_NAME.  */
>  typedef bool (symbol_name_matcher_ftype)
>    (const char *symbol_search_name,
>     const lookup_name_info &lookup_name,
> -   completion_match *match);
> +   completion_match_result *comp_match_res);
>  
>  /* Some of the structures in this file are space critical.
>     The space-critical structures are:
> 

Keith

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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-06-02 12:30 ` [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching] Pedro Alves
@ 2017-08-08 23:48   ` Keith Seitz
  2017-11-22 16:48     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-08 23:48 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:

> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index 70c0e02..328b6db 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -15694,7 +15694,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
>  syntax to specify location parameters.\n\
>  Example: To specify the start of the label named \"the_top\" in the\n\
>  function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
> --function fact -label the_top\".\n"
> +-function fact -label the_top\".\n\
> +For C++, \"-function\" matches all functions with the given name ignoring\n\
                                                                   ^
comma

> +missing leading specifiers (namespaces and classes).  You can override\n\
> +that by instead specifying a fully qualified name using \"-qualified\".\n"

I think this would read better if it read: "This behavior may be overridden
by using the \"-qualified\" flag and specifying a fully qualified name."
[I am not a fan of using informal writing in documentation.]

>  /* This help string is used for the break, hbreak, tbreak and thbreak
>     commands.  It is defined as a macro to prevent duplication.
> diff --git a/gdb/completer.c b/gdb/completer.c
> index eabbce7..99e40a3 100644
> --- a/gdb/completer.c
> +++ b/gdb/completer.c
> @@ -609,6 +612,7 @@ static const char *const explicit_options[] =
>    {
>      "-source",
>      "-function",
> +    "-qualified",
>      "-line",
>      "-label",
>      NULL

The "-qualified" option can be used with linespecs, too, right?

(gdb) b -qualified A::b   (a linespec location)
(gdb) b -qualified -function A::b  (an explicit location)

Actually, I see that it does not work (yet?). Consider:

     1	struct A
     2	{
     3	  int doit ()
     4	  {
     5	    int i;
     6	
     7	    for (i = 0; i < 10; ++i)
     8	      {
     9	        switch (i)
    10		  {
    11		  top: 
    12	          case 5:
    13	            ++i;
    14	            goto top;
    15	          default:  break;
    16		  }
    17	      }
    18	    return i;
    19	  }
    20	};

(gdb) b A::doit:top
Breakpoint 1 at 0x400633: file simple-label.cc, line 11.
(gdb) b -function A::doit -label top
Note: breakpoint 1 also set at pc 0x400633.
Breakpoint 2 at 0x400633: file simple-label.cc, line 11.
(gdb) b -qualified A::doit:top
Function "A::doit:top" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n
(gdb) b -qualified A::doit -label top
Note: breakpoints 1 and 2 also set at pc 0x400633.
Breakpoint 3 at 0x400633: file simple-label.cc, line 11.

Is there a reason to exclude linespecs? Perhaps naively, I would have thought to
make -qualified a parsing option instead of a replacement for -function.

> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
> index 397c738..84d8a6b 100644
> --- a/gdb/cp-support.c
> +++ b/gdb/cp-support.c
> @@ -1671,19 +1760,151 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
>    return false;
>  }
>  
> +/* C++ symbol_name_matcher_ftype implementation for wild matches.
> +   Defers work to cp_symbol_name_ncmp.  */
                     ^^^^^^^^^^^^^^^^^^^

I think that's supposed to be cp_symbol_name_matches_1.

> +
> +static bool
> +cp_symbol_name_matches (const char *symbol_search_name,
> +			const lookup_name_info &lookup_name,
> +			completion_match_result *comp_match_res)
> +{
> +  const std::string &name = lookup_name.cplus ().lookup_name ();
> +
> +  strncmp_iw_mode mode = (lookup_name.completion_mode ()
> +			  ? strncmp_iw_mode::NORMAL
> +			  : strncmp_iw_mode::MATCH_PARAMS);
> +
> +  return cp_symbol_name_matches_1 (symbol_search_name,
> +				   name.c_str (), name.size (),
> +				   mode, comp_match_res);
> +}
> +
[snip]
> diff --git a/gdb/cp-support.h b/gdb/cp-support.h
> index 1cef3f7..600720f 100644
> --- a/gdb/cp-support.h
> +++ b/gdb/cp-support.h
> @@ -110,6 +110,8 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
>  extern struct type *cp_lookup_rtti_type (const char *name,
>  					 struct block *block);
>  
> +extern unsigned int cp_search_name_hash (const char *search_name);
> +

Shouldn't the comment from cp-support.c be here?

>  extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
>    (const lookup_name_info &lookup_name);
>  
[snip]

WDYT?

Keith

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

* Re: [PATCH 33/40] Make the linespec/location completer ignore data symbols
  2017-06-02 12:29 ` [PATCH 33/40] Make the linespec/location completer ignore data symbols Pedro Alves
@ 2017-08-09 15:42   ` Keith Seitz
  2017-11-08 16:22     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-09 15:42 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
>
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index bbc317d..52cf70a 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -5034,11 +5034,44 @@ completion_list_add_fields (completion_tracker &tracker,
>      }
>  }
>  
> +/* See symtab.h.  */
> +
> +bool
> +symbol_is_function_or_method (symbol *sym)
> +{
> +  switch (TYPE_CODE (SYMBOL_TYPE (sym)))
> +    {
> +    case TYPE_CODE_FUNC:
> +    case TYPE_CODE_METHOD:
> +      return true;
> +    default:
> +      return false;
> +    }
> +}
> +
> +/* Return whether MSYMBOL is a function/method.  */
> +

I think "See symtab.h." is more customary here?

> +bool
> +symbol_is_function_or_method (minimal_symbol *msymbol)
> +{
> +  switch (MSYMBOL_TYPE (msymbol))
> +    {
> +    case mst_text:
> +    case mst_text_gnu_ifunc:
> +    case mst_solib_trampoline:
> +    case mst_file_text:
> +      return true;
> +    default:
> +      return false;
> +    }
> +}
> +
>  /* Add matching symbols from SYMTAB to the current completion list.  */
>  
>  static void
>  add_symtab_completions (struct compunit_symtab *cust,
>  			completion_tracker &tracker,
> +			complete_symbol_mode mode,
>  			const lookup_name_info &lookup_name,
>  			const char *text, const char *word,
>  			enum type_code code)

Otherwise, LGTM.

Keith

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

* Re: [PATCH 34/40] Make strcmp_iw NOT ignore whitespace in the middle of tokens
  2017-06-02 12:23 ` [PATCH 34/40] Make strcmp_iw NOT ignore whitespace in the middle of tokens Pedro Alves
@ 2017-08-09 15:48   ` Keith Seitz
  2017-11-24 23:38     ` [pushed] " Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-09 15:48 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> currently "b func tion" manages to set a breakpoint at "function" !
> 
> All this years I had never noticed this, but now that the linespec
> completer actually works, this easily happens by accident, with:

That makes two of us!

> The operator_stoken changes are necessary due to a latent bug --
> currently "operator char" becomes "operatorchar", and later look ups
> only find it because strcmp_iw ignores the whitespace...

I have a similar fix on the compile branch. :-)

> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* c-exp.y (oper): Add space to operator names.
> 	* cp-support.c (cp_symbol_name_matches_1)
> 	(cp_fq_symbol_name_matches): Pass language to
> 	strncmp_iw_with_mode.
> 	(test_cp_symbol_name_cmp): Add unit tests.
> 	* language.c (default_symbol_name_matcher): Pass language to
> 	strncmp_iw_with_mode.
> 	* utils.c: Include "cp-support.h" and <algorithm>.
> 	(valid_identifier_name_char, cp_skip_operator_token, skip_ws)
> 	(cp_is_operator): New functions.
> 	(strncmp_iw_with_mode): Use them.  Add language parameter.  Don't
> 	skip whitespace in the symbol name when the lookup name doesn't
> 	have spaces, and vice versa.
> 	(strncmp_iw, strcmp_iw): Pass language to strncmp_iw_with_mode.
                                      ^^^^^^^^

Not just language, but language_minimal.

> 	* utils.h (strncmp_iw_with_mode): Add language parameter.

> diff --git a/gdb/c-exp.y b/gdb/c-exp.y
> index 24a2fbd..0a182cc 100644
> --- a/gdb/c-exp.y
> +++ b/gdb/c-exp.y
> @@ -1487,7 +1487,7 @@ oper:	OPERATOR NEW
>  	|	OPERATOR '>'
>  			{ $$ = operator_stoken (">"); }
>  	|	OPERATOR ASSIGN_MODIFY
> -			{ const char *op = "unknown";
> +			{ const char *op = " unknown";

Good catch. I missed that.

>  			  switch ($2)
>  			    {
>  			    case BINOP_RSH:
> @@ -1563,7 +1563,8 @@ oper:	OPERATOR NEW
>  
>  			  c_print_type ($2, NULL, &buf, -1, 0,
>  					&type_print_raw_options);
> -			  $$ = operator_stoken (buf.c_str ());
> +			  std::string name = " " + buf.string ();
> +			  $$ = operator_stoken (name.c_str ());
>  			}
>  	;
>  

The only additional change that I have in my compile branch is
that since this type's name could come from a user, it needs to be canonicalized.
But I can hit that when(ever?!) I start submitting some of the precursor
patches that I have. [I have a test that demonstrates the need for
canonicalization in the c++compile branch.]

> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
> index 84d8a6b..4c353c5 100644
> --- a/gdb/cp-support.c
> +++ b/gdb/cp-support.c
> @@ -1857,6 +1857,67 @@ test_cp_symbol_name_cmp ()
>    CHECK_MATCH_C ("function(int)", "function(int)");

[snip a WHOLE LOTTA TESTS]

AWESOME!

> diff --git a/gdb/utils.c b/gdb/utils.c
> index 9798edc..484c1ef 100644
> --- a/gdb/utils.c
> +++ b/gdb/utils.c
> @@ -65,6 +65,8 @@
>  #include "gdb_usleep.h"
>  #include "interps.h"
>  #include "gdb_regex.h"
> +#include "cp-support.h"
> +#include <algorithm>
>  
>  #if !HAVE_DECL_MALLOC
>  extern PTR malloc ();		/* ARI: PTR */
> @@ -2418,22 +2420,227 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
>      }
>  }
>  
> +/* True if CH is a character that can be part of a symbol name.  I.e.,
> +   either a number, a letter, or a '_'.  */
> +
> +static bool
> +valid_identifier_name_char (int ch)
> +{
> +  return (isalnum (ch) || ch == '_');
> +}

Couldn't this be language-dependent? [Yikes!]
Also note that there are a handful of places where this could be used
[follow-up patch?] in linespec.c, location.c, symtab.c. Maybe more.

We have logic for c++ operators all over the place. Note to self: this needs
to be cleaned up/consolidated.

> +
> +/* Skip to end of token, or to END, whatever comes first.  */
> +

I think a(n explicit) mention that the input is assumed to be an operator name. It's
mentioned in the name, but please consider repeating that in the comment. It's important.

> +static const char *
> +cp_skip_operator_token (const char *token, const char *end)
> +{
> +  const char *p = token;
> +  while (p != end && !isspace (*p) && *p != '(')
> +    {
> +      if (valid_identifier_name_char (*p))
> +	{
> +	  while (p != end && valid_identifier_name_char (*p))
> +	    p++;
> +	  return p;
> +	}
> +      else
> +	{
> +	  /* Note, ordered such that among ops that share a prefix,
> +	     longer comes first.  This is so that the loop below can
> +	     bail on first match.  */
> +	  static const char *ops[] =
> +	    {
> +	      "[",
> +	      "]",
> +	      "~",
> +	      ",",
> +	      "-=", "--", "->", "-",
> +	      "+=", "++", "+",
> +	      "*=", "*",
> +	      "/=", "/",
> +	      "%=", "%",
> +	      "|=", "||", "|",
> +	      "&=", "&&", "&",
> +	      "^=", "^",
> +	      "!=", "!",
> +	      "<<=", "<=", "<<", "<",
> +	      ">>=", ">=", ">>", ">",
> +	      "==", "=",
> +	    };
> +

Man, I'd *swear* that we have this code repeated in numerous places, but
we actually don't. [Not that I looked that hard...]

> +	  for (const char *op : ops)
> +	    {
> +	      size_t oplen = strlen (op);
> +	      size_t lencmp = std::min<size_t> (oplen, end - p);
> +
> +	      if (strncmp (p, op, lencmp) == 0)
> +		return p + lencmp;
> +	    }
> +	  /* Some unidentified character.  Return it.  */
> +	  return p + 1;
> +	}
> +    }
> +
> +  return p;
> +}
> +
> +/* Advance string1/string2 past whitespace.  */
> +
> +static void
> +skip_ws (const char *&string1, const char *&string2, const char *end_str2)
> +{
> +  while (isspace (*string1))
> +    string1++;
> +  while (string2 < end_str2 && isspace (*string2))
> +    string2++;
> +}
> +
> +static bool
> +cp_is_operator (const char *string, const char *start)

Missing comment?

> +{
> +  return ((string == start
> +	   || !valid_identifier_name_char (string[-1]))
> +	  && strncmp (string, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
> +	  && !valid_identifier_name_char (string[CP_OPERATOR_LEN]));
> +}
> +
>  /* See utils.h.  */
>  
>  int
>  strncmp_iw_with_mode (const char *string1, const char *string2,
> -		      size_t string2_len, strncmp_iw_mode mode)
> +		      size_t string2_len, strncmp_iw_mode mode,
> +		      enum language language)
>  {
> +  const char *string1_start = string1;
>    const char *end_str2 = string2 + string2_len;
> +  bool skip_spaces = true;
> +  bool have_colon_op = (language == language_cplus
> +			|| language == language_rust
> +			|| language == language_fortran);
>  

Just a passing comment: I'm kinda torn on this. When new languages are added,
this is going to be yet another place that language implementers are going to
have to modify. While a language method would probably be better (for some
definition of "better"), I don't want to see the language vector bloat beyond
control either. So IMO there's no clear better path.

>    while (1)
>      {
> -      while (isspace (*string1))
> -	string1++;
> -      while (string2 < end_str2 && isspace (*string2))
> -	string2++;
> +      if (skip_spaces
> +	  || ((isspace (*string1) && !valid_identifier_name_char (*string2))
> +	      || (isspace (*string2) && !valid_identifier_name_char (*string1))))
> +	{
> +	  skip_ws (string1, string2, end_str2);
> +	  skip_spaces = false;
> +	}
> +
>        if (*string1 == '\0' || string2 == end_str2)
>  	break;
> +
> +      /* Handle the :: operator.  */
> +      if (have_colon_op && string1[0] == ':' && string1[1] == ':')

At least this part is language-agnostic. That's a plus.

> +	{
> +	  if (*string2 != ':')
> +	    return 1;
> +
> +	  string1++;
> +	  string2++;
> +
> +	  if (string2 == end_str2)
> +	    break;
> +
> +	  if (*string2 != ':')
> +	    return 1;
> +
> +	  string1++;
> +	  string2++;
> +
> +	  while (isspace (*string1))
> +	    string1++;
> +	  while (string2 < end_str2 && isspace (*string2))
> +	    string2++;
> +	  continue;
> +	}
> +
> +      /* Handle C++ user-defined operators.  */
> +      else if (language == language_cplus
> +	       && *string1 == 'o')
> +	{
> +	  if (cp_is_operator (string1, string1_start))
> +	    {
> +	      /* An operator name in STRING1.  Check STRING2.  */
> +	      size_t cmplen = std::min<size_t> (CP_OPERATOR_LEN, end_str2 - string2);

line length == 85

> +	      if (strncmp (string1, string2, cmplen) != 0)
> +		return 1;
[snip]
> @@ -2462,7 +2675,7 @@ int
>  strncmp_iw (const char *string1, const char *string2, size_t string2_len)
>  {
>    return strncmp_iw_with_mode (string1, string2, string2_len,
> -			       strncmp_iw_mode::NORMAL);
> +			       strncmp_iw_mode::NORMAL, language_minimal);
>  }
>  
>  /* See utils.h.  */
> @@ -2471,7 +2684,7 @@ int
>  strcmp_iw (const char *string1, const char *string2)
>  {
>    return strncmp_iw_with_mode (string1, string2, strlen (string2),
> -			       strncmp_iw_mode::MATCH_PARAMS);
> +			       strncmp_iw_mode::MATCH_PARAMS, language_minimal);
>  }

I think the comments for both of these functions should be updated, since
they pass language_minimal to strncmp_iw_with_mode. Therefore,

  strncmp_iw_with_mode (string1, string2, len, MATCH_PARAMS, a_language)

may not necessarily equal

  strncmp_iw (string1, string2)

That may not be obvious to the casual user. Some sort of caveat seems prudent.

>  /* This is like strcmp except that it ignores whitespace and treats
> diff --git a/gdb/utils.h b/gdb/utils.h
> index 9e531e0..4ce263e 100644
> --- a/gdb/utils.h
> +++ b/gdb/utils.h
> @@ -56,7 +56,8 @@ enum class strncmp_iw_mode
>  extern int strncmp_iw_with_mode (const char *string1,
>  				 const char *string2,
>  				 size_t string2_len,
> -				 strncmp_iw_mode mode);
> +				 strncmp_iw_mode mode,
> +				 enum language language);

While most of these parameters are rather obvious usage, it is not obvious to
me why a strncmp-like function needs a language definition. [Of course,
I understand why after reading the code, but a brief mention of how LANGUAGE
affects the operation might be useful IMO. YMMV.]

>  
>  /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
>     differences in whitespace.  STRING2_LEN is STRING2's length.
> 

Keith

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

* Re: [PATCH 35/40] Comprehensive C++ linespec/completer tests
  2017-06-02 12:23 ` [PATCH 35/40] Comprehensive C++ linespec/completer tests Pedro Alves
@ 2017-08-09 17:30   ` Keith Seitz
  2017-11-24 16:25     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-09 17:30 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> Exercises all sorts of aspects fixed by the previous patches.

SUPER!

> Grows the gdb.linespec/ tests like this:
> 
>   -# of expected passes           573
>   +# of expected passes           4458

/me drools

> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.linespec/cpcompletion.exp: New file.
> 	* gdb.linespec/cpls-hyphen.cc: New file.
> 	* gdb.linespec/cpls.cc: New file.
> 	* gdb.linespec/cpls2.cc: New file.
> 	* gdb.linespec/explicit.exp: Load completion-support.exp.  Adjust
> 	test to use test_gdb_complete_unique.  Add label completion,
> 	keyword completion and explicit location completion tests.
> 	* lib/completion-support.exp: New file.

> diff --git a/gdb/testsuite/gdb.linespec/cpls-hyphen.cc b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
> new file mode 100644
> index 0000000..fdc063f
> --- /dev/null
> +++ b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
> @@ -0,0 +1,14 @@
> +int
> +ns_hyphen_function (int i)
> +{
> +  if (i > 0)
> +    {
> +    label1:
> +      return i + 20;
> +    }
> +  else
> +    {
> +    label2:
> +      return i + 10;
> +    }
> +}

Does this file not require a copyright header?

> diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp
> index 65d78ca..998b70a 100644
> --- a/gdb/testsuite/gdb.linespec/explicit.exp
> +++ b/gdb/testsuite/gdb.linespec/explicit.exp
> @@ -326,10 +329,202 @@ namespace eval $testfile {
[snip]
>  
> +	# Follows completion tests that require having no symbols
> +	# loaded.

"The following completion tests," perhaps?

> +	gdb_exit
> +	gdb_start
> +
> +	# The match list you get when you complete with no options
> +	# specified at all.
> +	set completion_list {
> +	    "-function"
> +	    "-label"
> +	    "-line"
> +	    "-probe"
> +	    "-probe-dtrace"
> +	    "-probe-stap"
> +	    "-qualified"
> +	    "-source"
> +	}
> +	with_test_prefix "complete with no arguments and no symbols" {
> +	    test_gdb_complete_multiple "b " "" "-" $completion_list
> +	    test_gdb_complete_multiple "b " "-" "" $completion_list
> +	}
>      }
>      # End of completion tests.
>  
> diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
> new file mode 100644
> index 0000000..ef78269
> --- /dev/null
> +++ b/gdb/testsuite/lib/completion-support.exp
> @@ -0,0 +1,513 @@
[snip]
> +
> +# Test that completing INPUT_LINE with TAB completes to
> +# COMPLETE_LINE_RE.  APPEND_CHAR_RE is the character expected to be
> +# appended after EXPECTED_OUTPUT.  Normally that's a whitespace, but
> +# in some cases it's some other character, like a colon.
> +
> +proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re } {
> +
> +    set test "tab complete \"$input_line\""
> +    send_gdb "$input_line\t"
> +    gdb_test_multiple "" "$test" {
> +	-re "^$complete_line_re$append_char_re$" {
> +	    pass "$test"
> +	}
> +    }
> +
> +    clear_input_line $test
> +}
> +
> +# Test that completing INPUT_LINE with TAB completes to "INPUT_LINE +
> +# ADD_COMPLETED_LINE" and that is displays the completion matches in
                                  ^^

s/is/it/

> +# COMPLETION_LIST.
> +

> +proc test_gdb_complete_tab_multiple { input_line add_completed_line \
> +					  completion_list } {
> +    global gdb_prompt
> +    global bell_re
[snip]
> +
> +proc test_gdb_complete_menu { line expected_output } {
> +

Is this used? Maybe in a subsequent patch?

> +    set test "menu complete $line"
> +#    send_gdb "$expr\033?"
> +#    send_gdb "$expr^\[?"
> +    send_gdb "$expr"
> +    send_gdb "\x1b"
> +    send_gdb "?"
> +    gdb_test_multiple "" "$test" {
> +	-re "$expected_output" {
> +	    pass "$test"
> +	}
> +    }
> +}
> +
[snip]
> +
> +proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {

This procedure is only called from this file. If it is meant to be called by
test writers, it deserves a comment, even if trivial.

If it is only meant for use here, then it should be named differently to
differentiate it, or stick it into an appropriately namespace so that it is
more obvious to potential users of this code that it is not meant to be
"exported." There's a lot of (sometimes mindless) cut-n-paste going on in the
test suite.

> +    set append_char_re [string_to_regexp $append_char]
> +    test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re
> +
> +    # Trim INPUT_LINE and COMPLETE LINE, for the case we're completing
> +    # a command with leading whitespace.  Leading command whitespace
> +    # is discarded by GDB.
> +    set input_line [string trimleft $input_line]
> +    set expected_output_re [string trimleft $complete_line_re]
> +    if {$append_char_re != " "} {
> +	append expected_output_re $append_char_re
> +    }
> +    if {$max_completions} {
> +	set max_completion_reached_msg \
> +	    "*** List may be truncated, max-completions reached. ***"
> +	set input_line_re \
> +	    [string_to_regexp $input_line]
> +	set max_completion_reached_msg_re \
> +	    [string_to_regexp $max_completion_reached_msg]
> +
> +	append expected_output_re \
> +	    "\r\n$input_line_re $max_completion_reached_msg_re"
> +    }
> +
> +    test_gdb_complete_cmd_unique $input_line $expected_output_re
> +}
[snip]
> +
> +# Test completing all the substring prefixes of COMPLETION from
> +# [0..START) to [0..END) complete to COMPLETION.  If END is ommitted,
> +# default to the length of COMPLETION.
> +
> +proc test_complete_prefix_range {completion start {end -1}} {
> +    if {$end == -1} {
> +	set end [string length $completion]
> +    }
> +
> +    for {set i $start} {$i < $end} {incr i} {
> +	set line [string range $completion 0 $i]
> +	test_gdb_complete_unique "$line" "$completion"
> +    }
> +}
> +
> +proc test_complete_prefix_range_input {input completion_re start {end -1}} {

This procedure also isn't used. Maybe also a later patch? Ditto the "if it
is meant to be used, ..., or renamed/hidden."

> +    if {$end == -1} {
> +	set end [string length $input]
> +    }
> +
> +    for {set i $start} {$i < $end} {incr i} {
> +	set line [string range $input 0 $i]
> +	test_gdb_complete_unique_re "$line" $completion_re
> +    }
> +}
> +
[snip]
> +# Return true if lists A and B have the same elements.  Order of
> +# elements does not matter.
> +
> +proc gdb_leq {a b} {
> +    return [expr {[lsort $a] eq [lsort $b]}]
> +}
> +
> +# Check that creating the breakpoint at LINESPEC finds the same
> +# breakpoint locations as completing LINESPEC.  COMPLETION_LIST is
> +# expected completion match list.

You mention "LINESPEC" here, but this procedure actually takes a breakpoint
command and a linepsec (or location?), no?

> +
> +proc check_bp_locations_match_list {break_command completion_list} {
> +    global gdb_prompt
> +    global hex
> +
> +    with_test_prefix "compare \"$break_command\" completion list with bp location list" {
> +	set num_locations [create_bp $break_command]
> +
> +	set found_list ""
> +
> +	set any "\[^\r\n\]*"
> +
> +	gdb_test_multiple "info breakpoint \$bpnum" "info breakpoint" {
> +	    -re "in \(\[^\r\n\]*\) at " {
> +		# A function location.
> +		set found_location "$expect_out(1,string)"
> +		lappend found_list $found_location
> +		exp_continue
> +	    }
> +	    -re "breakpoint${any}keep${any}y${any}$hex\[ \t]*\(${any}\)\r\n" {
> +		# A label location.
> +		set found_location "$expect_out(1,string)"
> +		lappend found_list $found_location
> +		exp_continue
> +	    }
> +	    -re "$gdb_prompt $" {
> +	    }
> +	}
> +
> +	gdb_assert {[gdb_leq $found_list $completion_list]} "matches"
> +
> +	delete_breakpoints
> +    }
> +}
> +
[snip]

Keith

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

* Re: [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests
  2017-06-02 12:23 ` [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests Pedro Alves
@ 2017-08-09 17:59   ` Keith Seitz
  2017-11-25  0:18     ` [pushed] " Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-09 17:59 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
> Grows the gdb.linespec/ tests like this:
> 
>  -# of expected passes           4458
>  +# of expected passes           8817

Again, this is awesome.

Just one little nit.

> diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.cc b/gdb/testsuite/gdb.linespec/cpls-ops.cc
> new file mode 100644
> index 0000000..c1156f1
> --- /dev/null
> +++ b/gdb/testsuite/gdb.linespec/cpls-ops.cc
> @@ -0,0 +1,253 @@
[snip]
> +
> +test_op_conversion::operator const volatile test_op_conversion_res **() const volatile

line length == 86

> +{
> +  return NULL;
> +}
> +
[snip]


Otherwise, LGTM.

Keith

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

* Re: [PATCH 37/40] Fix completing an empty string
  2017-06-02 12:23 ` [PATCH 37/40] Fix completing an empty string Pedro Alves
@ 2017-08-09 18:01   ` Keith Seitz
  2017-11-25  0:28     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-09 18:01 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd   Pedro Alves  <palves@redhat.com>
> 
> 	* completer.c (complete_line_internal_1): Skip spaces until the
> 	start of the command.
> 	* gdb.base/complete-empty.exp: New file.
> 	* gdb.base/completion.exp: Adjust.

Looks good!

Keith

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

* Re: [PATCH 38/40] Use TOLOWER in SYMBOL_HASH_NEXT
  2017-06-02 12:23 ` [PATCH 38/40] Use TOLOWER in SYMBOL_HASH_NEXT Pedro Alves
@ 2017-08-09 19:25   ` Keith Seitz
  2017-11-25  0:35     ` [pushed] " Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-09 19:25 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
>      * dictionary.c: Include "safe-ctype.h".
>      * minsyms.c: Include "safe-ctype.h".
>      * minsyms.c (SYMBOL_HASH_NEXT): Use TOLOWER instead of tolower.

Looks good!

Keith

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

* Re: [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436)
  2017-06-02 12:23 ` [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436) Pedro Alves
@ 2017-08-09 19:34   ` Keith Seitz
  2017-11-27 17:14     ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-08-09 19:34 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 06/02/2017 05:22 AM, Pedro Alves wrote:
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* completer.h (completion_match_for_lcd) <match,
> 	mark_ignored_range>: New methods.
> 	<finish>: Consider ignored ranges.
> 	<clear>: Clear ignored ranges.
> 	<m_ignored_ranges, m_finished_storage>: New fields.
> 	* cp-support.c (cp_search_name_hash): Ignore ABI tags.
> 	(cp_symbol_name_matches_1, cp_fq_symbol_name_matches): Pass the
> 	completion_match_for_lcd pointer to strncmp_iw_with_mode.
> 	(test_cp_symbol_name_cmp): Add [abi:...] tags unit tests.
> 	* language.c (default_symbol_name_matcher): Pass the
> 	completion_match_for_lcd pointer to strncmp_iw_with_mode.
> 	* linespec.c (linespec_lexer_lex_string): Don't tokenize ABI tags.
> 	* utils.c (skip_abi_tag): New function.
> 	(strncmp_iw_with_mode): Add completion_match_for_lcd parameter.
> 	Handle ABI tags.
> 	* utils.h (strncmp_iw_with_mode): Add completion_match_for_lcd
> 	parameter.
> 
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.linespec/cpls-abi-tag.cc: New file.
> 	* gdb.linespec/cpls-abi-tag.exp: New file.

Just one little thing: the PR# isn't mentioned in the ChangeLogs.
[This is also c++/18601, but I've marked that as a dup of this bug which is
discussed more.]

Keith

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

* Re: [PATCH 27/40] Make cp_remove_params return a unique_ptr
  2017-08-08 20:35   ` Keith Seitz
@ 2017-10-09 15:13     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-10-09 15:13 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/08/2017 09:35 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	(overload_list_add_symbol): Adjust to use gdb::unique_xmalloc_ptr.
>> 	* cp-support.h (cp_remove_params): Now returns a
>> 	gdb::unique_xmalloc_ptr.
>> 	* dwarf2read.c (find_slot_in_mapped_hash): Now returns bool.
>> 	Adjust to use gdb::unique_xmalloc_ptr.
>> 	(dw2_expand_symtabs_matching_symbol): Adjust to use
>> 	gdb::unique_xmalloc_ptr.
>> 	* psymtab.c (psymtab_search_name): Now returns a
>> 	gdb::unique_xmalloc_ptr.
>> 	(lookup_partial_symbol): Adjust to use gdb::unique_xmalloc_ptr.
>> 	* stack.c (find_frame_funname): Adjust to use
>> 	gdb::unique_xmalloc_ptr.
> 
> LGTM
> 

Thanks Keith.

I neglected applying this promptly, and meanwhile the cleanups
have been replaced in master via other patches.

The change to return unique_ptr is still useful on its own IMO,
so I've now pushed the remainder of the patch to master, as below.

From 109483d9eec3f0d0c3eaafd5d829435059167c52 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 9 Oct 2017 15:57:36 +0100
Subject: [PATCH] Make cp_remove_params return a gdb::unique_xmalloc_ptr

Use the type system instead of callers needing to know how the
returned string's memory is supposed to be managed.

gdb/ChangeLog:
2017-10-09  Pedro Alves  <palves@redhat.com>

	* cp-support.c (cp_remove_params): Return a gdb::unique_xmalloc_ptr.
	Use bool.
	(overload_list_add_symbol): Adjust to use gdb::unique_xmalloc_ptr.
	* cp-support.h (cp_remove_params): Now returns a
	gdb::unique_xmalloc_ptr.
	* dwarf2read.c (find_slot_in_mapped_hash): Now returns bool.
	Adjust to cp_remove_params returning a gdb::unique_xmalloc_ptr.
	* psymtab.c (psymtab_search_name): Adjust to cp_remove_params
	returning a gdb::unique_xmalloc_ptr.
	(lookup_partial_symbol): Adjust to use gdb::unique_xmalloc_ptr.
	* stack.c (find_frame_funname): Adjust to cp_remove_params
	returning a gdb::unique_xmalloc_ptr.
---
 gdb/ChangeLog    | 15 +++++++++++++++
 gdb/cp-support.c | 19 +++++++------------
 gdb/cp-support.h |  3 ++-
 gdb/dwarf2read.c | 11 ++++++-----
 gdb/psymtab.c    |  8 +++-----
 gdb/stack.c      |  7 ++-----
 6 files changed, 35 insertions(+), 28 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index a21f8a0..9bf5f6e 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,18 @@
+2017-10-09  Pedro Alves  <palves@redhat.com>
+
+	* cp-support.c (cp_remove_params): Return a gdb::unique_xmalloc_ptr.
+	Use bool.
+	(overload_list_add_symbol): Adjust to use gdb::unique_xmalloc_ptr.
+	* cp-support.h (cp_remove_params): Now returns a
+	gdb::unique_xmalloc_ptr.
+	* dwarf2read.c (find_slot_in_mapped_hash): Now returns bool.
+	Adjust to cp_remove_params returning a gdb::unique_xmalloc_ptr.
+	* psymtab.c (psymtab_search_name): Adjust to cp_remove_params
+	returning a gdb::unique_xmalloc_ptr.
+	(lookup_partial_symbol): Adjust to use gdb::unique_xmalloc_ptr.
+	* stack.c (find_frame_funname): Adjust to cp_remove_params
+	returning a gdb::unique_xmalloc_ptr.
+
 2017-10-08  Tom Tromey  <tom@tromey.com>
 
 	* dwarf2read.c (dwarf2_get_dwz_file): Use
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index d88bdaa..7bcb155 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -838,10 +838,10 @@ cp_func_name (const char *full_name)
    (optionally) a return type.  Return the name of the function without
    parameters or return type, or NULL if we can not parse the name.  */
 
-char *
+gdb::unique_xmalloc_ptr<char>
 cp_remove_params (const char *demangled_name)
 {
-  int done = 0;
+  bool done = false;
   struct demangle_component *ret_comp;
   std::unique_ptr<demangle_parse_info> info;
   gdb::unique_xmalloc_ptr<char> ret;
@@ -868,7 +868,7 @@ cp_remove_params (const char *demangled_name)
         ret_comp = d_left (ret_comp);
         break;
       default:
-	done = 1;
+	done = true;
 	break;
       }
 
@@ -876,7 +876,7 @@ cp_remove_params (const char *demangled_name)
   if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME)
     ret = cp_comp_to_string (d_left (ret_comp), 10);
 
-  return ret.release ();
+  return ret;
 }
 
 /* Here are some random pieces of trivia to keep in mind while trying
@@ -1103,7 +1103,7 @@ overload_list_add_symbol (struct symbol *sym,
 {
   int newsize;
   int i;
-  char *sym_name;
+  gdb::unique_xmalloc_ptr<char> sym_name;
 
   /* If there is no type information, we can't do anything, so
      skip.  */
@@ -1122,13 +1122,8 @@ overload_list_add_symbol (struct symbol *sym,
     return;
 
   /* skip symbols that cannot match */
-  if (strcmp (sym_name, oload_name) != 0)
-    {
-      xfree (sym_name);
-      return;
-    }
-
-  xfree (sym_name);
+  if (strcmp (sym_name.get (), oload_name) != 0)
+    return;
 
   /* We have a match for an overload instance, so add SYM to the
      current list of overload instances */
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 9210165..28353a2 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -95,7 +95,8 @@ extern unsigned int cp_entire_prefix_len (const char *name);
 
 extern char *cp_func_name (const char *full_name);
 
-extern char *cp_remove_params (const char *demangled_name);
+extern gdb::unique_xmalloc_ptr<char> cp_remove_params
+  (const char *demanged_name);
 
 extern struct symbol **make_symbol_overload_list (const char *,
 						  const char *);
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 3b90359..ca5b3a8 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -3194,9 +3194,10 @@ mapped_index_string_hash (int index_version, const void *p)
 
 /* Find a slot in the mapped index INDEX for the object named NAME.
    If NAME is found, set *VEC_OUT to point to the CU vector in the
-   constant pool and return 1.  If NAME cannot be found, return 0.  */
+   constant pool and return true.  If NAME cannot be found, return
+   false.  */
 
-static int
+static bool
 find_slot_in_mapped_hash (struct mapped_index *index, const char *name,
 			  offset_type **vec_out)
 {
@@ -3214,7 +3215,7 @@ find_slot_in_mapped_hash (struct mapped_index *index, const char *name,
 
       if (strchr (name, '(') != NULL)
 	{
-	  without_params.reset (cp_remove_params (name));
+	  without_params = cp_remove_params (name);
 
 	  if (without_params != NULL)
 	    name = without_params.get ();
@@ -3239,14 +3240,14 @@ find_slot_in_mapped_hash (struct mapped_index *index, const char *name,
       offset_type i = 2 * slot;
       const char *str;
       if (index->symbol_table[i] == 0 && index->symbol_table[i + 1] == 0)
-	return 0;
+	return false;
 
       str = index->constant_pool + MAYBE_SWAP (index->symbol_table[i]);
       if (!cmp (name, str))
 	{
 	  *vec_out = (offset_type *) (index->constant_pool
 				      + MAYBE_SWAP (index->symbol_table[i + 1]));
-	  return 1;
+	  return true;
 	}
 
       slot = (slot + step) & (index->symbol_table_slots - 1);
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 4527d69..f55c98c 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -623,9 +623,7 @@ match_partial_symbol (struct objfile *objfile,
    not contain any method/function instance information (since this would
    force reading type information while reading psymtabs).  Therefore,
    if NAME contains overload information, it must be stripped before searching
-   psymtabs.
-
-   The caller is responsible for freeing the return result.  */
+   psymtabs.  */
 
 static gdb::unique_xmalloc_ptr<char>
 psymtab_search_name (const char *name)
@@ -636,10 +634,10 @@ psymtab_search_name (const char *name)
       {
 	if (strchr (name, '('))
 	  {
-	    char *ret = cp_remove_params (name);
+	    gdb::unique_xmalloc_ptr<char> ret = cp_remove_params (name);
 
 	    if (ret)
-	      return gdb::unique_xmalloc_ptr<char> (ret);
+	      return ret;
 	  }
       }
       break;
diff --git a/gdb/stack.c b/gdb/stack.c
index 53dc829..4e40e32 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1101,10 +1101,7 @@ find_frame_funname (struct frame_info *frame, enum language *funlang,
 		 stored in the symbol table, but we stored a version
 		 with DMGL_PARAMS turned on, and here we don't want to
 		 display parameters.  So remove the parameters.  */
-	      char *func_only = cp_remove_params (print_name);
-
-	      if (func_only)
-		funname.reset (func_only);
+	      funname = cp_remove_params (print_name);
 	    }
 
 	  /* If we didn't hit the C++ case above, set *funname
@@ -1434,7 +1431,7 @@ info_frame_command (char *addr_exp, int from_tty)
 	     stored in the symbol table, but we stored a version
 	     with DMGL_PARAMS turned on, and here we don't want to
 	     display parameters.  So remove the parameters.  */
-	  func_only.reset (cp_remove_params (funname));
+	  func_only = cp_remove_params (funname);
 
 	  if (func_only)
 	    funname = func_only.get ();
-- 
2.5.5

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-08-08 20:29           ` Keith Seitz
@ 2017-10-19 17:36             ` Pedro Alves
  2017-11-01 15:38               ` Joel Brobecker
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-10-19 17:36 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches, Joel Brobecker

Joel,

This patch touches the Ada code a good deal.  It's main goal is
to generalize Ada's FULL/WILD name matching, so that C++ can do
something very similar.  Let me know if you'd like to review it.
I've been holding on moving forward with this
series because this is a central patch on top of which the
rest of the series sits on.  

For convenience, I've (force) pushed this patch,
along with the rest of the series to the
users/palves/cxx-breakpoint-improvements branch,
rebased on current master.

I've addressed Keith's comments below.  Updated version of
the patch also pasted at the bottom.

On 08/08/2017 09:29 PM, Keith Seitz wrote:
> On 07/20/2017 12:06 PM, Pedro Alves wrote:
>> On 07/20/2017 08:00 PM, Pedro Alves wrote:
>>
>>> I'll send the updated patch #25 as a reply.
>>
>> Here's the updated patch.  I've also force-pushed this to
>> the users/palves/cxx-breakpoint-improvements branch, meanwhile
>> rebased to current master.
> 
> Awesome. I've pulled your changes to retest, and I found no regressions
> this time. :-)
> 
>> 	* ada-lang.c (ada_encode): Rename to ..
>> 	(ada_encode_1): ... this.  Add throw_errors parameter and handle
>> 	it.
>> 	(ada_encode): Reimplement.
>> 	(match_name): Delete, folded into full_name.
>> 	(ada_lookup_simple_minsym): Use lookup_name_info and the
>> 	language's symbol_name_matcher_ftype.
>> 	(add_symbols_from_enclosing_procs, ada_add_local_symbols)
>> 	(ada_add_block_symbols, ada_add_block_symbols): Adjust to use
>> 	lookup_name_info.
> 
> ada_add_block_symbols is listed twice.

Whoops.  Thanks, fixed.

> 
>> 	* linespec.h (linespec_complete_function)
>> 	(linespec_complete_label): Add name_match_type parameter.
> 
> I don't actually see any linespec.h diffs...

Yeah, sorry about that.  I probably split those off to a
different  patch and didn't update here correctly.  There were
a few entries referring to linespec.c changes that were stale.

> 
>> 	* psymtab.c (psym_lookup_symbol): Use lookup_name_info.
>> 	(match_partial_symbol): Use symbol_name_match_type and
>> 	lookup_name_info.
> 
> Mention psymbol_name_matches (since you've mentioned it in other entries).

Fixed.

> 
>> 	* symfile-debug.c (debug_qf_map_matching_symbols)
>> 	(debug_qf_map_matching_symbols): Use symbol_name_match_type.
>> 	(debug_qf_expand_symtabs_matching): use lookup_name_info.
>                                             ^
> typo (lowercase letter)

Fixed.

> 
>> diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
>> index b6eb382..58d51d7 100644
>> --- a/gdb/ada-lang.c
>> +++ b/gdb/ada-lang.c
>> @@ -4935,23 +4928,19 @@ ada_lookup_simple_minsym (const char *name)
>>    struct bound_minimal_symbol result;
>>    struct objfile *objfile;
>>    struct minimal_symbol *msymbol;
>> -  const int wild_match_p = should_use_wild_match (name);
>>  
>>    memset (&result, 0, sizeof (result));
>>  
>> -  /* Special case: If the user specifies a symbol name inside package
>> -     Standard, do a non-wild matching of the symbol name without
>> -     the "standard__" prefix.  This was primarily introduced in order
>> -     to allow the user to specifically access the standard exceptions
>> -     using, for instance, Standard.Constraint_Error when Constraint_Error
>> -     is ambiguous (due to the user defining its own Constraint_Error
>> -     entity inside its program).  */
>> -  if (startswith (name, "standard__"))
>> -    name += sizeof ("standard__") - 1;
>> +  symbol_name_match_type match_type = name_match_type_from_name (name);
>> +  lookup_name_info lookup_name (name, match_type);
>> +
>> +  symbol_name_matcher_ftype *match_name
>> +    = language_get_symbol_name_matcher (language_def (language_ada),
>> +					lookup_name);
> 
> Okay, I understand that you did this to be consistent with generic code, but
> it seems odd to use all that machinery to access ada_get_symbol_name_mathcer,
> which is defined in this file. Just sayin' it kinda surprised me.

Yeah, I probably just copy/pasted and didn't even thing about it.
Fixed.

> 
>>  
>>    ALL_MSYMBOLS (objfile, msymbol)
>>    {
>> -    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), name, wild_match_p)
>> +    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), lookup_name, NULL)
>>          && MSYMBOL_TYPE (msymbol) != mst_solib_trampoline)
>>        {
>>  	result.minsym = msymbol;
>> @@ -5501,28 +5489,28 @@ aux_add_nonlocal_symbols (struct block *block, struct symbol *sym, void *data0)
>>    return 0;
>>  }
>>  
>> -/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are targetted
>> -   by renamings matching NAME in BLOCK.  Add these symbols to OBSTACKP.  If
>> -   WILD_MATCH_P is nonzero, perform the naming matching in "wild" mode (see
>> -   function "wild_match" for more information).  Return whether we found such
>> -   symbols.  */
>> +/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are
>> +   targetted by renamings matching LOOKUP_NAME in BLOCK.  Add these
> 
> *targeted

Fixed.

> 
>> +   symbols to OBSTACKP.  Return whether we found such symbols.  */
>>  
>>  static int
>>  ada_add_block_renamings (struct obstack *obstackp,
>>  			 const struct block *block,
>> -			 const char *name,
>> -			 domain_enum domain,
>> -			 int wild_match_p)
>> +			 const lookup_name_info &lookup_name,
>> +			 domain_enum domain)
>>  {
>>    struct using_direct *renaming;
>>    int defns_mark = num_defns_collected (obstackp);
>>  
>> +  symbol_name_matcher_ftype *name_match
>> +    = language_get_symbol_name_matcher (language_def (language_ada),
>> +					lookup_name);
>> +
> 
> [/me again slightly surprised]

Fixed.

> 
>>    for (renaming = block_using (block);
>>         renaming != NULL;
>>         renaming = renaming->next)
>>      {
>>        const char *r_name;
>> -      int name_match;
>>  
>>        /* Avoid infinite recursions: skip this renaming if we are actually
>>  	 already traversing it.
>>      }      	
>>  }
>>  
>> -/* Find symbols in DOMAIN matching NAME, in BLOCK and, if FULL_SEARCH is
>> -   non-zero, enclosing scope and in global scopes, returning the number of
>> -   matches.  Add these to OBSTACKP.
>> +/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if
>> +   FULL_SEARCH is non-zero, enclosing scope and in global scopes,
>> +   returning the number of matches.  Add these to OBSTACKP.
>>  
>>     When FULL_SEARCH is non-zero, any non-function/non-enumeral
>>     symbol match within the nest of blocks whose innermost member is BLOCK,
>> @@ -5776,17 +5780,17 @@ ada_add_all_symbols (struct obstack *obstackp,
>>  
>>    /* Search symbols from all global blocks.  */
>>   
>> -  add_nonlocal_symbols (obstackp, name, domain, 1, wild_match_p);
>> +  add_nonlocal_symbols (obstackp, lookup_name, domain, 1);
>>  
>>    /* Now add symbols from all per-file blocks if we've gotten no hits
>>       (not strictly correct, but perhaps better than an error).  */
>>  
>>    if (num_defns_collected (obstackp) == 0)
>> -    add_nonlocal_symbols (obstackp, name, domain, 0, wild_match_p);
>> +    add_nonlocal_symbols (obstackp, lookup_name, domain, 0);
>>  }
>>  
>> -/* Find symbols in DOMAIN matching NAME, in BLOCK and, if full_search is
>> -   non-zero, enclosing scope and in global scopes, returning the number of
>> +/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if full_search
>                                                                     ^^^^^^^^^^^
> FULL_SEARCH (as for ada_add_all_symbols)?
> 

Fixed.

>> +   is non-zero, enclosing scope and in global scopes, returning the number of
>>     matches.
>>     Sets *RESULTS to point to a vector of (SYM,BLOCK) tuples,
>>     indicating the symbols found and the blocks and symbol tables (if
>> @@ -13920,16 +13841,113 @@ static const struct exp_descriptor ada_exp_descriptor = {
> [snip]
>>  
>> -static symbol_name_cmp_ftype
>> -ada_get_symbol_name_cmp (const char *lookup_name)
> 
> The removal of this function is not mentioned in the ChangeLog.
> 

Fixed.


> 
> Wow, that was a lot of Ada. I hope that Joel can run this through Ada's
> internal tests before this goes live (or he gives his approval). While I've
> tried to follow along, I am desperately unversed in Ada.

[Added Joel]

> 
> 
>> diff --git a/gdb/completer.h b/gdb/completer.h
>> index f68c6dc..1958808 100644
>> --- a/gdb/completer.h
>> +++ b/gdb/completer.h
>> @@ -68,6 +68,62 @@ struct match_list_displayer
>>     calls free on each element.  */
>>  typedef std::vector<gdb::unique_xmalloc_ptr<char>> completion_list;
>>  
>> +/* The result of a successful completion match.  When doing symbol
>> +   comparison, we use the symbol search name for the symbol name match
>> +   check, but the matched name that is shown to the user may be
>> +   different.  For example, Ada uses encoded names for lookup, but
>> +   then wants to decode the symbol name to show to the user, and also
>> +   in some cases wrap the matched name in "<sym>" (meaning we can't
>> +   always use the symbol's print name.  */
>                                         ^
> 
> Missing closing parenthesis.

Fixed.

> 
>> +private:
>> +  /* The completion match result.  This can either be a pointer into
>> +     M_STORAGE string, or it can be a pointer into the some other
>> +     string that outlives the completion matching sequence (usually, a
>> +     pointer to a symbol's name.  */
>                                   ^
> Another missing close parenthesis.

Fixed.

> 
>> +  const char *m_match;
>> +
>> +  /* Storage a symbol comparison routine can use for generating a
>> +     match result, dynamically.  The built string is only good until
>> +     the next clear() call.  I.e., good until the next symbol
>> +     comparison.  */
>> +  std::string m_storage;
>> +};
>> +
>> +/* Convenience aggregate holding info returned by the symbol name
>> +   matching routines (see symbol_name_matcher_ftype).  */
> 
>> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
>> index df9a563..95e7cb8 100644
>> --- a/gdb/cp-support.c
>> +++ b/gdb/cp-support.c
>> @@ -1592,6 +1594,41 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
>>    return *demangled != NULL;
>>  }
>>  
> [snip]
>> +
>> +/* Implement the "la_get_symbol_name_matcher" language_defn method for
>> +   C++.  */
>> +
> 
> This comment should be moved to cp-support.h.

Fixed.

> 
>> +symbol_name_matcher_ftype *
>> +cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
>> +{
>> +  return cp_fq_symbol_name_matches;
>> +}
>> +
>>  /* Don't allow just "maintenance cplus".  */
>>  
>>  static  void
>> diff --git a/gdb/cp-support.h b/gdb/cp-support.h
>> index 37b281f..3a42cd6 100644
>> --- a/gdb/cp-support.h
>> +++ b/gdb/cp-support.h
>> @@ -107,6 +107,9 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
>>  extern struct type *cp_lookup_rtti_type (const char *name,
>>  					 struct block *block);
>>  
>> +extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
>> +  (const lookup_name_info &lookup_name);
>> +
> 
> [insert comment from cp-support.c]

Done.

> 
>> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
>> index 3c547c1..28236aa 100644
>> --- a/gdb/dwarf2read.c
>> +++ b/gdb/dwarf2read.c
>> @@ -4063,10 +4065,97 @@ dw2_map_matching_symbols (struct objfile *objfile,
> [snip]
>> +
>> +gdb_index_symbol_name_matcher::gdb_index_symbol_name_matcher
>> +  (const lookup_name_info &lookup_name)
>> +    : m_lookup_name (lookup_name)
> 
> While I don't object to this formatting, IIRC we are following GCC's lead,
> and their coding standard says,
> 
>   If a C++ function name is long enough to cause the first function parameter
>   with its type to exceed 80 characters, it should appear on the next line
>   indented four spaces.
> 
>   void
>   very_long_class_name::very_long_function_name (
>       very_long_type_name arg)
>   {
>   [https://gcc.gnu.org/codingconventions.html#Member_Form]

Yeah, that's a case of the written conventions not following actual
practice.  GCC doesn't really follow that advice.  IMNSHO, it's the
document that should be fixed.

> 
> Have I mentioned that I think we really need to create/publish our own
> coding standard (or at least a C++ cookbook)? ;-)

We actually have our coding standard published already.
It's in the internal's manual in the wiki.  Do you mean something
else?

> 
>> +{
>> +  /* Prepare the vector of comparison functions upfront, to avoid
>> +     doing the same work for each symbol.  Care is taken to avoid
>> +     matching with the same matcher more than once if/when multiple
>> +     languages use the same matcher function.  */
>> +  auto &matchers = m_symbol_name_matcher_funcs;
>> +  matchers.reserve (nr_languages);
>> +
>> +  for (int i = 0; i < nr_languages; i++)
>> +    {
>> +      const language_defn *lang = language_def ((enum language) i);
>> +      if (lang->la_get_symbol_name_matcher != NULL)
>> +	{
>> +	  symbol_name_matcher_ftype *name_matcher
>> +	    = lang->la_get_symbol_name_matcher (m_lookup_name);
>> +
>> +	  /* Don't insert the same comparison routine more than once.
>> +	     Note that we do this linear walk instead of a cheaper
>> +	     sorted insert, or use a std::set or something like that,
>> +	     because relative order of function addresses is not
>> +	     stable.  This is not a problem in practice because the
>> +	     number of supported languages is low, and the cost here
>> +	     is tiny compared to the number of searches we'll do
>> +	     afterwards using this object.  */
>> +	  if (std::find (matchers.begin (), matchers.end (), name_matcher)
>> +	      == matchers.end ())
>> +	    matchers.push_back (name_matcher);
>> +	}
>> +    }
>> +  if (std::find (matchers.begin (), matchers.end (),
>> +		 default_symbol_name_matcher) == matchers.end ())
>> +    matchers.push_back (default_symbol_name_matcher);
> 
> Is there a reason not to do this before looping since the loop already does
> duplicate elimination? [I realize the list of languages is tiny.]

I don't recall a reason.  I did that.

> 
>> +}
>> +
>> +bool
>> +gdb_index_symbol_name_matcher::matches (const char *symbol_name)
>> +{
>> +  for (auto matches_name : m_symbol_name_matcher_funcs)
>> +    if (matches_name (symbol_name, m_lookup_name, NULL))
>> +      return true;
>> +
>> +  return false;
>> +}
>> +
>>  static void
>>  dw2_expand_symtabs_matching
>>    (struct objfile *objfile,
>>     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
>> +   const lookup_name_info &lookup_name,
>>     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
>>     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
>>     enum search_domain kind)
>> diff --git a/gdb/linespec.c b/gdb/linespec.c
>> index 136cb65..a241419 100644
>> --- a/gdb/linespec.c
>> +++ b/gdb/linespec.c
>> @@ -3613,14 +3614,18 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
>>    struct symtab *elt;
>>    decode_compound_collector collector;
>>  
>> +  lookup_name_info lookup_name (class_name, symbol_name_match_type::FULL);
>> +
>>    for (ix = 0; VEC_iterate (symtab_ptr, file_symtabs, ix, elt); ++ix)
>>      {
>>        if (elt == NULL)
>>  	{
>> -	  iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN,
>> -					     NULL, false, collector);
>> -	  iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN,
>> -					     NULL, false, collector);
>> +	  iterate_over_all_matching_symtabs (state, lookup_name,
>> +					     STRUCT_DOMAIN, NULL, false,
>> +					     collector);
>> +	  iterate_over_all_matching_symtabs (state, lookup_name,
>> +					     VAR_DOMAIN, NULL, false,
>> +					     collector);
>>  	}
>>        else
>>  	{
> 
> Side note (for myself, really): I think we might be able to  avoid doing both
> STRUCT_DOMAIN and VAR_DOMAIN searches for several languages, including C++,
> because of the (vile) behavior of symbol_matches_domain. It might be a
> little optimization (which would need careful documenting if I should ever
> manage to delete symbol_matches_domain).
> 
>> diff --git a/gdb/minsyms.c b/gdb/minsyms.c
>> index c93eaa3..56fb5b4 100644
>> --- a/gdb/minsyms.c
>> +++ b/gdb/minsyms.c
> 
>> +/* Worker object for lookup_minimal_symbol.  Stores temporary results
>> +   while walking the symbol tables.  */
>> +
>> +struct found_minimal_symbols
>> +{
>> +  /* External symbols are best.  */
>> +  bound_minimal_symbol external_symbol {};
>> +
>> +  /* File-local symbols are next best.  */
>> +  bound_minimal_symbol file_symbol {};
>> +
>> +  /* Symbols for shared library trampolines are next best.  */
>> +  bound_minimal_symbol trampoline_symbol {};
>> +
>> +  /* Called when a symbol name matches.  Check if the minsym is a
>> +     better type than what we had already found, and record it in one
>> +     of the members fields if so.  Returns true if we already have the
>> +     real symbol.  */
>> +  bool maybe_collect (const char *sfile, objfile *objf,
>> +		      minimal_symbol *msymbol);
> 
> I apologize, but this is a nit; don't feel obligated to change anything.
> The return value to this function does not obviously map to its purpose.
> 
> Could the comment be amended/changed to explicitly mention that it means one
> should stop searching for better alternatives?

Sure thing.  Here's what I expanded it to:

  /* Called when a symbol name matches.  Check if the minsym is a
     better type than what we had already found, and record it in one
     of the members fields if so.  Returns true if we collected the
     real symbol, in which case we can stop searching.  */
  bool maybe_collect (const char *sfile, objfile *objf,
		      minimal_symbol *msymbol);

> 
>> +};
>> +
>> +bool
>> +found_minimal_symbols::maybe_collect (const char *sfile,
>> +                                     struct objfile *objfile,
>> +                                     minimal_symbol *msymbol)
> 
> Serious question: Do we need to put "See minsyms.h" here or are we going to
> start assuming that all member definitions are documented in the
> corresponding declaration? I wouldn't mind that simplification, but I thought
> I'd mention it.

Yeah, I wouldn't mind either.  Please do feel free to send a
proposal to the list.

For now I've added: 

/* See declaration above.  */


> 
>> diff --git a/gdb/psymtab.c b/gdb/psymtab.c
>> index 4077fb3..fb0a55d 100644
>> --- a/gdb/psymtab.c
>> +++ b/gdb/psymtab.c
>> @@ -542,6 +544,18 @@ psym_lookup_symbol (struct objfile *objfile,
>>    return stab_best;
>>  }
>>  
>> +/* Returns true if PSYM matches LOOKUP_NAME.  */
>> +
>> +static bool
>> +psymbol_name_matches (partial_symbol *psym,
>> +		      const lookup_name_info &lookup_name)
>> +{
>> +  const language_defn *lang = language_def (SYMBOL_LANGUAGE (psym));
>> +  symbol_name_matcher_ftype *name_match
>> +    = language_get_symbol_name_matcher (lang, lookup_name);
>> +  return name_match (SYMBOL_SEARCH_NAME (psym), lookup_name, NULL);
>> +}
>> +
> 
> This is called in tight loops while searching for matching psymbols.
> If there is some merit to the assumption (?!) that large amounts of psymbols
> within any given objfile are the same language, this seems ripe for some
> sort of trivial caching optimization. Perhaps, though, this is an
> "optimization" that will have no impact on performance. [Or maybe you've
> addressed this already in a future patch. Or I've missed a use case.]

Yeah, I had thought about it too, but never got to it.  I'll try to
give it a whirl, see if it makes a difference.

> 
>>  static bool
>>  recursively_search_psymtabs
>> -  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain kind,
>> +  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain domain,
>> +   const lookup_name_info &lookup_name,
>>     gdb::function_view<expand_symtabs_symbol_matcher_ftype> sym_matcher)
> 
> I am not bothered by renaming parameters for consistency, but it does, IMO,
> deserve mention in the ChangeLog when it is not necessitated by some other
> change, e.g., "name" -> "lookup_name".

Fixed.

> 
>>  {
>>    struct partial_symbol **psym;
>> @@ -1381,9 +1412,10 @@ static void
>>  psym_expand_symtabs_matching
>>    (struct objfile *objfile,
>>     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
>> +   const lookup_name_info &lookup_name,
>>     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
>>     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
>> -   enum search_domain kind)
>> +   enum search_domain domain)
>>  {
>>    struct partial_symtab *ps;
>>  
> 
> Likewise

Likewise fixed.

> 
>> diff --git a/gdb/symtab.c b/gdb/symtab.c
>> index ba2c559..6914f69 100644
>> --- a/gdb/symtab.c
>> +++ b/gdb/symtab.c
>> @@ -1751,6 +1766,30 @@ fixup_symbol_section (struct symbol *sym, struct objfile *objfile)
>>    return sym;
>>  }
>>  
>> +/* See symtab.h.  */
>> +
>> +demangle_for_lookup_info::demangle_for_lookup_info (const lookup_name_info &lookup_name,
>> +						    language lang)
> 
> line length == 88
> 

Fixed.

> [snip]
> 
>> @@ -4755,20 +4816,35 @@ compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
>>    return 1;
>>  }
>>  
>> -/*  Test to see if the symbol specified by SYMNAME (which is already
>> -   demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
>> -   characters.  If so, add it to the current completion list.  */
>> +/*  Test to see if the symbol of language SYMBOL_LANGUAGE specified by
>> +    SYMNAME (which is already demangled for C++ symbols) matches
>> +    SYM_TEXT in the first SYM_TEXT_LEN characters.  If so, add it to
>> +    the current completion list.  */
>>  
>> -static void
>> +void
>>  completion_list_add_name (completion_tracker &tracker,
> 
> This comment should have been moved to symtab.h along with the decl.

Fixed.

> 
>> +			  language symbol_language,
>>  			  const char *symname,
>> +			  const lookup_name_info &lookup_name,
>>  			  const char *sym_text, int sym_text_len,
>>  			  const char *text, const char *word)
>>  {
>> +  completion_match_result &match_res
>> +    = tracker.reset_completion_match_result ();
>> +
>>    /* Clip symbols that cannot match.  */
>> -  if (!compare_symbol_name (symname, sym_text, sym_text_len))
>> +  if (!compare_symbol_name (symname, symbol_language,
>> +			    lookup_name,
>> +			    sym_text, sym_text_len,
>> +			    match_res))
>>      return;
>>  
>> +  /* Refresh SYMNAME from the match string.  It's potentially
>> +     different depending on language.  (E.g., on Ada, the match may be
>> +     the encoded symbol name wrapped in "<>").  */
>> +  symname = match_res.match.match ();
>> +  gdb_assert (symname != NULL);
>> +
>>    /* We have a match for a completion, so add SYMNAME to the current list
>>       of matches.  Note that the name is moved to freshly malloc'd space.  */
>>  
>> +
> 
> I think you meant to clear the previous line (which is needlessly indented),
> no?

Sorry, I don't understand this comment.  :-/

> 
> [snip]
> 
>> diff --git a/gdb/symtab.h b/gdb/symtab.h
>> index da5cf25..fe7c1c4 100644
>> --- a/gdb/symtab.h
>> +++ b/gdb/symtab.h
>> @@ -43,6 +45,249 @@ struct probe;
> [snip]
>> +
>> +extern unsigned int search_name_hash (enum language language,
>> +				      const char *search_name);
>> +
> 
> symtab.c says, "See symtab.h." The above is missing that comment.

Eh, indeed.  I've added:

/* Hash the given symbol search name according to LANGUAGE's
   rules.  */
extern unsigned int search_name_hash (enum language language,
				      const char *search_name);

> 
>> +/* Ada-specific bits of a lookup_name_info object.  This is lazily
>> +   constructed on demand.  */
>> +
>> +class ada_lookup_name_info final
>> +{
>> + public:
>> +  /* Construct.  */
> 
> Just a side question... Are these types of comments particularly useful? I
> realize we (used to?) have a rather strict "document every function" policy, but
> (nearly) trivial ctors and (especially) dtors?

Maybe not.  It just felt like it'd demark the section for constructors,
as visual guidance.

> 
>> +  explicit ada_lookup_name_info (const lookup_name_info &lookup_name);
>> +
> 
>> +/* Object that aggregates all information relative to a symbol lookup
>                                              ^^^^^^^^
> I think you mean "relevant" here?

I meant it more as "related to", as in 

  "something having, or standing in, some relation or connection to
something else." 

  "existing or having its specific nature only by relation to something
else; not absolute or independent: " 

  "having relation or connection. "

from <http://www.dictionary.com/browse/relative>.

I've s/relative to/related to/ now.  Let me know whether that
sounds awkward.

> 
>> +   name.  I.e., the name that is matched against the symbol's search
>> +   name.  Caches per-language information so that it doesn't require
>> +   recomputing it for every symbol comparison, like for example the
>> +   Ada encoded name and the symbol's name hash for a given language.
>> +   The object is conceptually immutable once constructed, and thus has
>> +   no setters.  This is to avoid the case of a code path tweaking some
>> +   property of the lookup name for some local reason, and accidentally
>> +   causing other parts of symbol search that continue searching using
>> +   the same lookup name be affected.  lookup_name_info objects are
>> +   generally passed around as a const reference to reinforce that.
>> +   (They're not passed around by value because they're not small.)  */
> 
> May I suggest rewording that one sentence:
> 
>   This is to prevent some code path from tweaking some property of the
>   lookup name for some local reason and accidentally altering the results
>   of any continuing search(es).
> 
> Or something like that. The fragment "... using the same lookup name be
> affected" doesn't make sense (grammatically).

I think I intended there to be a "to" as in "causing it to be affected".
I think that'd make it grammatical, but yours is definitely better.  I've
used it.

> 
>> +class lookup_name_info final
>> +{
>> + public:
>> +  /* Create a new object.  */
>> +  lookup_name_info (std::string name,
>> +		    symbol_name_match_type match_type,
>> +		    bool completion_mode = false)
>> +    : m_match_type (match_type),
>> +      m_completion_mode (completion_mode),
>> +      m_name (std::move (name))
>> +  {}
>> +
>> +  /* Getters.  See description of each corresponding field.  */
>> +  symbol_name_match_type match_type () const { return m_match_type; }
>> +  bool completion_mode () const { return m_completion_mode; }
>> +  const std::string &name () const { return m_name; }
> 
> For the record, I don't have a problem with this style of commenting multiple
> simplistic methods. I hope we codify this (at least) for simple
> getter/setter-type methods.
> 
> [snip]
> 
>> @@ -1700,4 +1953,11 @@ void initialize_objfile_symbol (struct symbol *);
>>  
>>  struct template_symbol *allocate_template_symbol (struct objfile *);
>>  
>> +void completion_list_add_name (completion_tracker &tracker,
>> +			       language symbol_language,
>> +			       const char *symname,
>> +			       const lookup_name_info &lookup_name,
>> +			       const char *sym_text, int sym_text_len,
>> +			       const char *text, const char *word);
>> +
> 
> [comment from symtab.c goes here]

Fixed.

> 
>> diff --git a/gdb/utils.h b/gdb/utils.h
>> index 48330a1..c3d707c 100644
>> --- a/gdb/utils.h
>> +++ b/gdb/utils.h
>> @@ -31,6 +31,33 @@ extern void initialize_utils (void);
>>  
>>  extern int sevenbit_strings;
>>  
>> +/* Modes of operation for strncmp_iw_with_mode.  */
>> +
>> +enum class strncmp_iw_mode
>> +{
>> +/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
>> +   differences in whitespace.  Returns 0 if they match, non-zero if they
>> +   don't (slightly different than strcmp()'s range of return values).
>> +
>> +   As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
>> +   This "feature" is useful when searching for matching C++ function names
>> +   (such as if the user types 'break FOO', where FOO is a mangled C++
>> +   function).  */
>> +  NORMAL,
> 
> Isn't this described "hack" the same as the hack for MATCH_PARAMS:
> 
>> +
>> +  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
>> +     string1=="FOO(PARAMS)" matches string2=="FOO".  */
>> +  MATCH_PARAMS,
>> +};
> 
> Maybe it's a cut-n-paste-o?

Righto.  The whole second paragraph describing NORMAL shouldn't be
there.  That sentence was copied from strcmp_iw's description.
This now reads:

/* Modes of operation for strncmp_iw_with_mode.  */

enum class strncmp_iw_mode
{
/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
   differences in whitespace.  Returns 0 if they match, non-zero if
   they don't (slightly different than strcmp()'s range of return
   values).  */
  NORMAL,

  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
     string1=="FOO(PARAMS)" matches string2=="FOO".  */
  MATCH_PARAMS,
};

Thanks so much for the review, Keith.

Updated patch follows.

From e0fa43cf411a4b393f36233071b4e883b082a74a Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 19 Oct 2017 18:27:53 +0100
Subject: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD
 name matching

Summary:
 - This is preparation for supporting wild name matching on C++ too.
 - This is also preparation for TAB-completion fixes.
 - Makes symbol name matching (think strcmp_iw) be based on a per-language method.
 - Merges completion and non-completion name comparison (think
   language_ops::la_get_symbol_name_cmp generalized).
 - Avoid re-hashing lookup name multiple times
 - Centralizes preparing a name for lookup (Ada name encoding / C++ Demangling),
   both completion and non-completion.
 - Fixes Ada latent bug with verbatim name matches in expressions
 - Makes ada-lang.c use common|symtab.c completion code a bit more.

Ada's wild matching basically means that

 "(gdb) break foo"

will find all methods named "foo" in all packages.  Translating to
C++, it's roughly the same as saying that "break klass::method" sets
breakpoints on all "klass::method" methods of all classes, no matter
the namespace.  A following patch will teach GDB about fullname vs
wild matching for C++ too.  This patch is preparatory work to get
there.

Another idea here is to do symbol name matching based on the symbol
language's algorithm.  I.e., avoid dependency on current language set.

This allows for example doing

  (gdb) b foo::bar< int > (<tab>

and having gdb name match the C++ symbols correctly even if the
current language is C or Assembly (or Rust, or Ada, or ...), which can
easily happen if you step into an Assembly/C runtime library frame.

By encapsulating all the information related to a lookup name in a
class, we can also cache hash computation for a given language in the
lookup name object, to avoid recomputing it over and over.

Similarly, because we don't really know upfront which languages the
lookup name will be matched against, for each language we store the
lookup name transformed into a search name.  E.g., for C++, that means
demangling the name.  But for Ada, it means encoding the name.  This
actually forces us to centralize all the different lookup name
encoding in a central place, resulting in clearer code, IMO.  See
e.g., the new ada_lookup_name_info class.

The lookup name -> symbol search name computation is also done only
once per language.

The old language->la_get_symbol_name_cmp / symbol_name_cmp_ftype are
generalized to work with both completion, and normal symbol look up.

At some point early on, I had separate completion vs non-completion
language vector entry points, but a single method ends up being better
IMO for simplifying things -- the more we merge the completion /
non-completion name lookup code paths, the less changes for bugs
causing completion vs normal lookup finding different symbols.

The ada-lex.l change is necessary because when doing

  (gdb) p <UpperCase>

then the name that is passed to write_ write_var_or_type ->
ada_lookup_symbol_list misses the "<>", i.e., it's just "UpperCase",
and we end up doing a wild match against "UpperCase" lowercased by
ada_lookup_name_info's constructor.  I.e., "uppercase" wouldn't ever
match "UpperCase", and the symbol lookup fails.

This wouldn't cause any regression in the testsuite, but I added a new
test that would pass before the patch and fail after, if it weren't
for that fix.

This is latent bug that happens to go unnoticed because that
particular path was inconsistent with the rest of Ada symbol lookup by
not lowercasing the lookup name.

Ada's symbol_completion_add is deleted, replaced by using common
code's completion_list_add_name.  To make the latter work for Ada, we
needed to add a new output parameter, because Ada wants to return back
a custom completion candidates that are not the symbol name.

With this patch, minimal symbol demangled name hashing is made
consistent with regular symbol hashing.  I.e., it now goes via the
language vector's search_name_hash method too, as I had suggested in a
previous patch.

dw2_expand_symtabs_matching / .gdb_index symbol names were a
challenge.  The problem is that we have no way to telling what is the
language of each symbol name found in the index, until we expand the
corresponding full symbol, which is off course what we're trying to
avoid.  Language information is simply not considered in the index
format...  Since the symbol name hashing and comparison routines are
per-language, we now have a problem.  The patch sorts this out by
matching each name against all languages.  This is inneficient, and
indeed slows down completion several times.  E.g., with:

 $ cat script.cmd
 set pagination off
 set $count = 0
 while $count < 400
   complete b string_prin
   printf "count = %d\n", $count
   set $count = $count + 1
 end

 $ time gdb --batch -q ./gdb-with-index -ex "source script-string_printf.cmd"

I get, before patch (-O2, x86-64):

 real    0m1.773s
 user    0m1.737s
 sys     0m0.040s

While after patch (-O2, x86-64):

 real    0m9.843s
 user    0m9.482s
 sys     0m0.034s

However, the following patch will optimize this, and will actually
make this use case faster compared to the "before patch" above:

 real    0m1.321s
 user    0m1.285s
 sys     0m0.039s

gdb/ChangeLog:
yyyy-mm-dd   Pedro Alves  <palves@redhat.com>

	* ada-lang.c (ada_encode): Rename to ..
	(ada_encode_1): ... this.  Add throw_errors parameter and handle
	it.
	(ada_encode): Reimplement.
	(match_name): Delete, folded into full_name.
	(resolve_subexp): No longer pass the encoded name to
	ada_lookup_symbol_list.
	(should_use_wild_match): Delete.
	(name_match_type_from_name): New.
	(ada_lookup_simple_minsym): Use lookup_name_info and the
	language's symbol_name_matcher_ftype.
	(add_symbols_from_enclosing_procs, ada_add_local_symbols)
	(ada_add_block_renamings): Adjust to use lookup_name_info.
	(ada_lookup_name): New.
	(add_nonlocal_symbols, ada_add_all_symbols)
	(ada_lookup_symbol_list_worker, ada_lookup_symbol_list)
	(ada_iterate_over_symbols): Adjust to use lookup_name_info.
	(ada_name_for_lookup): Delete.
	(ada_lookup_encoded_symbol): Construct a verbatim name.
	(wild_match): Reverse sense of return type.  Use bool.
	(full_match): Reverse sense of return type.  Inline bits of old
	match_name here.
	(ada_add_block_symbols): Adjust to use lookup_name_info.
	(symbol_completion_match): Delete, folded into...
	(ada_lookup_name_info::matches): ... .this new method.
	(symbol_completion_add): Delete.
	(ada_collect_symbol_completion_matches): Add name_match_type
	parameter.  Adjust to use lookup_name_info and
	completion_list_add_name.
	(get_var_value, ada_add_global_exceptions): Adjust to use
	lookup_name_info.
	(ada_get_symbol_name_cmp): Delete.
	(do_wild_match, do_full_match): New functions.
	(ada_lookup_name_info::ada_lookup_name_info): New method.
	(ada_symbol_name_matches, ada_get_symbol_name_matcher): New
	functions.
	(ada_language_defn): Install ada_get_symbol_name_matcher.
	* ada-lex.l (processId): If name starts with '<', copy it
	verbatim.
	* block.c (block_iter_match_step, block_iter_match_first)
	(block_iter_match_next, block_lookup_symbol)
	(block_lookup_symbol_primary, block_find_symbol): Adjust to use
	lookup_name_info.
	* block.h (block_iter_match_first, block_iter_match_next)
	(ALL_BLOCK_SYMBOLS_WITH_NAME): Adjust to use lookup_name_info.
	* c-lang.c (c_language_defn, cplus_language_defn)
	(asm_language_defn, minimal_language_defn): Adjust comments to
	refer to la_get_symbol_name_matcher.
	* completer.c (complete_files_symbols)
	(collect_explicit_location_matches, symbol_completer): Pass a
	symbol_name_match_type down.
	* completer.h (class completion_match, completion_match_result):
	New classes.
	(completion_tracker::reset_completion_match_result): New method.
	(completion_tracker::m_completion_match_result): New field.
	* cp-support.c (make_symbol_overload_list_block): Adjust to use
	lookup_name_info.
	(cp_fq_symbol_name_matches, cp_get_symbol_name_matcher): New
	functions.
	* cp-support.h (cp_get_symbol_name_matcher): New declaration.
	* d-lang.c: Adjust comments to refer to
	la_get_symbol_name_matcher.
	* dictionary.c (dict_vector) <iter_match_first, iter_match_next>:
	Adjust to use lookup_name_info.
	(dict_iter_match_first, dict_iter_match_next)
	(iter_match_first_hashed, iter_match_next_hashed)
	(iter_match_first_linear, iter_match_next_linear): Adjust to work
	with a lookup_name_info.
	* dictionary.h (dict_iter_match_first, dict_iter_match_next):
	Likewise.
	* dwarf2read.c (dw2_lookup_symbol): Adjust to use lookup_name_info.
	(dw2_map_matching_symbols): Adjust to use symbol_name_match_type.
	(gdb_index_symbol_name_matcher): New class.
	(dw2_expand_symtabs_matching) Adjust to use lookup_name_info and
	gdb_index_symbol_name_matcher.  Accept a NULL symbol_matcher.
	* f-lang.c (f_collect_symbol_completion_matches): Adjust to work
	with a symbol_name_match_type.
	(f_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* go-lang.c (go_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* language.c (default_symbol_name_matcher)
	(language_get_symbol_name_matcher): New functions.
	(unknown_language_defn, auto_language_defn): Adjust comments to
	refer to la_get_symbol_name_matcher.
	* language.h (symbol_name_cmp_ftype): Delete.
	(language_defn) <la_collect_symbol_completion_matches>: Add match
	type parameter.
	<la_get_symbol_name_cmp>: Delete field.
	<la_get_symbol_name_matcher>: New field.
	<la_iterate_over_symbols>: Adjust to use lookup_name_info.
	(default_symbol_name_matcher, language_get_symbol_name_matcher):
	Declare.
	* linespec.c (iterate_over_all_matching_symtabs)
	(iterate_over_file_blocks): Adjust to use lookup_name_info.
	(find_methods): Add language parameter, and use lookup_name_info
	and the language's symbol_name_matcher_ftype.
	(linespec_complete_function): Adjust.
	(lookup_prefix_sym): Use lookup_name_info.
	(add_all_symbol_names_from_pspace): Adjust.
	(find_superclass_methods): Add language parameter and pass it
	down.
	(find_method): Pass symbol language down.
	(find_linespec_symbols): Don't demangle or Ada encode here.
	(search_minsyms_for_name): Add lookup_name_info parameter.
	(add_matching_symbols_to_info): Add name_match_type parameter.
	Use lookup_name_info.
	* m2-lang.c (m2_language_defn): Adjust comments to refer to
	la_get_symbol_name_matcher.
	* minsyms.c: Include <algorithm>.
	(add_minsym_to_demangled_hash_table): Remove table parameter and
	add objfile parameter.  Use search_name_hash, and add language to
	demangled languages vector.
	(struct found_minimal_symbols): New struct.
	(lookup_minimal_symbol_mangled, lookup_minimal_symbol_demangled):
	New functions.
	(lookup_minimal_symbol): Adjust to use them.  Don't canonicalize
	input names here.  Use lookup_name_info instead.  Lookup up
	demangled names once for each language in the demangled names
	vector.
	(iterate_over_minimal_symbols): Use lookup_name_info.  Lookup up
	demangled names once for each language in the demangled names
	vector.
	(build_minimal_symbol_hash_tables): Adjust.
	* minsyms.h (iterate_over_minimal_symbols): Adjust to pass down a
	lookup_name_info.
	* objc-lang.c (objc_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* objfiles.h: Include <vector>.
	(objfile_per_bfd_storage) <demangled_hash_languages>: New field.
	* opencl-lang.c (opencl_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* p-lang.c (pascal_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* psymtab.c (psym_lookup_symbol): Use lookup_name_info.
	(match_partial_symbol): Use symbol_name_match_type,
	lookup_name_info and psymbol_name_matches.
	(lookup_partial_symbol): Use lookup_name_info.
	(map_block): Use symbol_name_match_type and lookup_name_info.
	(psym_map_matching_symbols): Use symbol_name_match_type.
	(psymbol_name_matches): New.
	(recursively_search_psymtabs): Use lookup_name_info and
	psymbol_name_matches.  Rename 'kind' parameter to 'domain'.
	(psym_expand_symtabs_matching): Use lookup_name_info.  Rename
	'kind' parameter to 'domain'.
	* rust-lang.c (rust_language_defn): Adjust comment to refer to
	la_get_symbol_name_matcher.
	* symfile-debug.c (debug_qf_map_matching_symbols)
	(debug_qf_map_matching_symbols): Use symbol_name_match_type.
	(debug_qf_expand_symtabs_matching): Use lookup_name_info.
	* symfile.c (expand_symtabs_matching): Use lookup_name_info.
	* symfile.h (quick_symbol_functions) <map_matching_symbols>:
	Adjust to use symbol_name_match_type.
	<expand_symtabs_matching>: Adjust to use lookup_name_info.
	(expand_symtabs_matching): Adjust to use lookup_name_info.
	* symmisc.c (maintenance_expand_symtabs): Use
	lookup_name_info::match_any ().
	* symtab.c (symbol_matches_search_name): New.
	(eq_symbol_entry): Adjust to use lookup_name_info and the
	language's matcher.
	(demangle_for_lookup_info::demangle_for_lookup_info): New.
	(lookup_name_info::match_any): New.
	(iterate_over_symbols, search_symbols): Use lookup_name_info.
	(compare_symbol_name): Add language, lookup_name_info and
	completion_match_result parameters, and use them.
	(completion_list_add_name): Make extern.  Add language and
	lookup_name_info parameters.  Use them.
	(completion_list_add_symbol, completion_list_add_msymbol)
	(completion_list_objc_symbol): Add lookup_name_info parameters and
	adjust.  Pass down language.
	(completion_list_add_fields): Add lookup_name_info parameters and
	adjust.  Pass down language.
	(add_symtab_completions): Add lookup_name_info parameters and
	adjust.
	(default_collect_symbol_completion_matches_break_on): Add
	name_match_type parameter, and use it.  Use lookup_name_info.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches): Add name_match_type
	parameter, and pass it down.
	(collect_symbol_completion_matches_type): Adjust.
	(collect_file_symbol_completion_matches): Add name_match_type
	parameter, and use lookup_name_info.
	* symtab.h: Include <string> and "common/gdb_optional.h".
	(enum class symbol_name_match_type): New.
	(class ada_lookup_name_info): New.
	(struct demangle_for_lookup_info): New.
	(class lookup_name_info): New.
	(symbol_name_matcher_ftype): New.
	(SYMBOL_MATCHES_SEARCH_NAME): Use symbol_matches_search_name.
	(symbol_matches_search_name): Declare.
	(MSYMBOL_MATCHES_SEARCH_NAME): Delete.
	(default_collect_symbol_completion_matches)
	(collect_symbol_completion_matches)
	(collect_file_symbol_completion_matches): Add name_match_type
	parameter.
	(iterate_over_symbols): Use lookup_name_info.
	(completion_list_add_name): Declare.
	* utils.c (enum class strncmp_iw_mode): Moved to utils.h.
	(strncmp_iw_with_mode): Now extern.
	* utils.h (enum class strncmp_iw_mode): Moved from utils.c.
	(strncmp_iw_with_mode): Declare.

gdb/testsuite/ChangeLog:
yyyy-mm-dd   Pedro Alves  <palves@redhat.com>

	* gdb.ada/complete.exp (p <Exported_Capitalized>): New test.
	(p Exported_Capitalized): New test.
	(p exported_capitalized): New test.
---
 gdb/ada-lang.c                     | 728 +++++++++++++++++++------------------
 gdb/ada-lex.l                      |  22 +-
 gdb/block.c                        |  38 +-
 gdb/block.h                        |  33 +-
 gdb/c-lang.c                       |   8 +-
 gdb/completer.c                    |   4 +
 gdb/completer.h                    |  75 ++++
 gdb/cp-support.c                   |  38 +-
 gdb/cp-support.h                   |   5 +
 gdb/d-lang.c                       |   2 +-
 gdb/dictionary.c                   |  66 ++--
 gdb/dictionary.h                   |   6 +-
 gdb/dwarf2read.c                   |  99 ++++-
 gdb/f-lang.c                       |   4 +-
 gdb/go-lang.c                      |   2 +-
 gdb/language.c                     |  39 +-
 gdb/language.h                     |  43 ++-
 gdb/linespec.c                     | 132 ++++---
 gdb/m2-lang.c                      |   2 +-
 gdb/minsyms.c                      | 369 ++++++++++++-------
 gdb/minsyms.h                      |   2 +-
 gdb/objc-lang.c                    |   2 +-
 gdb/objfiles.h                     |   7 +
 gdb/opencl-lang.c                  |   2 +-
 gdb/p-lang.c                       |   2 +-
 gdb/psymtab.c                      |  83 +++--
 gdb/rust-lang.c                    |   2 +-
 gdb/symfile-debug.c                |   6 +-
 gdb/symfile.c                      |   2 +
 gdb/symfile.h                      |   4 +-
 gdb/symmisc.c                      |   1 +
 gdb/symtab.c                       | 198 +++++++---
 gdb/symtab.h                       | 300 ++++++++++++++-
 gdb/testsuite/gdb.ada/complete.exp |  10 +
 gdb/utils.c                        |  16 +-
 gdb/utils.h                        |  23 ++
 36 files changed, 1579 insertions(+), 796 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index a3d1207..4f0b9f4 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -104,16 +104,16 @@ static int ada_type_match (struct type *, struct type *, int);
 
 static int ada_args_match (struct symbol *, struct value **, int);
 
-static int full_match (const char *, const char *);
-
 static struct value *make_array_descriptor (struct type *, struct value *);
 
 static void ada_add_block_symbols (struct obstack *,
-                                   const struct block *, const char *,
-                                   domain_enum, struct objfile *, int);
+				   const struct block *,
+				   const lookup_name_info &lookup_name,
+				   domain_enum, struct objfile *);
 
 static void ada_add_all_symbols (struct obstack *, const struct block *,
-				 const char *, domain_enum, int, int *);
+				 const lookup_name_info &lookup_name,
+				 domain_enum, int, int *);
 
 static int is_nonfunction (struct block_symbol *, int);
 
@@ -203,7 +203,7 @@ static int is_name_suffix (const char *);
 
 static int advance_wild_match (const char **, const char *, int);
 
-static int wild_match (const char *, const char *);
+static bool wild_match (const char *name, const char *patn);
 
 static struct value *ada_coerce_ref (struct value *);
 
@@ -270,6 +270,10 @@ static void ada_forward_operator_length (struct expression *, int, int *,
 					 int *);
 
 static struct type *ada_find_any_type (const char *name);
+
+static symbol_name_matcher_ftype *ada_get_symbol_name_matcher
+  (const lookup_name_info &lookup_name);
+
 \f
 
 /* The result of a symbol lookup to be stored in our symbol cache.  */
@@ -976,11 +980,13 @@ const struct ada_opname_map ada_opname_table[] = {
   {NULL, NULL}
 };
 
-/* The "encoded" form of DECODED, according to GNAT conventions.
-   The result is valid until the next call to ada_encode.  */
+/* The "encoded" form of DECODED, according to GNAT conventions.  The
+   result is valid until the next call to ada_encode.  If
+   THROW_ERRORS, throw an error if invalid operator name is found.
+   Otherwise, return NULL in that case.  */
 
-char *
-ada_encode (const char *decoded)
+static char *
+ada_encode_1 (const char *decoded, bool throw_errors)
 {
   static char *encoding_buffer = NULL;
   static size_t encoding_buffer_size = 0;
@@ -1010,7 +1016,12 @@ ada_encode (const char *decoded)
                && !startswith (p, mapping->decoded); mapping += 1)
             ;
           if (mapping->encoded == NULL)
-            error (_("invalid Ada operator name: %s"), p);
+	    {
+	      if (throw_errors)
+		error (_("invalid Ada operator name: %s"), p);
+	      else
+		return NULL;
+	    }
           strcpy (encoding_buffer + k, mapping->encoded);
           k += strlen (mapping->encoded);
           break;
@@ -1026,6 +1037,15 @@ ada_encode (const char *decoded)
   return encoding_buffer;
 }
 
+/* The "encoded" form of DECODED, according to GNAT conventions.
+   The result is valid until the next call to ada_encode.  */
+
+char *
+ada_encode (const char *decoded)
+{
+  return ada_encode_1 (decoded, true);
+}
+
 /* Return NAME folded to lower case, or, if surrounded by single
    quotes, unfolded, but with the quotes stripped away.  Result good
    to next call.  */
@@ -1490,31 +1510,6 @@ ada_sniff_from_mangled_name (const char *mangled, char **out)
   return 0;
 }
 
-/* Returns non-zero iff SYM_NAME matches NAME, ignoring any trailing
-   suffixes that encode debugging information or leading _ada_ on
-   SYM_NAME (see is_name_suffix commentary for the debugging
-   information that is ignored).  If WILD, then NAME need only match a
-   suffix of SYM_NAME minus the same suffixes.  Also returns 0 if
-   either argument is NULL.  */
-
-static int
-match_name (const char *sym_name, const char *name, int wild)
-{
-  if (sym_name == NULL || name == NULL)
-    return 0;
-  else if (wild)
-    return wild_match (sym_name, name) == 0;
-  else
-    {
-      int len_name = strlen (name);
-
-      return (strncmp (sym_name, name, len_name) == 0
-              && is_name_suffix (sym_name + len_name))
-        || (startswith (sym_name, "_ada_")
-            && strncmp (sym_name + 5, name, len_name) == 0
-            && is_name_suffix (sym_name + len_name + 5));
-    }
-}
 \f
 
                                 /* Arrays */
@@ -3587,7 +3582,7 @@ resolve_subexp (struct expression **expp, int *pos, int deprocedure_p,
           int n_candidates;
 
           n_candidates =
-            ada_lookup_symbol_list (ada_encode (ada_decoded_op_name (op)),
+            ada_lookup_symbol_list (ada_decoded_op_name (op),
                                     (struct block *) NULL, VAR_DOMAIN,
                                     &candidates);
           i = ada_resolve_function (candidates, n_candidates, argvec, nargs,
@@ -4758,16 +4753,18 @@ cache_symbol (const char *name, domain_enum domain, struct symbol *sym,
 \f
                                 /* Symbol Lookup */
 
-/* Return nonzero if wild matching should be used when searching for
-   all symbols matching LOOKUP_NAME.
+/* Return the symbol name match type that should be used used when
+   searching for all symbols matching LOOKUP_NAME.
 
    LOOKUP_NAME is expected to be a symbol name after transformation
    for Ada lookups (see ada_name_for_lookup).  */
 
-static int
-should_use_wild_match (const char *lookup_name)
+static symbol_name_match_type
+name_match_type_from_name (const char *lookup_name)
 {
-  return (strstr (lookup_name, "__") == NULL);
+  return (strstr (lookup_name, "__") == NULL
+	  ? symbol_name_match_type::WILD
+	  : symbol_name_match_type::FULL);
 }
 
 /* Return the result of a standard (literal, C-like) lookup of NAME in
@@ -4937,23 +4934,18 @@ ada_lookup_simple_minsym (const char *name)
   struct bound_minimal_symbol result;
   struct objfile *objfile;
   struct minimal_symbol *msymbol;
-  const int wild_match_p = should_use_wild_match (name);
 
   memset (&result, 0, sizeof (result));
 
-  /* Special case: If the user specifies a symbol name inside package
-     Standard, do a non-wild matching of the symbol name without
-     the "standard__" prefix.  This was primarily introduced in order
-     to allow the user to specifically access the standard exceptions
-     using, for instance, Standard.Constraint_Error when Constraint_Error
-     is ambiguous (due to the user defining its own Constraint_Error
-     entity inside its program).  */
-  if (startswith (name, "standard__"))
-    name += sizeof ("standard__") - 1;
+  symbol_name_match_type match_type = name_match_type_from_name (name);
+  lookup_name_info lookup_name (name, match_type);
+
+  symbol_name_matcher_ftype *match_name
+    = ada_get_symbol_name_matcher (lookup_name);
 
   ALL_MSYMBOLS (objfile, msymbol)
   {
-    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), name, wild_match_p)
+    if (match_name (MSYMBOL_LINKAGE_NAME (msymbol), lookup_name, NULL)
         && MSYMBOL_TYPE (msymbol) != mst_solib_trampoline)
       {
 	result.minsym = msymbol;
@@ -4973,8 +4965,8 @@ ada_lookup_simple_minsym (const char *name)
 
 static void
 add_symbols_from_enclosing_procs (struct obstack *obstackp,
-                                  const char *name, domain_enum domain,
-                                  int wild_match_p)
+				  const lookup_name_info &lookup_name,
+				  domain_enum domain)
 {
 }
 
@@ -5426,17 +5418,16 @@ remove_irrelevant_renamings (struct block_symbol *syms,
    Note: This function assumes that OBSTACKP has 0 (zero) element in it.  */
 
 static void
-ada_add_local_symbols (struct obstack *obstackp, const char *name,
-                       const struct block *block, domain_enum domain,
-                       int wild_match_p)
+ada_add_local_symbols (struct obstack *obstackp,
+		       const lookup_name_info &lookup_name,
+		       const struct block *block, domain_enum domain)
 {
   int block_depth = 0;
 
   while (block != NULL)
     {
       block_depth += 1;
-      ada_add_block_symbols (obstackp, block, name, domain, NULL,
-			     wild_match_p);
+      ada_add_block_symbols (obstackp, block, lookup_name, domain, NULL);
 
       /* If we found a non-function match, assume that's the one.  */
       if (is_nonfunction (defns_collected (obstackp, 0),
@@ -5449,7 +5440,7 @@ ada_add_local_symbols (struct obstack *obstackp, const char *name,
   /* If no luck so far, try to find NAME as a local symbol in some lexically
      enclosing subprogram.  */
   if (num_defns_collected (obstackp) == 0 && block_depth > 2)
-    add_symbols_from_enclosing_procs (obstackp, name, domain, wild_match_p);
+    add_symbols_from_enclosing_procs (obstackp, lookup_name, domain);
 }
 
 /* An object of this type is used as the user_data argument when
@@ -5503,28 +5494,27 @@ aux_add_nonlocal_symbols (struct block *block, struct symbol *sym, void *data0)
   return 0;
 }
 
-/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are targetted
-   by renamings matching NAME in BLOCK.  Add these symbols to OBSTACKP.  If
-   WILD_MATCH_P is nonzero, perform the naming matching in "wild" mode (see
-   function "wild_match" for more information).  Return whether we found such
-   symbols.  */
+/* Helper for add_nonlocal_symbols.  Find symbols in DOMAIN which are
+   targeted by renamings matching LOOKUP_NAME in BLOCK.  Add these
+   symbols to OBSTACKP.  Return whether we found such symbols.  */
 
 static int
 ada_add_block_renamings (struct obstack *obstackp,
 			 const struct block *block,
-			 const char *name,
-			 domain_enum domain,
-			 int wild_match_p)
+			 const lookup_name_info &lookup_name,
+			 domain_enum domain)
 {
   struct using_direct *renaming;
   int defns_mark = num_defns_collected (obstackp);
 
+  symbol_name_matcher_ftype *name_match
+    = ada_get_symbol_name_matcher (lookup_name);
+
   for (renaming = block_using (block);
        renaming != NULL;
        renaming = renaming->next)
     {
       const char *r_name;
-      int name_match;
 
       /* Avoid infinite recursions: skip this renaming if we are actually
 	 already traversing it.
@@ -5549,11 +5539,13 @@ ada_add_block_renamings (struct obstack *obstackp,
       r_name = (renaming->alias != NULL
 		? renaming->alias
 		: renaming->declaration);
-      name_match
-	= wild_match_p ? wild_match (r_name, name) : strcmp (r_name, name);
-      if (name_match == 0)
-	ada_add_all_symbols (obstackp, block, renaming->declaration, domain,
-			     1, NULL);
+      if (name_match (r_name, lookup_name, NULL))
+	{
+	  lookup_name_info decl_lookup_name (renaming->declaration,
+					     lookup_name.match_type ());
+	  ada_add_all_symbols (obstackp, block, decl_lookup_name, domain,
+			       1, NULL);
+	}
       renaming->searched = 0;
     }
   return num_defns_collected (obstackp) != defns_mark;
@@ -5644,14 +5636,24 @@ compare_names (const char *string1, const char *string2)
   return result;
 }
 
+/* Convenience function to get at the Ada encoded lookup name for
+   LOOKUP_NAME, as a C string.  */
+
+static const char *
+ada_lookup_name (const lookup_name_info &lookup_name)
+{
+  return lookup_name.ada ().lookup_name ().c_str ();
+}
+
 /* Add to OBSTACKP all non-local symbols whose name and domain match
-   NAME and DOMAIN respectively.  The search is performed on GLOBAL_BLOCK
-   symbols if GLOBAL is non-zero, or on STATIC_BLOCK symbols otherwise.  */
+   LOOKUP_NAME and DOMAIN respectively.  The search is performed on
+   GLOBAL_BLOCK symbols if GLOBAL is non-zero, or on STATIC_BLOCK
+   symbols otherwise.  */
 
 static void
-add_nonlocal_symbols (struct obstack *obstackp, const char *name,
-		      domain_enum domain, int global,
-		      int is_wild_match)
+add_nonlocal_symbols (struct obstack *obstackp,
+		      const lookup_name_info &lookup_name,
+		      domain_enum domain, int global)
 {
   struct objfile *objfile;
   struct compunit_symtab *cu;
@@ -5660,50 +5662,57 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
   memset (&data, 0, sizeof data);
   data.obstackp = obstackp;
 
+  bool is_wild_match = lookup_name.ada ().wild_match_p ();
+
   ALL_OBJFILES (objfile)
     {
       data.objfile = objfile;
 
       if (is_wild_match)
-	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
+	objfile->sf->qf->map_matching_symbols (objfile, lookup_name.name ().c_str (),
+					       domain, global,
 					       aux_add_nonlocal_symbols, &data,
-					       wild_match, NULL);
+					       symbol_name_match_type::WILD,
+					       NULL);
       else
-	objfile->sf->qf->map_matching_symbols (objfile, name, domain, global,
+	objfile->sf->qf->map_matching_symbols (objfile, lookup_name.name ().c_str (),
+					       domain, global,
 					       aux_add_nonlocal_symbols, &data,
-					       full_match, compare_names);
+					       symbol_name_match_type::FULL,
+					       compare_names);
 
       ALL_OBJFILE_COMPUNITS (objfile, cu)
 	{
 	  const struct block *global_block
 	    = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cu), GLOBAL_BLOCK);
 
-	  if (ada_add_block_renamings (obstackp, global_block , name, domain,
-				       is_wild_match))
+	  if (ada_add_block_renamings (obstackp, global_block, lookup_name,
+				       domain))
 	    data.found_sym = 1;
 	}
     }
 
   if (num_defns_collected (obstackp) == 0 && global && !is_wild_match)
     {
+      const char *name = ada_lookup_name (lookup_name);
+      std::string name1 = std::string ("<_ada_") + name + '>';
+
       ALL_OBJFILES (objfile)
         {
-	  char *name1 = (char *) alloca (strlen (name) + sizeof ("_ada_"));
-	  strcpy (name1, "_ada_");
-	  strcpy (name1 + sizeof ("_ada_") - 1, name);
 	  data.objfile = objfile;
-	  objfile->sf->qf->map_matching_symbols (objfile, name1, domain,
-						 global,
+	  objfile->sf->qf->map_matching_symbols (objfile, name1.c_str (),
+						 domain, global,
 						 aux_add_nonlocal_symbols,
 						 &data,
-						 full_match, compare_names);
+						 symbol_name_match_type::FULL,
+						 compare_names);
 	}
     }      	
 }
 
-/* Find symbols in DOMAIN matching NAME, in BLOCK and, if FULL_SEARCH is
-   non-zero, enclosing scope and in global scopes, returning the number of
-   matches.  Add these to OBSTACKP.
+/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if
+   FULL_SEARCH is non-zero, enclosing scope and in global scopes,
+   returning the number of matches.  Add these to OBSTACKP.
 
    When FULL_SEARCH is non-zero, any non-function/non-enumeral
    symbol match within the nest of blocks whose innermost member is BLOCK,
@@ -5711,8 +5720,9 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
    enclosing blocks is returned).  If there are any matches in or
    surrounding BLOCK, then these alone are returned.
 
-   Names prefixed with "standard__" are handled specially: "standard__"
-   is first stripped off, and only static and global symbols are searched.
+   Names prefixed with "standard__" are handled specially:
+   "standard__" is first stripped off (by the lookup_name
+   constructor), and only static and global symbols are searched.
 
    If MADE_GLOBAL_LOOKUP_P is non-null, set it before return to whether we had
    to lookup global symbols.  */
@@ -5720,13 +5730,12 @@ add_nonlocal_symbols (struct obstack *obstackp, const char *name,
 static void
 ada_add_all_symbols (struct obstack *obstackp,
 		     const struct block *block,
-		     const char *name,
+		     const lookup_name_info &lookup_name,
 		     domain_enum domain,
 		     int full_search,
 		     int *made_global_lookup_p)
 {
   struct symbol *sym;
-  const int wild_match_p = should_use_wild_match (name);
 
   if (made_global_lookup_p)
     *made_global_lookup_p = 0;
@@ -5738,25 +5747,21 @@ ada_add_all_symbols (struct obstack *obstackp,
      using, for instance, Standard.Constraint_Error when Constraint_Error
      is ambiguous (due to the user defining its own Constraint_Error
      entity inside its program).  */
-  if (startswith (name, "standard__"))
-    {
-      block = NULL;
-      name = name + sizeof ("standard__") - 1;
-    }
+  if (lookup_name.ada ().standard_p ())
+    block = NULL;
 
   /* Check the non-global symbols.  If we have ANY match, then we're done.  */
 
   if (block != NULL)
     {
       if (full_search)
-	ada_add_local_symbols (obstackp, name, block, domain, wild_match_p);
+	ada_add_local_symbols (obstackp, lookup_name, block, domain);
       else
 	{
 	  /* In the !full_search case we're are being called by
 	     ada_iterate_over_symbols, and we don't want to search
 	     superblocks.  */
-	  ada_add_block_symbols (obstackp, block, name, domain, NULL,
-				 wild_match_p);
+	  ada_add_block_symbols (obstackp, block, lookup_name, domain, NULL);
 	}
       if (num_defns_collected (obstackp) > 0 || !full_search)
 	return;
@@ -5766,10 +5771,11 @@ ada_add_all_symbols (struct obstack *obstackp,
      already performed this search before.  If we have, then return
      the same result.  */
 
-  if (lookup_cached_symbol (name, domain, &sym, &block))
+  if (lookup_cached_symbol (ada_lookup_name (lookup_name),
+			    domain, &sym, &block))
     {
       if (sym != NULL)
-        add_defn_to_vec (obstackp, sym, block);
+	add_defn_to_vec (obstackp, sym, block);
       return;
     }
 
@@ -5778,17 +5784,17 @@ ada_add_all_symbols (struct obstack *obstackp,
 
   /* Search symbols from all global blocks.  */
  
-  add_nonlocal_symbols (obstackp, name, domain, 1, wild_match_p);
+  add_nonlocal_symbols (obstackp, lookup_name, domain, 1);
 
   /* Now add symbols from all per-file blocks if we've gotten no hits
      (not strictly correct, but perhaps better than an error).  */
 
   if (num_defns_collected (obstackp) == 0)
-    add_nonlocal_symbols (obstackp, name, domain, 0, wild_match_p);
+    add_nonlocal_symbols (obstackp, lookup_name, domain, 0);
 }
 
-/* Find symbols in DOMAIN matching NAME, in BLOCK and, if full_search is
-   non-zero, enclosing scope and in global scopes, returning the number of
+/* Find symbols in DOMAIN matching LOOKUP_NAME, in BLOCK and, if FULL_SEARCH
+   is non-zero, enclosing scope and in global scopes, returning the number of
    matches.
    Sets *RESULTS to point to a vector of (SYM,BLOCK) tuples,
    indicating the symbols found and the blocks and symbol tables (if
@@ -5805,19 +5811,19 @@ ada_add_all_symbols (struct obstack *obstackp,
    is first stripped off, and only static and global symbols are searched.  */
 
 static int
-ada_lookup_symbol_list_worker (const char *name, const struct block *block,
+ada_lookup_symbol_list_worker (const lookup_name_info &lookup_name,
+			       const struct block *block,
 			       domain_enum domain,
 			       struct block_symbol **results,
 			       int full_search)
 {
-  const int wild_match_p = should_use_wild_match (name);
   int syms_from_global_search;
   int ndefns;
 
   obstack_free (&symbol_list_obstack, NULL);
   obstack_init (&symbol_list_obstack);
-  ada_add_all_symbols (&symbol_list_obstack, block, name, domain,
-		       full_search, &syms_from_global_search);
+  ada_add_all_symbols (&symbol_list_obstack, block, lookup_name,
+		       domain, full_search, &syms_from_global_search);
 
   ndefns = num_defns_collected (&symbol_list_obstack);
   *results = defns_collected (&symbol_list_obstack, 1);
@@ -5825,32 +5831,37 @@ ada_lookup_symbol_list_worker (const char *name, const struct block *block,
   ndefns = remove_extra_symbols (*results, ndefns);
 
   if (ndefns == 0 && full_search && syms_from_global_search)
-    cache_symbol (name, domain, NULL, NULL);
+    cache_symbol (ada_lookup_name (lookup_name), domain, NULL, NULL);
 
   if (ndefns == 1 && full_search && syms_from_global_search)
-    cache_symbol (name, domain, (*results)[0].symbol, (*results)[0].block);
+    cache_symbol (ada_lookup_name (lookup_name), domain,
+		  (*results)[0].symbol, (*results)[0].block);
 
   ndefns = remove_irrelevant_renamings (*results, ndefns, block);
   return ndefns;
 }
 
-/* Find symbols in DOMAIN matching NAME0, in BLOCK0 and enclosing scope and
+/* Find symbols in DOMAIN matching NAME, in BLOCK and enclosing scope and
    in global scopes, returning the number of matches, and setting *RESULTS
    to a vector of (SYM,BLOCK) tuples.
    See ada_lookup_symbol_list_worker for further details.  */
 
 int
-ada_lookup_symbol_list (const char *name0, const struct block *block0,
+ada_lookup_symbol_list (const char *name, const struct block *block,
 			domain_enum domain, struct block_symbol **results)
 {
-  return ada_lookup_symbol_list_worker (name0, block0, domain, results, 1);
+  symbol_name_match_type name_match_type = name_match_type_from_name (name);
+  lookup_name_info lookup_name (name, name_match_type);
+
+  return ada_lookup_symbol_list_worker (lookup_name, block, domain, results, 1);
 }
 
 /* Implementation of the la_iterate_over_symbols method.  */
 
 static void
 ada_iterate_over_symbols
-  (const struct block *block, const char *name, domain_enum domain,
+  (const struct block *block, const lookup_name_info &name,
+   domain_enum domain,
    gdb::function_view<symbol_found_callback_ftype> callback)
 {
   int ndefs, i;
@@ -5864,24 +5875,6 @@ ada_iterate_over_symbols
     }
 }
 
-/* If NAME is the name of an entity, return a string that should
-   be used to look that entity up in Ada units.
-
-   NAME can have any form that the "break" or "print" commands might
-   recognize.  In other words, it does not have to be the "natural"
-   name, or the "encoded" name.  */
-
-std::string
-ada_name_for_lookup (const char *name)
-{
-  int nlen = strlen (name);
-
-  if (name[0] == '<' && name[nlen - 1] == '>')
-    return std::string (name + 1, nlen - 2);
-  else
-    return ada_encode (ada_fold_name (name));
-}
-
 /* The result is as for ada_lookup_symbol_list with FULL_SEARCH set
    to 1, but choosing the first symbol found if there are multiple
    choices.
@@ -5897,10 +5890,19 @@ ada_lookup_encoded_symbol (const char *name, const struct block *block,
   struct block_symbol *candidates;
   int n_candidates;
 
+  /* Since we already have an encoded name, wrap it in '<>' to force a
+     verbatim match.  Otherwise, if the name happens to not look like
+     an encoded name (because it doesn't include a "__"),
+     ada_lookup_name_info would re-encode/fold it again, and that
+     would e.g., incorrectly lowercase object renaming names like
+     "R28b" -> "r28b".  */
+  std::string verbatim = std::string ("<") + name + '>';
+
   gdb_assert (info != NULL);
   memset (info, 0, sizeof (struct block_symbol));
 
-  n_candidates = ada_lookup_symbol_list (name, block, domain, &candidates);
+  n_candidates = ada_lookup_symbol_list (verbatim.c_str (), block,
+					 domain, &candidates);
   if (n_candidates == 0)
     return;
 
@@ -6181,11 +6183,12 @@ advance_wild_match (const char **namep, const char *name0, int target0)
   return 1;
 }
 
-/* Return 0 iff NAME encodes a name of the form prefix.PATN.  Ignores any
-   informational suffixes of NAME (i.e., for which is_name_suffix is
-   true).  Assumes that PATN is a lower-cased Ada simple name.  */
+/* Return true iff NAME encodes a name of the form prefix.PATN.
+   Ignores any informational suffixes of NAME (i.e., for which
+   is_name_suffix is true).  Assumes that PATN is a lower-cased Ada
+   simple name.  */
 
-static int
+static bool
 wild_match (const char *name, const char *patn)
 {
   const char *p;
@@ -6201,39 +6204,49 @@ wild_match (const char *name, const char *patn)
 	    if (*p != *name)
 	      break;
 	  if (*p == '\0' && is_name_suffix (name))
-	    return match != name0 && !is_valid_name_for_wild_match (name0);
+	    return match == name0 || is_valid_name_for_wild_match (name0);
 
 	  if (name[-1] == '_')
 	    name -= 1;
 	}
       if (!advance_wild_match (&name, name0, *patn))
-	return 1;
+	return false;
     }
 }
 
-/* Returns 0 iff symbol name SYM_NAME matches SEARCH_NAME, apart from
-   informational suffix.  */
+/* Returns true iff symbol name SYM_NAME matches SEARCH_NAME, ignoring
+   any trailing suffixes that encode debugging information or leading
+   _ada_ on SYM_NAME (see is_name_suffix commentary for the debugging
+   information that is ignored).  */
 
-static int
+static bool
 full_match (const char *sym_name, const char *search_name)
 {
-  return !match_name (sym_name, search_name, 0);
-}
+  size_t search_name_len = strlen (search_name);
+
+  if (strncmp (sym_name, search_name, search_name_len) == 0
+      && is_name_suffix (sym_name + search_name_len))
+    return true;
+
+  if (startswith (sym_name, "_ada_")
+      && strncmp (sym_name + 5, search_name, search_name_len) == 0
+      && is_name_suffix (sym_name + search_name_len + 5))
+    return true;
 
+  return false;
+}
 
-/* Add symbols from BLOCK matching identifier NAME in DOMAIN to
-   vector *defn_symbols, updating the list of symbols in OBSTACKP 
-   (if necessary).  If WILD, treat as NAME with a wildcard prefix.
-   OBJFILE is the section containing BLOCK.  */
+/* Add symbols from BLOCK matching LOOKUP_NAME in DOMAIN to vector
+   *defn_symbols, updating the list of symbols in OBSTACKP (if
+   necessary).  OBJFILE is the section containing BLOCK.  */
 
 static void
 ada_add_block_symbols (struct obstack *obstackp,
-                       const struct block *block, const char *name,
-                       domain_enum domain, struct objfile *objfile,
-                       int wild)
+		       const struct block *block,
+		       const lookup_name_info &lookup_name,
+		       domain_enum domain, struct objfile *objfile)
 {
   struct block_iterator iter;
-  int name_len = strlen (name);
   /* A matching argument symbol, if any.  */
   struct symbol *arg_sym;
   /* Set true when we find a matching non-argument symbol.  */
@@ -6242,56 +6255,31 @@ ada_add_block_symbols (struct obstack *obstackp,
 
   arg_sym = NULL;
   found_sym = 0;
-  if (wild)
+  for (sym = block_iter_match_first (block, lookup_name, &iter);
+       sym != NULL;
+       sym = block_iter_match_next (lookup_name, &iter))
     {
-      for (sym = block_iter_match_first (block, name, wild_match, &iter);
-	   sym != NULL; sym = block_iter_match_next (name, wild_match, &iter))
-      {
-        if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                   SYMBOL_DOMAIN (sym), domain)
-            && wild_match (SYMBOL_LINKAGE_NAME (sym), name) == 0)
-          {
-	    if (SYMBOL_CLASS (sym) == LOC_UNRESOLVED)
-	      continue;
-	    else if (SYMBOL_IS_ARGUMENT (sym))
-	      arg_sym = sym;
-	    else
-	      {
-                found_sym = 1;
-                add_defn_to_vec (obstackp,
-                                 fixup_symbol_section (sym, objfile),
-                                 block);
-              }
-          }
-      }
-    }
-  else
-    {
-     for (sym = block_iter_match_first (block, name, full_match, &iter);
-	  sym != NULL; sym = block_iter_match_next (name, full_match, &iter))
-      {
-        if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                   SYMBOL_DOMAIN (sym), domain))
-          {
-	    if (SYMBOL_CLASS (sym) != LOC_UNRESOLVED)
-	      {
-		if (SYMBOL_IS_ARGUMENT (sym))
-		  arg_sym = sym;
-		else
-		  {
-		    found_sym = 1;
-		    add_defn_to_vec (obstackp,
-				     fixup_symbol_section (sym, objfile),
-				     block);
-		  }
-	      }
-          }
-      }
+      if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
+				 SYMBOL_DOMAIN (sym), domain))
+	{
+	  if (SYMBOL_CLASS (sym) != LOC_UNRESOLVED)
+	    {
+	      if (SYMBOL_IS_ARGUMENT (sym))
+		arg_sym = sym;
+	      else
+		{
+		  found_sym = 1;
+		  add_defn_to_vec (obstackp,
+				   fixup_symbol_section (sym, objfile),
+				   block);
+		}
+	    }
+	}
     }
 
   /* Handle renamings.  */
 
-  if (ada_add_block_renamings (obstackp, block, name, domain, wild))
+  if (ada_add_block_renamings (obstackp, block, lookup_name, domain))
     found_sym = 1;
 
   if (!found_sym && arg_sym != NULL)
@@ -6301,10 +6289,13 @@ ada_add_block_symbols (struct obstack *obstackp,
                        block);
     }
 
-  if (!wild)
+  if (!lookup_name.ada ().wild_match_p ())
     {
       arg_sym = NULL;
       found_sym = 0;
+      const std::string &ada_lookup_name = lookup_name.ada ().lookup_name ();
+      const char *name = ada_lookup_name.c_str ();
+      size_t name_len = ada_lookup_name.size ();
 
       ALL_BLOCK_SYMBOLS (block, iter, sym)
       {
@@ -6355,51 +6346,39 @@ ada_add_block_symbols (struct obstack *obstackp,
 
                                 /* Symbol Completion */
 
-/* If SYM_NAME is a completion candidate for TEXT, return this symbol
-   name in a form that's appropriate for the completion.  The result
-   does not need to be deallocated, but is only good until the next call.
-
-   TEXT_LEN is equal to the length of TEXT.
-   Perform a wild match if WILD_MATCH_P is set.
-   ENCODED_P should be set if TEXT represents the start of a symbol name
-   in its encoded form.  */
+/* See symtab.h.  */
 
-static const char *
-symbol_completion_match (const char *sym_name,
-                         const char *text, int text_len,
-                         int wild_match_p, int encoded_p)
+bool
+ada_lookup_name_info::matches
+  (const char *sym_name,
+   symbol_name_match_type match_type,
+   completion_match *comp_match) const
 {
-  const int verbatim_match = (text[0] == '<');
-  int match = 0;
-
-  if (verbatim_match)
-    {
-      /* Strip the leading angle bracket.  */
-      text = text + 1;
-      text_len--;
-    }
+  bool match = false;
+  const char *text = m_encoded_name.c_str ();
+  size_t text_len = m_encoded_name.size ();
 
   /* First, test against the fully qualified name of the symbol.  */
 
   if (strncmp (sym_name, text, text_len) == 0)
-    match = 1;
+    match = true;
 
-  if (match && !encoded_p)
+  if (match && !m_encoded_p)
     {
       /* One needed check before declaring a positive match is to verify
          that iff we are doing a verbatim match, the decoded version
          of the symbol name starts with '<'.  Otherwise, this symbol name
          is not a suitable completion.  */
       const char *sym_name_copy = sym_name;
-      int has_angle_bracket;
+      bool has_angle_bracket;
 
       sym_name = ada_decode (sym_name);
       has_angle_bracket = (sym_name[0] == '<');
-      match = (has_angle_bracket == verbatim_match);
+      match = (has_angle_bracket == m_verbatim_p);
       sym_name = sym_name_copy;
     }
 
-  if (match && !verbatim_match)
+  if (match && !m_verbatim_p)
     {
       /* When doing non-verbatim match, another check that needs to
          be done is to verify that the potentially matching symbol name
@@ -6410,12 +6389,12 @@ symbol_completion_match (const char *sym_name,
 
       for (tmp = sym_name; *tmp != '\0' && !isupper (*tmp); tmp++);
       if (*tmp != '\0')
-        match = 0;
+	match = false;
     }
 
   /* Second: Try wild matching...  */
 
-  if (!match && wild_match_p)
+  if (!match && m_wild_match_p)
     {
       /* Since we are doing wild matching, this means that TEXT
          may represent an unqualified symbol name.  We therefore must
@@ -6423,91 +6402,48 @@ symbol_completion_match (const char *sym_name,
       sym_name = ada_unqualified_name (ada_decode (sym_name));
 
       if (strncmp (sym_name, text, text_len) == 0)
-        match = 1;
+	match = true;
     }
 
-  /* Finally: If we found a mach, prepare the result to return.  */
+  /* Finally: If we found a match, prepare the result to return.  */
 
   if (!match)
-    return NULL;
-
-  if (verbatim_match)
-    sym_name = add_angle_brackets (sym_name);
-
-  if (!encoded_p)
-    sym_name = ada_decode (sym_name);
-
-  return sym_name;
-}
-
-/* A companion function to ada_collect_symbol_completion_matches().
-   Check if SYM_NAME represents a symbol which name would be suitable
-   to complete TEXT (TEXT_LEN is the length of TEXT), in which case it
-   is added as a completion match to TRACKER.
-
-   ORIG_TEXT is the string original string from the user command
-   that needs to be completed.  WORD is the entire command on which
-   completion should be performed.  These two parameters are used to
-   determine which part of the symbol name should be added to the
-   completion vector.
-   if WILD_MATCH_P is set, then wild matching is performed.
-   ENCODED_P should be set if TEXT represents a symbol name in its
-   encoded formed (in which case the completion should also be
-   encoded).  */
-
-static void
-symbol_completion_add (completion_tracker &tracker,
-		       const char *sym_name,
-                       const char *text, int text_len,
-                       const char *orig_text, const char *word,
-                       int wild_match_p, int encoded_p)
-{
-  const char *match = symbol_completion_match (sym_name, text, text_len,
-                                               wild_match_p, encoded_p);
-  char *completion;
+    return false;
 
-  if (match == NULL)
-    return;
+  if (comp_match != NULL)
+    {
+      std::string &match_str = comp_match->storage ();
 
-  /* We found a match, so add the appropriate completion to the given
-     string vector.  */
+      if (!m_encoded_p)
+	{
+	  match_str = ada_decode (sym_name);
+	  comp_match->set_match (match_str.c_str ());
+	}
+      else
+	{
+	  if (m_verbatim_p)
+	    match_str = add_angle_brackets (sym_name);
+	  else
+	    match_str = sym_name;
 
-  if (word == orig_text)
-    {
-      completion = (char *) xmalloc (strlen (match) + 5);
-      strcpy (completion, match);
-    }
-  else if (word > orig_text)
-    {
-      /* Return some portion of sym_name.  */
-      completion = (char *) xmalloc (strlen (match) + 5);
-      strcpy (completion, match + (word - orig_text));
-    }
-  else
-    {
-      /* Return some of ORIG_TEXT plus sym_name.  */
-      completion = (char *) xmalloc (strlen (match) + (orig_text - word) + 5);
-      strncpy (completion, word, orig_text - word);
-      completion[orig_text - word] = '\0';
-      strcat (completion, match);
+	  comp_match->set_match (match_str.c_str ());
+	}
     }
 
-  tracker.add_completion (gdb::unique_xmalloc_ptr<char> (completion));
+  return true;
 }
 
-/* Add the list of possible symbol names completing TEXT0 to TRACKER.
+/* Add the list of possible symbol names completing TEXT to TRACKER.
    WORD is the entire command on which completion is made.  */
 
 static void
 ada_collect_symbol_completion_matches (completion_tracker &tracker,
 				       complete_symbol_mode mode,
-				       const char *text0, const char *word,
+				       symbol_name_match_type name_match_type,
+				       const char *text, const char *word,
 				       enum type_code code)
 {
-  char *text;
   int text_len;
-  int wild_match_p;
-  int encoded_p;
   struct symbol *sym;
   struct compunit_symtab *s;
   struct minimal_symbol *msymbol;
@@ -6519,39 +6455,15 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
   gdb_assert (code == TYPE_CODE_UNDEF);
 
-  if (text0[0] == '<')
-    {
-      text = xstrdup (text0);
-      make_cleanup (xfree, text);
-      text_len = strlen (text);
-      wild_match_p = 0;
-      encoded_p = 1;
-    }
-  else
-    {
-      text = xstrdup (ada_encode (text0));
-      make_cleanup (xfree, text);
-      text_len = strlen (text);
-      for (i = 0; i < text_len; i++)
-        text[i] = tolower (text[i]);
+  text_len = strlen (text);
 
-      encoded_p = (strstr (text0, "__") != NULL);
-      /* If the name contains a ".", then the user is entering a fully
-         qualified entity name, and the match must not be done in wild
-         mode.  Similarly, if the user wants to complete what looks like
-         an encoded name, the match must not be done in wild mode.  */
-      wild_match_p = (strchr (text0, '.') == NULL && !encoded_p);
-    }
+  lookup_name_info lookup_name (std::string (text, text_len),
+				name_match_type, true);
 
   /* First, look at the partial symtab symbols.  */
   expand_symtabs_matching (NULL,
-			   [&] (const char *symname)
-			   {
-			     return symbol_completion_match (symname,
-							     text, text_len,
-							     wild_match_p,
-							     encoded_p);
-			   },
+			   lookup_name,
+			   NULL,
 			   NULL,
 			   ALL_DOMAIN);
 
@@ -6563,9 +6475,12 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
   ALL_MSYMBOLS (objfile, msymbol)
   {
     QUIT;
-    symbol_completion_add (tracker, MSYMBOL_LINKAGE_NAME (msymbol),
-			   text, text_len, text0, word, wild_match_p,
-			   encoded_p);
+
+    completion_list_add_name (tracker,
+			      MSYMBOL_LANGUAGE (msymbol),
+			      MSYMBOL_LINKAGE_NAME (msymbol),
+			      lookup_name,
+			      text, text_len, text, word);
   }
 
   /* Search upwards from currently selected frame (so that we can
@@ -6578,9 +6493,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
 
       ALL_BLOCK_SYMBOLS (b, iter, sym)
       {
-	symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                               text, text_len, text0, word,
-                               wild_match_p, encoded_p);
+	completion_list_add_name (tracker,
+				  SYMBOL_LANGUAGE (sym),
+				  SYMBOL_LINKAGE_NAME (sym),
+				  lookup_name,
+				  text, text_len, text, word);
       }
     }
 
@@ -6593,9 +6510,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
     b = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (s), GLOBAL_BLOCK);
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                             text, text_len, text0, word,
-                             wild_match_p, encoded_p);
+      completion_list_add_name (tracker,
+				SYMBOL_LANGUAGE (sym),
+				SYMBOL_LINKAGE_NAME (sym),
+				lookup_name,
+				text, text_len, text, word);
     }
   }
 
@@ -6608,9 +6527,11 @@ ada_collect_symbol_completion_matches (completion_tracker &tracker,
       continue;
     ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      symbol_completion_add (tracker, SYMBOL_LINKAGE_NAME (sym),
-                             text, text_len, text0, word,
-                             wild_match_p, encoded_p);
+      completion_list_add_name (tracker,
+				SYMBOL_LANGUAGE (sym),
+				SYMBOL_LINKAGE_NAME (sym),
+				lookup_name,
+				text, text_len, text, word);
     }
   }
 
@@ -11598,11 +11519,12 @@ scan_discrim_bound (const char *str, int k, struct value *dval, LONGEST * px,
 static struct value *
 get_var_value (const char *name, const char *err_msg)
 {
-  struct block_symbol *syms;
-  int nsyms;
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
-  nsyms = ada_lookup_symbol_list (name, get_selected_block (0), VAR_DOMAIN,
-                                  &syms);
+  struct block_symbol *syms;
+  int nsyms = ada_lookup_symbol_list_worker (lookup_name,
+					     get_selected_block (0),
+					     VAR_DOMAIN, &syms, 1);
 
   if (nsyms != 1)
     {
@@ -13275,6 +13197,7 @@ ada_add_global_exceptions (compiled_regex *preg,
      regular expression used to do the matching refers to the natural
      name.  So match against the decoded name.  */
   expand_symtabs_matching (NULL,
+			   lookup_name_info::match_any (),
 			   [&] (const char *search_name)
 			   {
 			     const char *decoded = ada_decode (search_name);
@@ -13874,16 +13797,113 @@ static const struct exp_descriptor ada_exp_descriptor = {
   ada_evaluate_subexp
 };
 
-/* Implement the "la_get_symbol_name_cmp" language_defn method
-   for Ada.  */
+/* symbol_name_matcher_ftype adapter for wild_match.  */
+
+static bool
+do_wild_match (const char *symbol_search_name,
+	       const lookup_name_info &lookup_name,
+	       completion_match *match)
+{
+  return wild_match (symbol_search_name, ada_lookup_name (lookup_name));
+}
+
+/* symbol_name_matcher_ftype adapter for full_match.  */
+
+static bool
+do_full_match (const char *symbol_search_name,
+	       const lookup_name_info &lookup_name,
+	       completion_match *match)
+{
+  return full_match (symbol_search_name, ada_lookup_name (lookup_name));
+}
+
+/* Build the Ada lookup name for LOOKUP_NAME.  */
+
+ada_lookup_name_info::ada_lookup_name_info (const lookup_name_info &lookup_name)
+{
+  const std::string &user_name = lookup_name.name ();
+
+  if (user_name[0] == '<')
+    {
+      if (user_name.back () == '>')
+	m_encoded_name = user_name.substr (1, user_name.size () - 2);
+      else
+	m_encoded_name = user_name.substr (1, user_name.size () - 1);
+      m_encoded_p = true;
+      m_verbatim_p = true;
+      m_wild_match_p = false;
+      m_standard_p = false;
+    }
+  else
+    {
+      m_verbatim_p = false;
+
+      m_encoded_p = user_name.find ("__") != std::string::npos;
+
+      if (!m_encoded_p)
+	{
+	  const char *folded = ada_fold_name (user_name.c_str ());
+	  const char *encoded = ada_encode_1 (folded, false);
+	  if (encoded != NULL)
+	    m_encoded_name = encoded;
+	  else
+	    m_encoded_name = user_name;
+	}
+      else
+	m_encoded_name = user_name;
+
+      /* Handle the 'package Standard' special case.  See description
+	 of m_standard_p.  */
+      if (startswith (m_encoded_name.c_str (), "standard__"))
+	{
+	  m_encoded_name = m_encoded_name.substr (sizeof ("standard__") - 1);
+	  m_standard_p = true;
+	}
+      else
+	m_standard_p = false;
 
-static symbol_name_cmp_ftype
-ada_get_symbol_name_cmp (const char *lookup_name)
+      /* If the name contains a ".", then the user is entering a fully
+	 qualified entity name, and the match must not be done in wild
+	 mode.  Similarly, if the user wants to complete what looks
+	 like an encoded name, the match must not be done in wild
+	 mode.  Also, in the standard__ special case always do
+	 non-wild matching.  */
+      m_wild_match_p
+	= (lookup_name.match_type () != symbol_name_match_type::FULL
+	   && !m_encoded_p
+	   && !m_standard_p
+	   && user_name.find ('.') == std::string::npos);
+    }
+}
+
+/* symbol_name_matcher_ftype method for Ada.  This only handles
+   completion mode.  */
+
+static bool
+ada_symbol_name_matches (const char *symbol_search_name,
+			 const lookup_name_info &lookup_name,
+			 completion_match *match)
 {
-  if (should_use_wild_match (lookup_name))
-    return wild_match;
+  return lookup_name.ada ().matches (symbol_search_name,
+				     lookup_name.match_type (),
+				     match);
+}
+
+/* Implement the "la_get_symbol_name_matcher" language_defn method for
+   Ada.  */
+
+static symbol_name_matcher_ftype *
+ada_get_symbol_name_matcher (const lookup_name_info &lookup_name)
+{
+  if (lookup_name.completion_mode ())
+    return ada_symbol_name_matches;
   else
-    return compare_names;
+    {
+      if (lookup_name.ada ().wild_match_p ())
+	return do_wild_match;
+      else
+	return do_full_match;
+    }
 }
 
 /* Implement the "la_read_var_value" language_defn method for Ada.  */
@@ -13954,7 +13974,7 @@ extern const struct language_defn ada_language_defn = {
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  ada_get_symbol_name_cmp,	/* la_get_symbol_name_cmp */
+  ada_get_symbol_name_matcher,	/* la_get_symbol_name_matcher */
   ada_iterate_over_symbols,
   default_search_name_hash,
   &ada_varobj_ops,
diff --git a/gdb/ada-lex.l b/gdb/ada-lex.l
index fe97352..d30485c 100644
--- a/gdb/ada-lex.l
+++ b/gdb/ada-lex.l
@@ -420,13 +420,12 @@ processReal (struct parser_state *par_state, const char *num0)
 /* Store a canonicalized version of NAME0[0..LEN-1] in yylval.ssym.  The
    resulting string is valid until the next call to ada_parse.  If
    NAME0 contains the substring "___", it is assumed to be already
-   encoded and the resulting name is equal to it.  Otherwise, it differs
+   encoded and the resulting name is equal to it.  Similarly, if the name
+   starts with '<', it is copied verbatim.  Otherwise, it differs
    from NAME0 in that:
-    + Characters between '...' or <...> are transfered verbatim to 
-      yylval.ssym.
-    + <, >, and trailing "'" characters in quoted sequences are removed
-      (a leading quote is preserved to indicate that the name is not to be
-      GNAT-encoded).
+    + Characters between '...' are transfered verbatim to yylval.ssym.
+    + Trailing "'" characters in quoted sequences are removed (a leading quote is
+      preserved to indicate that the name is not to be GNAT-encoded).
     + Unquoted whitespace is removed.
     + Unquoted alphabetic characters are mapped to lower case.
    Result is returned as a struct stoken, but for convenience, the string
@@ -444,7 +443,7 @@ processId (const char *name0, int len)
   while (len > 0 && isspace (name0[len-1]))
     len -= 1;
 
-  if (strstr (name0, "___") != NULL)
+  if (name0[0] == '<' || strstr (name0, "___") != NULL)
     {
       strncpy (name, name0, len);
       name[len] = '\000';
@@ -478,15 +477,6 @@ processId (const char *name0, int len)
 	  while (i0 < len && name0[i0] != '\'');
 	  i0 += 1;
 	  break;
-	case '<':
-	  i0 += 1;
-	  while (i0 < len && name0[i0] != '>')
-	    {
-	      name[i] = name0[i0];
-	      i += 1; i0 += 1;
-	    }
-	  i0 += 1;
-	  break;
 	}
     }
   name[i] = '\000';
diff --git a/gdb/block.c b/gdb/block.c
index 1c343aa..a8075a1 100644
--- a/gdb/block.c
+++ b/gdb/block.c
@@ -595,8 +595,7 @@ block_iterator_next (struct block_iterator *iterator)
 
 static struct symbol *
 block_iter_match_step (struct block_iterator *iterator,
-		       const char *name,
-		       symbol_compare_ftype *compare,
+		       const lookup_name_info &name,
 		       int first)
 {
   struct symbol *sym;
@@ -618,10 +617,10 @@ block_iter_match_step (struct block_iterator *iterator,
 	  block = BLOCKVECTOR_BLOCK (COMPUNIT_BLOCKVECTOR (cust),
 				     iterator->which);
 	  sym = dict_iter_match_first (BLOCK_DICT (block), name,
-				       compare, &iterator->dict_iter);
+				       &iterator->dict_iter);
 	}
       else
-	sym = dict_iter_match_next (name, compare, &iterator->dict_iter);
+	sym = dict_iter_match_next (name, &iterator->dict_iter);
 
       if (sym != NULL)
 	return sym;
@@ -638,30 +637,27 @@ block_iter_match_step (struct block_iterator *iterator,
 
 struct symbol *
 block_iter_match_first (const struct block *block,
-			const char *name,
-			symbol_compare_ftype *compare,
+			const lookup_name_info &name,
 			struct block_iterator *iterator)
 {
   initialize_block_iterator (block, iterator);
 
   if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_match_first (block->dict, name, compare,
-				  &iterator->dict_iter);
+    return dict_iter_match_first (block->dict, name, &iterator->dict_iter);
 
-  return block_iter_match_step (iterator, name, compare, 1);
+  return block_iter_match_step (iterator, name, 1);
 }
 
 /* See block.h.  */
 
 struct symbol *
-block_iter_match_next (const char *name,
-		       symbol_compare_ftype *compare,
+block_iter_match_next (const lookup_name_info &name,
 		       struct block_iterator *iterator)
 {
   if (iterator->which == FIRST_LOCAL_BLOCK)
-    return dict_iter_match_next (name, compare, &iterator->dict_iter);
+    return dict_iter_match_next (name, &iterator->dict_iter);
 
-  return block_iter_match_step (iterator, name, compare, 0);
+  return block_iter_match_step (iterator, name, 0);
 }
 
 /* See block.h.
@@ -682,11 +678,13 @@ block_lookup_symbol (const struct block *block, const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   if (!BLOCK_FUNCTION (block))
     {
       struct symbol *other = NULL;
 
-      ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+      ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
 	{
 	  if (SYMBOL_DOMAIN (sym) == domain)
 	    return sym;
@@ -713,7 +711,7 @@ block_lookup_symbol (const struct block *block, const char *name,
 
       struct symbol *sym_found = NULL;
 
-      ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+      ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				     SYMBOL_DOMAIN (sym), domain))
@@ -738,14 +736,16 @@ block_lookup_symbol_primary (const struct block *block, const char *name,
   struct symbol *sym, *other;
   struct dict_iterator dict_iter;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   /* Verify BLOCK is STATIC_BLOCK or GLOBAL_BLOCK.  */
   gdb_assert (BLOCK_SUPERBLOCK (block) == NULL
 	      || BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL);
 
   other = NULL;
-  for (sym = dict_iter_match_first (block->dict, name, strcmp_iw, &dict_iter);
+  for (sym = dict_iter_match_first (block->dict, lookup_name, &dict_iter);
        sym != NULL;
-       sym = dict_iter_match_next (name, strcmp_iw, &dict_iter))
+       sym = dict_iter_match_next (lookup_name, &dict_iter))
     {
       if (SYMBOL_DOMAIN (sym) == domain)
 	return sym;
@@ -772,11 +772,13 @@ block_find_symbol (const struct block *block, const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   /* Verify BLOCK is STATIC_BLOCK or GLOBAL_BLOCK.  */
   gdb_assert (BLOCK_SUPERBLOCK (block) == NULL
 	      || BLOCK_SUPERBLOCK (BLOCK_SUPERBLOCK (block)) == NULL);
 
-  ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+  ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
     {
       /* MATCHER is deliberately called second here so that it never sees
 	 a non-domain-matching symbol.  */
diff --git a/gdb/block.h b/gdb/block.h
index 1741e52..0326c18 100644
--- a/gdb/block.h
+++ b/gdb/block.h
@@ -237,27 +237,22 @@ extern struct symbol *block_iterator_first (const struct block *block,
 extern struct symbol *block_iterator_next (struct block_iterator *iterator);
 
 /* Initialize ITERATOR to point at the first symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (which must use
-   the same conventions as strcmp_iw and be compatible with any
-   block hashing function), and return that first symbol, or NULL
-   if there are no such symbols.  */
+   SYMBOL_SEARCH_NAME matches NAME, and return that first symbol, or
+   NULL if there are no such symbols.  */
 
 extern struct symbol *block_iter_match_first (const struct block *block,
-					      const char *name,
-					      symbol_compare_ftype *compare,
+					      const lookup_name_info &name,
 					      struct block_iterator *iterator);
 
 /* Advance ITERATOR to point at the next symbol in BLOCK whose
-   SYMBOL_SEARCH_NAME is NAME, as tested using COMPARE (see
-   block_iter_match_first), or NULL if there are no more such symbols.
-   Don't call this if you've previously received NULL from 
+   SYMBOL_SEARCH_NAME matches NAME, or NULL if there are no more such
+   symbols.  Don't call this if you've previously received NULL from
    block_iterator_match_first or block_iterator_match_next on this
    iteration.  And don't call it unless ITERATOR was created by a
-   previous call to block_iter_match_first with the same NAME and COMPARE.  */
+   previous call to block_iter_match_first with the same NAME.  */
 
-extern struct symbol *block_iter_match_next (const char *name,
-					     symbol_compare_ftype *compare,
-					     struct block_iterator *iterator);
+extern struct symbol *block_iter_match_next
+  (const lookup_name_info &name, struct block_iterator *iterator);
 
 /* Search BLOCK for symbol NAME in DOMAIN.  */
 
@@ -316,14 +311,14 @@ extern int block_find_non_opaque_type_preferred (struct symbol *sym,
        (sym);						\
        (sym) = block_iterator_next (&(iter)))
 
-/* Macro to loop through all symbols with name NAME in BLOCK,
-   in no particular order.  ITER helps keep track of the iteration, and
-   must be a struct block_iterator.  SYM points to the current symbol.  */
+/* Macro to loop through all symbols in BLOCK with a name that matches
+   NAME, in no particular order.  ITER helps keep track of the
+   iteration, and must be a struct block_iterator.  SYM points to the
+   current symbol.  */
 
 #define ALL_BLOCK_SYMBOLS_WITH_NAME(block, name, iter, sym)		\
-  for ((sym) = block_iter_match_first ((block), (name),			\
-				       strcmp_iw, &(iter));		\
+  for ((sym) = block_iter_match_first ((block), (name), &(iter));	\
        (sym) != NULL;							\
-       (sym) = block_iter_match_next ((name), strcmp_iw, &(iter)))
+       (sym) = block_iter_match_next ((name), &(iter)))
 
 #endif /* BLOCK_H */
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 9749935..49077c7 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -869,7 +869,7 @@ extern const struct language_defn c_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &c_varobj_ops,
@@ -1014,7 +1014,7 @@ extern const struct language_defn cplus_language_defn =
   cp_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  cp_get_symbol_name_matcher,
   iterate_over_symbols,
   default_search_name_hash,
   &cplus_varobj_ops,
@@ -1068,7 +1068,7 @@ extern const struct language_defn asm_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
@@ -1122,7 +1122,7 @@ extern const struct language_defn minimal_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/completer.c b/gdb/completer.c
index cd0ecc3..68e9171 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -499,6 +499,7 @@ complete_files_symbols (completion_tracker &tracker,
     {
       collect_file_symbol_completion_matches (tracker,
 					      complete_symbol_mode::EXPRESSION,
+					      symbol_name_match_type::EXPRESSION,
 					      symbol_start, word,
 					      file_to_match);
       xfree (file_to_match);
@@ -509,6 +510,7 @@ complete_files_symbols (completion_tracker &tracker,
 
       collect_symbol_completion_matches (tracker,
 					 complete_symbol_mode::EXPRESSION,
+					 symbol_name_match_type::EXPRESSION,
 					 symbol_start, word);
       /* If text includes characters which cannot appear in a file
 	 name, they cannot be asking for completion on files.  */
@@ -551,6 +553,7 @@ complete_files_symbols (completion_tracker &tracker,
 	 on the entire text as a symbol.  */
       collect_symbol_completion_matches (tracker,
 					 complete_symbol_mode::EXPRESSION,
+					 symbol_name_match_type::EXPRESSION,
 					 orig_text, word);
     }
 }
@@ -1104,6 +1107,7 @@ symbol_completer (struct cmd_list_element *ignore,
 		  const char *text, const char *word)
 {
   collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+				     symbol_name_match_type::EXPRESSION,
 				     text, word);
 }
 
diff --git a/gdb/completer.h b/gdb/completer.h
index 82a994c..38fee6b 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -68,6 +68,62 @@ struct match_list_displayer
    calls free on each element.  */
 typedef std::vector<gdb::unique_xmalloc_ptr<char>> completion_list;
 
+/* The result of a successful completion match.  When doing symbol
+   comparison, we use the symbol search name for the symbol name match
+   check, but the matched name that is shown to the user may be
+   different.  For example, Ada uses encoded names for lookup, but
+   then wants to decode the symbol name to show to the user, and also
+   in some cases wrap the matched name in "<sym>" (meaning we can't
+   always use the symbol's print name).  */
+
+class completion_match
+{
+public:
+  /* Get the completion match result.  See m_match/m_storage's
+     descriptions.  */
+  const char *match ()
+  { return m_match; }
+
+  /* Set the completion match result.  See m_match/m_storage's
+     descriptions.  */
+  void set_match (const char *match)
+  { m_match = match; }
+
+  /* Get temporary storage for generating a match result, dynamically.
+     The built string is only good until the next clear() call.  I.e.,
+     good until the next symbol comparison.  */
+  std::string &storage ()
+  { return m_storage; }
+
+  /* Prepare for another completion matching sequence.  */
+  void clear ()
+  {
+    m_match = NULL;
+    m_storage.clear ();
+  }
+
+private:
+  /* The completion match result.  This can either be a pointer into
+     M_STORAGE string, or it can be a pointer into the some other
+     string that outlives the completion matching sequence (usually, a
+     pointer to a symbol's name).  */
+  const char *m_match;
+
+  /* Storage a symbol comparison routine can use for generating a
+     match result, dynamically.  The built string is only good until
+     the next clear() call.  I.e., good until the next symbol
+     comparison.  */
+  std::string m_storage;
+};
+
+/* Convenience aggregate holding info returned by the symbol name
+   matching routines (see symbol_name_matcher_ftype).  */
+struct completion_match_result
+{
+  /* The completion match candidate.  */
+  completion_match match;
+};
+
 /* The final result of a completion that is handed over to either
    readline or the "completion" command (which pretends to be
    readline).  Mainly a wrapper for a readline-style match list array,
@@ -203,6 +259,18 @@ public:
      already have.  */
   bool completes_to_completion_word (const char *word);
 
+  /* Get a reference to the shared (between all the multiple symbol
+     name comparison calls) completion_match_result object, ready for
+     another symbol name match sequence.  */
+  completion_match_result &reset_completion_match_result ()
+  {
+    completion_match_result &res = m_completion_match_result;
+
+    /* Clear any previous match.  */
+    res.match.clear ();
+    return m_completion_match_result;
+  }
+
   /* True if we have any completion match recorded.  */
   bool have_completions () const
   { return !m_entries_vec.empty (); }
@@ -228,6 +296,13 @@ private:
      to hand over to readline.  */
   void recompute_lowest_common_denominator (const char *new_match);
 
+  /* Completion match outputs returned by the symbol name matching
+     routines (see symbol_name_matcher_ftype).  These results are only
+     valid for a single match call.  This is here in order to be able
+     to conveniently share the same storage among all the calls to the
+     symbol name matching routines.  */
+  completion_match_result m_completion_match_result;
+
   /* The completion matches found so far, in a vector.  */
   completion_list m_entries_vec;
 
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 37b2b4a..7bf5d1b 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1181,7 +1181,9 @@ make_symbol_overload_list_block (const char *name,
   struct block_iterator iter;
   struct symbol *sym;
 
-  ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
+  ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym)
     overload_list_add_symbol (sym, name);
 }
 
@@ -1579,6 +1581,40 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
   return *demangled != NULL;
 }
 
+/* C++ symbol_name_matcher_ftype implementation.  */
+
+static bool
+cp_fq_symbol_name_matches (const char *symbol_search_name,
+			   const lookup_name_info &lookup_name,
+			   completion_match *match)
+{
+  /* Get the demangled name.  */
+  const std::string &name = lookup_name.cplus ().lookup_name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  if (strncmp_iw_with_mode (symbol_search_name,
+			    name.c_str (), name.size (),
+			    mode) == 0)
+    {
+      if (match != NULL)
+	match->set_match (symbol_search_name);
+      return true;
+    }
+
+  return false;
+}
+
+/* See cp-support.h.  */
+
+symbol_name_matcher_ftype *
+cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
+{
+  return cp_fq_symbol_name_matches;
+}
+
 /* Don't allow just "maintenance cplus".  */
 
 static  void
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 28353a2..a699a80 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -108,6 +108,11 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
 extern struct type *cp_lookup_rtti_type (const char *name,
 					 struct block *block);
 
+/* Implement the "la_get_symbol_name_matcher" language_defn method for
+   C++.  */
+extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
+  (const lookup_name_info &lookup_name);
+
 /* Functions/variables from cp-namespace.c.  */
 
 extern int cp_is_in_anonymous (const char *symbol_name);
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 2fa429b..e83e599 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -245,7 +245,7 @@ extern const struct language_defn d_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index 1ffa4f3..8277c5d 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -115,11 +115,9 @@ struct dict_vector
   struct symbol *(*iterator_next) (struct dict_iterator *iterator);
   /* Functions to iterate over symbols with a given name.  */
   struct symbol *(*iter_match_first) (const struct dictionary *dict,
-				      const char *name,
-				      symbol_compare_ftype *equiv,
+				      const lookup_name_info &name,
 				      struct dict_iterator *iterator);
-  struct symbol *(*iter_match_next) (const char *name,
-				     symbol_compare_ftype *equiv,
+  struct symbol *(*iter_match_next) (const lookup_name_info &name,
 				     struct dict_iterator *iterator);
   /* A size function, for maint print symtabs.  */
   int (*size) (const struct dictionary *dict);
@@ -239,12 +237,10 @@ static struct symbol *iterator_first_hashed (const struct dictionary *dict,
 static struct symbol *iterator_next_hashed (struct dict_iterator *iterator);
 
 static struct symbol *iter_match_first_hashed (const struct dictionary *dict,
-					       const char *name,
-					       symbol_compare_ftype *compare,
+					       const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
-static struct symbol *iter_match_next_hashed (const char *name,
-					      symbol_compare_ftype *compare,
+static struct symbol *iter_match_next_hashed (const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
 /* Functions only for DICT_HASHED.  */
@@ -269,12 +265,10 @@ static struct symbol *iterator_first_linear (const struct dictionary *dict,
 static struct symbol *iterator_next_linear (struct dict_iterator *iterator);
 
 static struct symbol *iter_match_first_linear (const struct dictionary *dict,
-					       const char *name,
-					       symbol_compare_ftype *compare,
+					       const lookup_name_info &name,
 					       struct dict_iterator *iterator);
 
-static struct symbol *iter_match_next_linear (const char *name,
-					      symbol_compare_ftype *compare,
+static struct symbol *iter_match_next_linear (const lookup_name_info &name,
 					      struct dict_iterator *iterator);
 
 static int size_linear (const struct dictionary *dict);
@@ -526,19 +520,18 @@ dict_iterator_next (struct dict_iterator *iterator)
 
 struct symbol *
 dict_iter_match_first (const struct dictionary *dict,
-		       const char *name, symbol_compare_ftype *compare,
+		       const lookup_name_info &name,
 		       struct dict_iterator *iterator)
 {
-  return (DICT_VECTOR (dict))->iter_match_first (dict, name,
-						 compare, iterator);
+  return (DICT_VECTOR (dict))->iter_match_first (dict, name, iterator);
 }
 
 struct symbol *
-dict_iter_match_next (const char *name, symbol_compare_ftype *compare,
+dict_iter_match_next (const lookup_name_info &name,
 		      struct dict_iterator *iterator)
 {
   return (DICT_VECTOR (DICT_ITERATOR_DICT (iterator)))
-    ->iter_match_next (name, compare, iterator);
+    ->iter_match_next (name, iterator);
 }
 
 int
@@ -629,13 +622,15 @@ iterator_hashed_advance (struct dict_iterator *iterator)
 }
 
 static struct symbol *
-iter_match_first_hashed (const struct dictionary *dict, const char *name,
-			 symbol_compare_ftype *compare,
+iter_match_first_hashed (const struct dictionary *dict,
+			 const lookup_name_info &name,
 			 struct dict_iterator *iterator)
 {
-  unsigned int hash_index
-    = (search_name_hash (DICT_LANGUAGE (dict)->la_language, name)
-       % DICT_HASHED_NBUCKETS (dict));
+  const language_defn *lang = DICT_LANGUAGE (dict);
+  unsigned int hash_index = (name.search_name_hash (lang->la_language)
+			     % DICT_HASHED_NBUCKETS (dict));
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
   struct symbol *sym;
 
   DICT_ITERATOR_DICT (iterator) = dict;
@@ -649,11 +644,8 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
        sym = sym->hash_next)
     {
       /* Warning: the order of arguments to compare matters!  */
-      if (compare (SYMBOL_SEARCH_NAME (sym), name) == 0)
-	{
-	  break;
-	}
-	
+      if (matches_name (SYMBOL_SEARCH_NAME (sym), name, NULL))
+	break;
     }
 
   DICT_ITERATOR_CURRENT (iterator) = sym;
@@ -661,16 +653,19 @@ iter_match_first_hashed (const struct dictionary *dict, const char *name,
 }
 
 static struct symbol *
-iter_match_next_hashed (const char *name, symbol_compare_ftype *compare,
+iter_match_next_hashed (const lookup_name_info &name,
 			struct dict_iterator *iterator)
 {
+  const language_defn *lang = DICT_LANGUAGE (DICT_ITERATOR_DICT (iterator));
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
   struct symbol *next;
 
   for (next = DICT_ITERATOR_CURRENT (iterator)->hash_next;
        next != NULL;
        next = next->hash_next)
     {
-      if (compare (SYMBOL_SEARCH_NAME (next), name) == 0)
+      if (matches_name (SYMBOL_SEARCH_NAME (next), name, NULL))
 	break;
     }
 
@@ -863,27 +858,32 @@ iterator_next_linear (struct dict_iterator *iterator)
 
 static struct symbol *
 iter_match_first_linear (const struct dictionary *dict,
-			 const char *name, symbol_compare_ftype *compare,
+			 const lookup_name_info &name,
 			 struct dict_iterator *iterator)
 {
   DICT_ITERATOR_DICT (iterator) = dict;
   DICT_ITERATOR_INDEX (iterator) = -1;
 
-  return iter_match_next_linear (name, compare, iterator);
+  return iter_match_next_linear (name, iterator);
 }
 
 static struct symbol *
-iter_match_next_linear (const char *name, symbol_compare_ftype *compare,
+iter_match_next_linear (const lookup_name_info &name,
 			struct dict_iterator *iterator)
 {
   const struct dictionary *dict = DICT_ITERATOR_DICT (iterator);
+  const language_defn *lang = DICT_LANGUAGE (dict);
+  symbol_name_matcher_ftype *matches_name
+    = language_get_symbol_name_matcher (lang, name);
+
   int i, nsyms = DICT_LINEAR_NSYMS (dict);
   struct symbol *sym, *retval = NULL;
 
   for (i = DICT_ITERATOR_INDEX (iterator) + 1; i < nsyms; ++i)
     {
       sym = DICT_LINEAR_SYM (dict, i);
-      if (compare (SYMBOL_SEARCH_NAME (sym), name) == 0)
+
+      if (matches_name (SYMBOL_SEARCH_NAME (sym), name, NULL))
 	{
 	  retval = sym;
 	  break;
diff --git a/gdb/dictionary.h b/gdb/dictionary.h
index e4a9315..e65026b 100644
--- a/gdb/dictionary.h
+++ b/gdb/dictionary.h
@@ -133,8 +133,7 @@ extern struct symbol *dict_iterator_next (struct dict_iterator *iterator);
    if there are no such symbols.  */
 
 extern struct symbol *dict_iter_match_first (const struct dictionary *dict,
-					     const char *name,
-					     symbol_compare_ftype *compare,
+					     const lookup_name_info &name,
 					     struct dict_iterator *iterator);
 
 /* Advance ITERATOR to point at the next symbol in DICT whose
@@ -145,8 +144,7 @@ extern struct symbol *dict_iter_match_first (const struct dictionary *dict,
    iteration.  And don't call it unless ITERATOR was created by a
    previous call to dict_iter_match_first with the same NAME and COMPARE.  */
 
-extern struct symbol *dict_iter_match_next (const char *name,
-					    symbol_compare_ftype *compare,
+extern struct symbol *dict_iter_match_next (const lookup_name_info &name,
 					    struct dict_iterator *iterator);
 
 /* Return some notion of the size of the dictionary: the number of
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 9da8667..c5b2394 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -3878,6 +3878,8 @@ dw2_lookup_symbol (struct objfile *objfile, int block_index,
 
   dw2_setup (objfile);
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   index = dwarf2_per_objfile->index_table;
 
   /* index is NULL if OBJF_READNOW.  */
@@ -3904,10 +3906,10 @@ dw2_lookup_symbol (struct objfile *objfile, int block_index,
 	     information (but NAME might contain it).  */
 
 	  if (sym != NULL
-	      && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	    return stab;
 	  if (with_opaque != NULL
-	      && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
+	      && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, lookup_name))
 	    stab_best = stab;
 
 	  /* Keep looking through other CUs.  */
@@ -4052,7 +4054,7 @@ dw2_map_matching_symbols (struct objfile *objfile,
 			  int global,
 			  int (*callback) (struct block *,
 					   struct symbol *, void *),
-			  void *data, symbol_compare_ftype *match,
+			  void *data, symbol_name_match_type match,
 			  symbol_compare_ftype *ordered_compare)
 {
   /* Currently unimplemented; used for Ada.  The function can be called if the
@@ -4060,10 +4062,96 @@ dw2_map_matching_symbols (struct objfile *objfile,
      does not look for non-Ada symbols this function should just return.  */
 }
 
+/* Symbol name matcher for .gdb_index names.
+
+   Symbol names in .gdb_index have a few particularities:
+
+   - There's no indication of which is the language of each symbol.
+
+     Since each language has its own symbol name matching algorithm,
+     and we don't know which language is the right one, we must match
+     each symbol against all languages.
+
+   - Symbol names in the index have no overload (parameter)
+     information.  I.e., in C++, "foo(int)" and "foo(long)" both
+     appear as "foo" in the index, for example.
+
+     This means that the lookup names passed to the symbol name
+     matcher functions must have no parameter information either
+     because (e.g.) symbol search name "foo" does not match
+     lookup-name "foo(int)" [while swapping search name for lookup
+     name would match].
+*/
+class gdb_index_symbol_name_matcher
+{
+public:
+  /* Prepares the vector of comparison functions for LOOKUP_NAME.  */
+  gdb_index_symbol_name_matcher (const lookup_name_info &lookup_name);
+
+  /* Walk all the matcher routines and match SYMBOL_NAME against them.
+     Returns true if any matcher matches.  */
+  bool matches (const char *symbol_name);
+
+private:
+  /* A reference to the lookup name we're matching against.  */
+  const lookup_name_info &m_lookup_name;
+
+  /* A vector holding all the different symbol name matchers, for all
+     languages.  */
+  std::vector<symbol_name_matcher_ftype *> m_symbol_name_matcher_funcs;
+};
+
+gdb_index_symbol_name_matcher::gdb_index_symbol_name_matcher
+  (const lookup_name_info &lookup_name)
+    : m_lookup_name (lookup_name)
+{
+  /* Prepare the vector of comparison functions upfront, to avoid
+     doing the same work for each symbol.  Care is taken to avoid
+     matching with the same matcher more than once if/when multiple
+     languages use the same matcher function.  */
+  auto &matchers = m_symbol_name_matcher_funcs;
+  matchers.reserve (nr_languages);
+
+  matchers.push_back (default_symbol_name_matcher);
+
+  for (int i = 0; i < nr_languages; i++)
+    {
+      const language_defn *lang = language_def ((enum language) i);
+      if (lang->la_get_symbol_name_matcher != NULL)
+	{
+	  symbol_name_matcher_ftype *name_matcher
+	    = lang->la_get_symbol_name_matcher (m_lookup_name);
+
+	  /* Don't insert the same comparison routine more than once.
+	     Note that we do this linear walk instead of a cheaper
+	     sorted insert, or use a std::set or something like that,
+	     because relative order of function addresses is not
+	     stable.  This is not a problem in practice because the
+	     number of supported languages is low, and the cost here
+	     is tiny compared to the number of searches we'll do
+	     afterwards using this object.  */
+	  if (std::find (matchers.begin (), matchers.end (), name_matcher)
+	      == matchers.end ())
+	    matchers.push_back (name_matcher);
+	}
+    }
+}
+
+bool
+gdb_index_symbol_name_matcher::matches (const char *symbol_name)
+{
+  for (auto matches_name : m_symbol_name_matcher_funcs)
+    if (matches_name (symbol_name, m_lookup_name, NULL))
+      return true;
+
+  return false;
+}
+
 static void
 dw2_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -4151,6 +4239,8 @@ dw2_expand_symtabs_matching
 	}
     }
 
+  gdb_index_symbol_name_matcher lookup_name_matcher (lookup_name);
+
   for (iter = 0; iter < index->symbol_table_slots; ++iter)
     {
       offset_type idx = 2 * iter;
@@ -4165,7 +4255,8 @@ dw2_expand_symtabs_matching
 
       name = index->constant_pool + MAYBE_SWAP (index->symbol_table[idx]);
 
-      if (!symbol_matcher (name))
+      if (!lookup_name_matcher.matches (name)
+	  || (symbol_matcher != NULL && !symbol_matcher (name)))
 	continue;
 
       /* The name was matched, now expand corresponding CUs that were
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 0d78e5a..fe909ff 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -229,10 +229,12 @@ f_word_break_characters (void)
 static void
 f_collect_symbol_completion_matches (completion_tracker &tracker,
 				     complete_symbol_mode mode,
+				     symbol_name_match_type compare_name,
 				     const char *text, const char *word,
 				     enum type_code code)
 {
   default_collect_symbol_completion_matches_break_on (tracker, mode,
+						      compare_name,
 						      text, word, ":", code);
 }
 
@@ -289,7 +291,7 @@ extern const struct language_defn f_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 87ad063..9f88e32 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -606,7 +606,7 @@ extern const struct language_defn go_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/language.c b/gdb/language.c
index 9c92704..f2c0db1 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -697,6 +697,41 @@ default_get_string (struct value *value, gdb_byte **buffer, int *length,
   error (_("Getting a string is unsupported in this language."));
 }
 
+/* See language.h.  */
+
+bool
+default_symbol_name_matcher (const char *symbol_search_name,
+			     const lookup_name_info &lookup_name,
+			     completion_match *match)
+{
+  const std::string &name = lookup_name.name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
+			    mode) == 0)
+    {
+      if (match != NULL)
+	match->set_match (symbol_search_name);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* See language.h.  */
+
+symbol_name_matcher_ftype *
+language_get_symbol_name_matcher (const language_defn *lang,
+				  const lookup_name_info &lookup_name)
+{
+  if (lang->la_get_symbol_name_matcher != nullptr)
+    return lang->la_get_symbol_name_matcher (lookup_name);
+  return default_symbol_name_matcher;
+}
+
 /* Define the language that is no language.  */
 
 static int
@@ -835,7 +870,7 @@ const struct language_defn unknown_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
@@ -886,7 +921,7 @@ const struct language_defn auto_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/language.h b/gdb/language.h
index 64ccfcc..34f5692 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -126,16 +126,6 @@ struct language_arch_info
   struct type *bool_type_default;
 };
 
-/* A pointer to a function expected to return nonzero if
-   SYMBOL_SEARCH_NAME matches the given LOOKUP_NAME.
-
-   SYMBOL_SEARCH_NAME should be a symbol's "search" name.
-   LOOKUP_NAME should be the name of an entity after it has been
-   transformed for lookup.  */
-
-typedef int (*symbol_name_cmp_ftype) (const char *symbol_search_name,
-				      const char *lookup_name);
-
 /* Structure tying together assorted information about a language.  */
 
 struct language_defn
@@ -331,6 +321,7 @@ struct language_defn
     void (*la_collect_symbol_completion_matches)
       (completion_tracker &tracker,
        complete_symbol_mode mode,
+       symbol_name_match_type match_type,
        const char *text,
        const char *word,
        enum type_code code);
@@ -367,13 +358,18 @@ struct language_defn
     gdb::unique_xmalloc_ptr<char> (*la_watch_location_expression)
          (struct type *type, CORE_ADDR addr);
 
-    /* Return a pointer to the function that should be used to match
-       a symbol name against LOOKUP_NAME. This is mostly for languages
-       such as Ada where the matching algorithm depends on LOOKUP_NAME.
+    /* Return a pointer to the function that should be used to match a
+       symbol name against LOOKUP_NAME, according to this language's
+       rules.  The matching algorithm depends on LOOKUP_NAME.  For
+       example, on Ada, the matching algorithm depends on the symbol
+       name (wild/full/verbatim matching), and on whether we're doing
+       a normal lookup or a completion match lookup.
 
-       This field may be NULL, in which case strcmp_iw will be used
-       to perform the matching.  */
-    symbol_name_cmp_ftype (*la_get_symbol_name_cmp) (const char *lookup_name);
+       This field may be NULL, in which case
+       default_symbol_name_matcher is used to perform the
+       matching.  */
+    symbol_name_matcher_ftype *(*la_get_symbol_name_matcher)
+      (const lookup_name_info &);
 
     /* Find all symbols in the current program space matching NAME in
        DOMAIN, according to this language's rules.
@@ -389,7 +385,8 @@ struct language_defn
        special processing here, 'iterate_over_symbols' should be
        used as the definition.  */
     void (*la_iterate_over_symbols)
-      (const struct block *block, const char *name, domain_enum domain,
+      (const struct block *block, const lookup_name_info &name,
+       domain_enum domain,
        gdb::function_view<symbol_found_callback_ftype> callback);
 
     /* Hash the given symbol search name.  Use
@@ -627,6 +624,18 @@ extern unsigned int default_search_name_hash (const char *search_name);
 void c_get_string (struct value *value, gdb_byte **buffer, int *length,
 		   struct type **char_type, const char **charset);
 
+/* The default implementation of la_symbol_name_matcher.  Matches with
+   strncmp_iw.  */
+extern bool default_symbol_name_matcher
+  (const char *symbol_search_name,
+   const lookup_name_info &lookup_name,
+   completion_match *match);
+
+/* Get LANG's symbol_name_matcher method for LOOKUP_NAME.  Returns
+   default_symbol_name_matcher if not set.  */
+symbol_name_matcher_ftype *language_get_symbol_name_matcher
+  (const language_defn *lang, const lookup_name_info &lookup_name);
+
 /* The languages supported by GDB.  */
 
 extern const struct language_defn auto_language_defn;
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 19db83e..05218bd 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -334,7 +334,8 @@ typedef struct ls_parser linespec_parser;
 /* Prototypes for local functions.  */
 
 static void iterate_over_file_blocks
-  (struct symtab *symtab, const char *name, domain_enum domain,
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain,
    gdb::function_view<symbol_found_callback_ftype> callback);
 
 static void initialize_defaults (struct symtab **default_symtab,
@@ -369,6 +370,7 @@ static int symbol_to_sal (struct symtab_and_line *result,
 			  int funfirstline, struct symbol *sym);
 
 static void add_matching_symbols_to_info (const char *name,
+					  symbol_name_match_type name_match_type,
 					  struct collect_info *info,
 					  struct program_space *pspace);
 
@@ -1101,19 +1103,15 @@ maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
 
 static void
 iterate_over_all_matching_symtabs
-  (struct linespec_state *state, const char *name, const domain_enum domain,
+  (struct linespec_state *state,
+   const lookup_name_info &lookup_name,
+   const domain_enum name_domain,
    struct program_space *search_pspace, bool include_inline,
    gdb::function_view<symbol_found_callback_ftype> callback)
 {
   struct objfile *objfile;
   struct program_space *pspace;
 
-  /* The routine to be used for comparison.  */
-  symbol_name_cmp_ftype symbol_name_cmp
-    = (state->language->la_get_symbol_name_cmp != NULL
-       ? state->language->la_get_symbol_name_cmp (name)
-       : strcmp_iw);
-
   ALL_PSPACES (pspace)
   {
     if (search_pspace != NULL && search_pspace != pspace)
@@ -1128,21 +1126,17 @@ iterate_over_all_matching_symtabs
       struct compunit_symtab *cu;
 
       if (objfile->sf)
-	objfile->sf->qf->expand_symtabs_matching
-	  (objfile,
-	   NULL,
-	   [&] (const char *symbol_name)
-	   {
-	     return symbol_name_cmp (symbol_name, name) == 0;
-	   },
-	   NULL,
-	   ALL_DOMAIN);
+	objfile->sf->qf->expand_symtabs_matching (objfile,
+						  NULL,
+						  lookup_name,
+						  NULL, NULL,
+						  ALL_DOMAIN);
 
       ALL_OBJFILE_COMPUNITS (objfile, cu)
 	{
 	  struct symtab *symtab = COMPUNIT_FILETABS (cu);
 
-	  iterate_over_file_blocks (symtab, name, domain, callback);
+	  iterate_over_file_blocks (symtab, lookup_name, name_domain, callback);
 
 	  if (include_inline)
 	    {
@@ -1155,7 +1149,7 @@ iterate_over_all_matching_symtabs
 		{
 		  block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), i);
 		  state->language->la_iterate_over_symbols
-		    (block, name, domain, [&] (symbol *sym)
+		    (block, lookup_name, name_domain, [&] (symbol *sym)
 		     {
 		       /* Restrict calls to CALLBACK to symbols
 			  representing inline symbols only.  */
@@ -1192,8 +1186,8 @@ get_current_search_block (void)
 
 static void
 iterate_over_file_blocks
-  (struct symtab *symtab, const char *name, domain_enum domain,
-   gdb::function_view<symbol_found_callback_ftype> callback)
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain, gdb::function_view<symbol_found_callback_ftype> callback)
 {
   struct block *block;
 
@@ -1203,12 +1197,12 @@ iterate_over_file_blocks
     LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback);
 }
 
-/* A helper for find_method.  This finds all methods in type T which
-   match NAME.  It adds matching symbol names to RESULT_NAMES, and
-   adds T's direct superclasses to SUPERCLASSES.  */
+/* A helper for find_method.  This finds all methods in type T of
+   language T_LANG which match NAME.  It adds matching symbol names to
+   RESULT_NAMES, and adds T's direct superclasses to SUPERCLASSES.  */
 
 static void
-find_methods (struct type *t, const char *name,
+find_methods (struct type *t, enum language t_lang, const char *name,
 	      VEC (const_char_ptr) **result_names,
 	      VEC (typep) **superclasses)
 {
@@ -1221,6 +1215,9 @@ find_methods (struct type *t, const char *name,
   if (class_name)
     {
       int method_counter;
+      lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+      symbol_name_matcher_ftype *symbol_name_compare
+	= language_get_symbol_name_matcher (language_def (t_lang), lookup_name);
 
       t = check_typedef (t);
 
@@ -1245,7 +1242,7 @@ find_methods (struct type *t, const char *name,
 		method_name = dem_opname;
 	    }
 
-	  if (strcmp_iw (method_name, name) == 0)
+	  if (symbol_name_compare (method_name, lookup_name, NULL))
 	    {
 	      int field_counter;
 
@@ -2831,15 +2828,19 @@ linespec_complete_function (completion_tracker &tracker,
 			    const char *source_filename)
 {
   complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
+  symbol_name_match_type func_match_type = symbol_name_match_type::WILD;
 
   if (source_filename != NULL)
     {
-      collect_file_symbol_completion_matches (tracker, mode,
-					      function, function,
-					      source_filename);
+      collect_file_symbol_completion_matches (tracker, mode, func_match_type,
+					      function, function, source_filename);
     }
   else
-    collect_symbol_completion_matches (tracker, mode, function, function);
+    {
+      collect_symbol_completion_matches (tracker, mode, func_match_type,
+					 function, function);
+
+    }
 }
 
 /* Helper for complete_linespec to simplify it.  SOURCE_FILENAME is
@@ -3576,14 +3577,18 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
   struct symtab *elt;
   decode_compound_collector collector;
 
+  lookup_name_info lookup_name (class_name, symbol_name_match_type::FULL);
+
   for (ix = 0; VEC_iterate (symtab_ptr, file_symtabs, ix, elt); ++ix)
     {
       if (elt == NULL)
 	{
-	  iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN,
-					     NULL, false, collector);
-	  iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN,
-					     NULL, false, collector);
+	  iterate_over_all_matching_symtabs (state, lookup_name,
+					     STRUCT_DOMAIN, NULL, false,
+					     collector);
+	  iterate_over_all_matching_symtabs (state, lookup_name,
+					     VAR_DOMAIN, NULL, false,
+					     collector);
 	}
       else
 	{
@@ -3591,8 +3596,8 @@ lookup_prefix_sym (struct linespec_state *state, VEC (symtab_ptr) *file_symtabs,
 	     been filtered out earlier.  */
 	  gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
 	  set_current_program_space (SYMTAB_PSPACE (elt));
-	  iterate_over_file_blocks (elt, class_name, STRUCT_DOMAIN, collector);
-	  iterate_over_file_blocks (elt, class_name, VAR_DOMAIN, collector);
+	  iterate_over_file_blocks (elt, lookup_name, STRUCT_DOMAIN, collector);
+	  iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN, collector);
 	}
     }
 
@@ -3673,12 +3678,14 @@ add_all_symbol_names_from_pspace (struct collect_info *info,
   const char *iter;
 
   for (ix = 0; VEC_iterate (const_char_ptr, names, ix, iter); ++ix)
-    add_matching_symbols_to_info (iter, info, pspace);
+    add_matching_symbols_to_info (iter,
+				  symbol_name_match_type::FULL,
+				  info, pspace);
 }
 
 static void
 find_superclass_methods (VEC (typep) *superclasses,
-			 const char *name,
+			 const char *name, enum language name_lang,
 			 VEC (const_char_ptr) **result_names)
 {
   int old_len = VEC_length (const_char_ptr, *result_names);
@@ -3694,7 +3701,7 @@ find_superclass_methods (VEC (typep) *superclasses,
 
       make_cleanup (VEC_cleanup (typep), &new_supers);
       for (ix = 0; VEC_iterate (typep, iter_classes, ix, t); ++ix)
-	find_methods (t, name, result_names, &new_supers);
+	find_methods (t, name_lang, name, result_names, &new_supers);
 
       if (VEC_length (const_char_ptr, *result_names) != old_len
 	  || VEC_empty (typep, new_supers))
@@ -3761,7 +3768,8 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs,
       gdb_assert (!pspace->executing_startup);
       set_current_program_space (pspace);
       t = check_typedef (SYMBOL_TYPE (sym));
-      find_methods (t, method_name, &result_names, &superclass_vec);
+      find_methods (t, SYMBOL_LANGUAGE (sym),
+		    method_name, &result_names, &superclass_vec);
 
       /* Handle all items from a single program space at once; and be
 	 sure not to miss the last batch.  */
@@ -3774,7 +3782,7 @@ find_method (struct linespec_state *self, VEC (symtab_ptr) *file_symtabs,
 	     this program space, consider superclasses.  */
 	  if (VEC_length (const_char_ptr, result_names) == last_result_len)
 	    find_superclass_methods (superclass_vec, method_name,
-				     &result_names);
+				     SYMBOL_LANGUAGE (sym), &result_names);
 
 	  /* We have a list of candidate symbol names, so now we
 	     iterate over the symbol tables looking for all
@@ -3941,7 +3949,8 @@ find_function_symbols (struct linespec_state *state,
     add_all_symbol_names_from_pspace (&info, state->search_pspace,
 				      symbol_names);
   else
-    add_matching_symbols_to_info (name, &info, state->search_pspace);
+    add_matching_symbols_to_info (name, symbol_name_match_type::WILD,
+				  &info, state->search_pspace);
 
   do_cleanups (cleanup);
 
@@ -3968,28 +3977,10 @@ find_function_symbols (struct linespec_state *state,
 static void
 find_linespec_symbols (struct linespec_state *state,
 		       VEC (symtab_ptr) *file_symtabs,
-		       const char *name,
+		       const char *lookup_name,
 		       VEC (symbolp) **symbols,
 		       VEC (bound_minimal_symbol_d) **minsyms)
 {
-  demangle_result_storage demangle_storage;
-  std::string ada_lookup_storage;
-  const char *lookup_name;
-
-  if (state->language->la_language == language_ada)
-    {
-      /* In Ada, the symbol lookups are performed using the encoded
-         name rather than the demangled name.  */
-      ada_lookup_storage = ada_name_for_lookup (name);
-      lookup_name = ada_lookup_storage.c_str ();
-    }
-  else
-    {
-      lookup_name = demangle_for_lookup (name,
-					 state->language->la_language,
-					 demangle_storage);
-    }
-
   std::string canon = cp_canonicalize_string_no_typedefs (lookup_name);
   if (!canon.empty ())
     lookup_name = canon.c_str ();
@@ -4483,7 +4474,8 @@ add_minsym (struct minimal_symbol *minsym, void *d)
    restrict results to the given SYMTAB.  */
 
 static void
-search_minsyms_for_name (struct collect_info *info, const char *name,
+search_minsyms_for_name (struct collect_info *info,
+			 const lookup_name_info &name,
 			 struct program_space *search_pspace,
 			 struct symtab *symtab)
 {
@@ -4525,8 +4517,7 @@ search_minsyms_for_name (struct collect_info *info, const char *name,
 	{
 	  set_current_program_space (SYMTAB_PSPACE (symtab));
 	  local.objfile = SYMTAB_OBJFILE(symtab);
-	  iterate_over_minimal_symbols (local.objfile, name, add_minsym,
-					&local);
+	  iterate_over_minimal_symbols (local.objfile, name, add_minsym, &local);
 	}
     }
 
@@ -4568,20 +4559,24 @@ search_minsyms_for_name (struct collect_info *info, const char *name,
 
 static void
 add_matching_symbols_to_info (const char *name,
+			      symbol_name_match_type name_match_type,
 			      struct collect_info *info,
 			      struct program_space *pspace)
 {
   int ix;
   struct symtab *elt;
 
+  lookup_name_info lookup_name (name, name_match_type);
+
   for (ix = 0; VEC_iterate (symtab_ptr, info->file_symtabs, ix, elt); ++ix)
     {
       if (elt == NULL)
 	{
-	  iterate_over_all_matching_symtabs (info->state, name, VAR_DOMAIN,
+	  iterate_over_all_matching_symtabs (info->state, lookup_name,
+					     VAR_DOMAIN,
 					     pspace, true, [&] (symbol *sym)
 	    { return info->add_symbol (sym); });
-	  search_minsyms_for_name (info, name, pspace, NULL);
+	  search_minsyms_for_name (info, lookup_name, pspace, NULL);
 	}
       else if (pspace == NULL || pspace == SYMTAB_PSPACE (elt))
 	{
@@ -4591,7 +4586,8 @@ add_matching_symbols_to_info (const char *name,
 	     been filtered out earlier.  */
 	  gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
 	  set_current_program_space (SYMTAB_PSPACE (elt));
-	  iterate_over_file_blocks (elt, name, VAR_DOMAIN, [&] (symbol *sym)
+	  iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN,
+				    [&] (symbol *sym)
 	    { return info->add_symbol (sym); });
 
 	  /* If no new symbols were found in this iteration and this symtab
@@ -4600,7 +4596,7 @@ add_matching_symbols_to_info (const char *name,
 	     this case.  */
 	  if (prev_len == VEC_length (symbolp, info->result.symbols)
 	      && elt->language == language_asm)
-	    search_minsyms_for_name (info, name, pspace, elt);
+	    search_minsyms_for_name (info, lookup_name, pspace, elt);
 	}
     }
 }
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 4170ae9..09050c0 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -393,7 +393,7 @@ extern const struct language_defn m2_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index 37edbd8..a0d3bd5 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -51,6 +51,7 @@
 #include "language.h"
 #include "cli/cli-utils.h"
 #include "symbol.h"
+#include <algorithm>
 
 /* See minsyms.h.  */
 
@@ -131,15 +132,139 @@ add_minsym_to_hash_table (struct minimal_symbol *sym,
    TABLE.  */
 static void
 add_minsym_to_demangled_hash_table (struct minimal_symbol *sym,
-                                  struct minimal_symbol **table)
+				    struct objfile *objfile)
 {
   if (sym->demangled_hash_next == NULL)
     {
-      unsigned int hash = msymbol_hash_iw (MSYMBOL_SEARCH_NAME (sym))
-	% MINIMAL_SYMBOL_HASH_SIZE;
+      unsigned int hash = search_name_hash (MSYMBOL_LANGUAGE (sym),
+					    MSYMBOL_SEARCH_NAME (sym));
+
+      auto &vec = objfile->per_bfd->demangled_hash_languages;
+      auto it = std::lower_bound (vec.begin (), vec.end (),
+				  MSYMBOL_LANGUAGE (sym));
+      if (it == vec.end () || *it != MSYMBOL_LANGUAGE (sym))
+	vec.insert (it, MSYMBOL_LANGUAGE (sym));
+
+      struct minimal_symbol **table
+	= objfile->per_bfd->msymbol_demangled_hash;
+      unsigned int hash_index = hash % MINIMAL_SYMBOL_HASH_SIZE;
+      sym->demangled_hash_next = table[hash_index];
+      table[hash_index] = sym;
+    }
+}
 
-      sym->demangled_hash_next = table[hash];
-      table[hash] = sym;
+/* Worker object for lookup_minimal_symbol.  Stores temporary results
+   while walking the symbol tables.  */
+
+struct found_minimal_symbols
+{
+  /* External symbols are best.  */
+  bound_minimal_symbol external_symbol {};
+
+  /* File-local symbols are next best.  */
+  bound_minimal_symbol file_symbol {};
+
+  /* Symbols for shared library trampolines are next best.  */
+  bound_minimal_symbol trampoline_symbol {};
+
+  /* Called when a symbol name matches.  Check if the minsym is a
+     better type than what we had already found, and record it in one
+     of the members fields if so.  Returns true if we collected the
+     real symbol, in which case we can stop searching.  */
+  bool maybe_collect (const char *sfile, objfile *objf,
+		      minimal_symbol *msymbol);
+};
+
+/* See declaration above.  */
+
+bool
+found_minimal_symbols::maybe_collect (const char *sfile,
+				      struct objfile *objfile,
+				      minimal_symbol *msymbol)
+{
+  switch (MSYMBOL_TYPE (msymbol))
+    {
+    case mst_file_text:
+    case mst_file_data:
+    case mst_file_bss:
+      if (sfile == NULL
+	  || filename_cmp (msymbol->filename, sfile) == 0)
+	{
+	  file_symbol.minsym = msymbol;
+	  file_symbol.objfile = objfile;
+	}
+      break;
+
+    case mst_solib_trampoline:
+
+      /* If a trampoline symbol is found, we prefer to keep
+	 looking for the *real* symbol.  If the actual symbol
+	 is not found, then we'll use the trampoline
+	 entry.  */
+      if (trampoline_symbol.minsym == NULL)
+	{
+	  trampoline_symbol.minsym = msymbol;
+	  trampoline_symbol.objfile = objfile;
+	}
+      break;
+
+    case mst_unknown:
+    default:
+      external_symbol.minsym = msymbol;
+      external_symbol.objfile = objfile;
+      /* We have the real symbol.  No use looking further.  */
+      return true;
+    }
+
+  /* Keep looking.  */
+  return false;
+}
+
+/* Walk the mangled name hash table, and pass each symbol whose name
+   matches LOOKUP_NAME according to NAMECMP to FOUND.  */
+
+static void
+lookup_minimal_symbol_mangled (const char *lookup_name,
+			       const char *sfile,
+			       struct objfile *objfile,
+			       struct minimal_symbol **table,
+			       unsigned int hash,
+			       int (*namecmp) (const char *, const char *),
+			       found_minimal_symbols &found)
+{
+  for (minimal_symbol *msymbol = table[hash];
+       msymbol != NULL;
+       msymbol = msymbol->hash_next)
+    {
+      const char *symbol_name = MSYMBOL_LINKAGE_NAME (msymbol);
+
+      if (namecmp (symbol_name, lookup_name) == 0
+	  && found.maybe_collect (sfile, objfile, msymbol))
+	return;
+    }
+}
+
+/* Walk the demangled name hash table, and pass each symbol whose name
+   matches LOOKUP_NAME according to MATCHER to FOUND.  */
+
+static void
+lookup_minimal_symbol_demangled (const lookup_name_info &lookup_name,
+				 const char *sfile,
+				 struct objfile *objfile,
+				 struct minimal_symbol **table,
+				 unsigned int hash,
+				 symbol_name_matcher_ftype *matcher,
+				 found_minimal_symbols &found)
+{
+  for (minimal_symbol *msymbol = table[hash];
+       msymbol != NULL;
+       msymbol = msymbol->demangled_hash_next)
+    {
+      const char *symbol_name = MSYMBOL_SEARCH_NAME (msymbol);
+
+      if (matcher (symbol_name, lookup_name, NULL)
+	  && found.maybe_collect (sfile, objfile, msymbol))
+	return;
     }
 }
 
@@ -168,32 +293,22 @@ lookup_minimal_symbol (const char *name, const char *sfile,
 		       struct objfile *objf)
 {
   struct objfile *objfile;
-  struct bound_minimal_symbol found_symbol = { NULL, NULL };
-  struct bound_minimal_symbol found_file_symbol = { NULL, NULL };
-  struct bound_minimal_symbol trampoline_symbol = { NULL, NULL };
+  found_minimal_symbols found;
 
-  unsigned int hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  unsigned int dem_hash = msymbol_hash_iw (name) % MINIMAL_SYMBOL_HASH_SIZE;
+  unsigned int mangled_hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
 
-  const char *modified_name = name;
+  auto *mangled_cmp
+    = (case_sensitivity == case_sensitive_on
+       ? strcmp
+       : strcasecmp);
 
   if (sfile != NULL)
     sfile = lbasename (sfile);
 
-  /* For C++, canonicalize the input name.  */
-  std::string modified_name_storage;
-  if (current_language->la_language == language_cplus)
-    {
-      std::string cname = cp_canonicalize_string (name);
-      if (!cname.empty ())
-	{
-	  std::swap (modified_name_storage, cname);
-	  modified_name = modified_name_storage.c_str ();
-	}
-    }
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
   for (objfile = object_files;
-       objfile != NULL && found_symbol.minsym == NULL;
+       objfile != NULL && found.external_symbol.minsym == NULL;
        objfile = objfile->next)
     {
       struct minimal_symbol *msymbol;
@@ -201,131 +316,95 @@ lookup_minimal_symbol (const char *name, const char *sfile,
       if (objf == NULL || objf == objfile
 	  || objf == objfile->separate_debug_objfile_backlink)
 	{
+	  if (symbol_lookup_debug)
+	    {
+	      fprintf_unfiltered (gdb_stdlog,
+				  "lookup_minimal_symbol (%s, %s, %s)\n",
+				  name, sfile != NULL ? sfile : "NULL",
+				  objfile_debug_name (objfile));
+	    }
+
 	  /* Do two passes: the first over the ordinary hash table,
 	     and the second over the demangled hash table.  */
-        int pass;
-
-	if (symbol_lookup_debug)
-	  {
-	    fprintf_unfiltered (gdb_stdlog,
-				"lookup_minimal_symbol (%s, %s, %s)\n",
-				name, sfile != NULL ? sfile : "NULL",
-				objfile_debug_name (objfile));
-	  }
+	  lookup_minimal_symbol_mangled (name, sfile, objfile,
+					 objfile->per_bfd->msymbol_hash,
+					 mangled_hash, mangled_cmp, found);
 
-        for (pass = 1; pass <= 2 && found_symbol.minsym == NULL; pass++)
+	  /* If not found, try the demangled hash table.  */
+	  if (found.external_symbol.minsym == NULL)
 	    {
-            /* Select hash list according to pass.  */
-            if (pass == 1)
-              msymbol = objfile->per_bfd->msymbol_hash[hash];
-            else
-              msymbol = objfile->per_bfd->msymbol_demangled_hash[dem_hash];
-
-            while (msymbol != NULL && found_symbol.minsym == NULL)
+	      /* Once for each language in the demangled hash names
+		 table (usually just zero or one languages).  */
+	      for (auto lang : objfile->per_bfd->demangled_hash_languages)
 		{
-		  int match;
-
-		  if (pass == 1)
-		    {
-		      int (*cmp) (const char *, const char *);
-
-		      cmp = (case_sensitivity == case_sensitive_on
-		             ? strcmp : strcasecmp);
-		      match = cmp (MSYMBOL_LINKAGE_NAME (msymbol),
-				   modified_name) == 0;
-		    }
-		  else
-		    {
-		      /* The function respects CASE_SENSITIVITY.  */
-		      match = MSYMBOL_MATCHES_SEARCH_NAME (msymbol,
-							  modified_name);
-		    }
-
-		  if (match)
-		    {
-                    switch (MSYMBOL_TYPE (msymbol))
-                      {
-                      case mst_file_text:
-                      case mst_file_data:
-                      case mst_file_bss:
-                        if (sfile == NULL
-			    || filename_cmp (msymbol->filename, sfile) == 0)
-			  {
-			    found_file_symbol.minsym = msymbol;
-			    found_file_symbol.objfile = objfile;
-			  }
-                        break;
-
-                      case mst_solib_trampoline:
-
-                        /* If a trampoline symbol is found, we prefer to
-                           keep looking for the *real* symbol.  If the
-                           actual symbol is not found, then we'll use the
-                           trampoline entry.  */
-                        if (trampoline_symbol.minsym == NULL)
-			  {
-			    trampoline_symbol.minsym = msymbol;
-			    trampoline_symbol.objfile = objfile;
-			  }
-                        break;
-
-                      case mst_unknown:
-                      default:
-                        found_symbol.minsym = msymbol;
-			found_symbol.objfile = objfile;
-                        break;
-                      }
-		    }
-
-                /* Find the next symbol on the hash chain.  */
-                if (pass == 1)
-                  msymbol = msymbol->hash_next;
-                else
-                  msymbol = msymbol->demangled_hash_next;
+		  unsigned int hash
+		    = (lookup_name.search_name_hash (lang)
+		       % MINIMAL_SYMBOL_HASH_SIZE);
+
+		  symbol_name_matcher_ftype *match
+		    = language_get_symbol_name_matcher (language_def (lang),
+							lookup_name);
+		  struct minimal_symbol **msymbol_demangled_hash
+		    = objfile->per_bfd->msymbol_demangled_hash;
+
+		  lookup_minimal_symbol_demangled (lookup_name, sfile, objfile,
+						   msymbol_demangled_hash,
+						   hash, match, found);
+
+		  if (found.external_symbol.minsym != NULL)
+		    break;
 		}
 	    }
 	}
     }
 
   /* External symbols are best.  */
-  if (found_symbol.minsym != NULL)
+  if (found.external_symbol.minsym != NULL)
     {
       if (symbol_lookup_debug)
 	{
+	  minimal_symbol *minsym = found.external_symbol.minsym;
+
 	  fprintf_unfiltered (gdb_stdlog,
-			      "lookup_minimal_symbol (...) = %s"
-			      " (external)\n",
-			      host_address_to_string (found_symbol.minsym));
+			      "lookup_minimal_symbol (...) = %s (external)\n",
+			      host_address_to_string (minsym));
 	}
-      return found_symbol;
+      return found.external_symbol;
     }
 
   /* File-local symbols are next best.  */
-  if (found_file_symbol.minsym != NULL)
+  if (found.file_symbol.minsym != NULL)
     {
       if (symbol_lookup_debug)
 	{
+	  minimal_symbol *minsym = found.file_symbol.minsym;
+
 	  fprintf_unfiltered (gdb_stdlog,
-			      "lookup_minimal_symbol (...) = %s"
-			      " (file-local)\n",
-			      host_address_to_string
-			        (found_file_symbol.minsym));
+			      "lookup_minimal_symbol (...) = %s (file-local)\n",
+			      host_address_to_string (minsym));
 	}
-      return found_file_symbol;
+      return found.file_symbol;
     }
 
   /* Symbols for shared library trampolines are next best.  */
-  if (symbol_lookup_debug)
+  if (found.trampoline_symbol.minsym != NULL)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "lookup_minimal_symbol (...) = %s%s\n",
-			  trampoline_symbol.minsym != NULL
-			  ? host_address_to_string (trampoline_symbol.minsym)
-			  : "NULL",
-			  trampoline_symbol.minsym != NULL
-			  ? " (trampoline)" : "");
+      if (symbol_lookup_debug)
+	{
+	  minimal_symbol *minsym = found.trampoline_symbol.minsym;
+
+	  fprintf_unfiltered (gdb_stdlog,
+			      "lookup_minimal_symbol (...) = %s (trampoline)\n",
+			      host_address_to_string (minsym));
+	}
+
+      return found.trampoline_symbol;
     }
-  return trampoline_symbol;
+
+  /* Not found.  */
+  if (symbol_lookup_debug)
+    fprintf_unfiltered (gdb_stdlog, "lookup_minimal_symbol (...) = NULL\n");
+  return {};
 }
 
 /* See minsyms.h.  */
@@ -354,34 +433,47 @@ find_minimal_symbol_address (const char *name, CORE_ADDR *addr,
 /* See minsyms.h.  */
 
 void
-iterate_over_minimal_symbols (struct objfile *objf, const char *name,
+iterate_over_minimal_symbols (struct objfile *objf,
+			      const lookup_name_info &lookup_name,
 			      void (*callback) (struct minimal_symbol *,
 						void *),
 			      void *user_data)
 {
-  unsigned int hash;
-  struct minimal_symbol *iter;
-  int (*cmp) (const char *, const char *);
 
   /* The first pass is over the ordinary hash table.  */
-  hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  iter = objf->per_bfd->msymbol_hash[hash];
-  cmp = (case_sensitivity == case_sensitive_on ? strcmp : strcasecmp);
-  while (iter)
     {
-      if (cmp (MSYMBOL_LINKAGE_NAME (iter), name) == 0)
-	(*callback) (iter, user_data);
-      iter = iter->hash_next;
+      const char *name = lookup_name.name ().c_str ();
+      unsigned int hash = msymbol_hash (name) % MINIMAL_SYMBOL_HASH_SIZE;
+      auto *mangled_cmp
+	= (case_sensitivity == case_sensitive_on
+	   ? strcmp
+	   : strcasecmp);
+
+      for (minimal_symbol *iter = objf->per_bfd->msymbol_hash[hash];
+	   iter != NULL;
+	   iter = iter->hash_next)
+	{
+	  if (mangled_cmp (MSYMBOL_LINKAGE_NAME (iter), name) == 0)
+	    (*callback) (iter, user_data);
+	}
     }
 
-  /* The second pass is over the demangled table.  */
-  hash = msymbol_hash_iw (name) % MINIMAL_SYMBOL_HASH_SIZE;
-  iter = objf->per_bfd->msymbol_demangled_hash[hash];
-  while (iter)
+  /* The second pass is over the demangled table.  Once for each
+     language in the demangled hash names table (usually just zero or
+     one).  */
+  for (auto lang : objf->per_bfd->demangled_hash_languages)
     {
-      if (MSYMBOL_MATCHES_SEARCH_NAME (iter, name))
-	(*callback) (iter, user_data);
-      iter = iter->demangled_hash_next;
+      const language_defn *lang_def = language_def (lang);
+      symbol_name_matcher_ftype *name_match
+	= language_get_symbol_name_matcher (lang_def, lookup_name);
+
+      unsigned int hash
+	= lookup_name.search_name_hash (lang) % MINIMAL_SYMBOL_HASH_SIZE;
+      for (minimal_symbol *iter = objf->per_bfd->msymbol_demangled_hash[hash];
+	   iter != NULL;
+	   iter = iter->demangled_hash_next)
+	if (name_match (MSYMBOL_SEARCH_NAME (iter), lookup_name, NULL))
+	  (*callback) (iter, user_data);
     }
 }
 
@@ -1187,8 +1279,7 @@ build_minimal_symbol_hash_tables (struct objfile *objfile)
 
       msym->demangled_hash_next = 0;
       if (MSYMBOL_SEARCH_NAME (msym) != MSYMBOL_LINKAGE_NAME (msym))
-	add_minsym_to_demangled_hash_table (msym,
-                                            objfile->per_bfd->msymbol_demangled_hash);
+	add_minsym_to_demangled_hash_table (msym, objfile);
     }
 }
 
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index dc51725..f326be9 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -264,7 +264,7 @@ struct bound_minimal_symbol lookup_minimal_symbol_by_pc (CORE_ADDR);
    USER_DATA as arguments.  */
 
 void iterate_over_minimal_symbols (struct objfile *objf,
-				   const char *name,
+				   const lookup_name_info &name,
 				   void (*callback) (struct minimal_symbol *,
 						     void *),
 				   void *user_data);
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 804a486..5811733 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -405,7 +405,7 @@ extern const struct language_defn objc_language_defn = {
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 453166a..92137e4 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -27,6 +27,7 @@
 #include "progspace.h"
 #include "registry.h"
 #include "gdb_bfd.h"
+#include <vector>
 
 struct bcache;
 struct htab;
@@ -266,6 +267,12 @@ struct objfile_per_bfd_storage
      demangled names.  */
 
   minimal_symbol *msymbol_demangled_hash[MINIMAL_SYMBOL_HASH_SIZE] {};
+
+  /* All the different languages of symbols found in the demangled
+     hash table.  A flat/vector-based map is more efficient than a map
+     or hash table here, since this will only usually contain zero or
+     one entries.  */
+  std::vector<enum language> demangled_hash_languages;
 };
 
 /* Master structure for keeping track of each file from which
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index ffd4c92..1267760 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1081,7 +1081,7 @@ extern const struct language_defn opencl_language_defn =
   default_pass_by_reference,
   c_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 2dca923..e93c15b 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -454,7 +454,7 @@ extern const struct language_defn pascal_language_defn =
   default_pass_by_reference,
   default_get_string,
   c_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_compare_symbol_for_completion */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index f848990..d7881d2 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -46,7 +46,7 @@ static struct partial_symbol *match_partial_symbol (struct objfile *,
 						    struct partial_symtab *,
 						    int,
 						    const char *, domain_enum,
-						    symbol_compare_ftype *,
+						    symbol_name_match_type,
 						    symbol_compare_ftype *);
 
 static struct partial_symbol *lookup_partial_symbol (struct objfile *,
@@ -510,6 +510,8 @@ psym_lookup_symbol (struct objfile *objfile,
   const int psymtab_index = (block_index == GLOBAL_BLOCK ? 1 : 0);
   struct compunit_symtab *stab_best = NULL;
 
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
   {
     if (!ps->readin && lookup_partial_symbol (objfile, ps, name,
@@ -532,10 +534,10 @@ psym_lookup_symbol (struct objfile *objfile,
 	   information (but NAME might contain it).  */
 
 	if (sym != NULL
-	    && SYMBOL_MATCHES_SEARCH_NAME (sym, name))
+	    && SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	  return stab;
 	if (with_opaque != NULL
-	    && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, name))
+	    && SYMBOL_MATCHES_SEARCH_NAME (with_opaque, lookup_name))
 	  stab_best = stab;
 
 	/* Keep looking through other psymtabs.  */
@@ -545,6 +547,18 @@ psym_lookup_symbol (struct objfile *objfile,
   return stab_best;
 }
 
+/* Returns true if PSYM matches LOOKUP_NAME.  */
+
+static bool
+psymbol_name_matches (partial_symbol *psym,
+		      const lookup_name_info &lookup_name)
+{
+  const language_defn *lang = language_def (SYMBOL_LANGUAGE (psym));
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (lang, lookup_name);
+  return name_match (SYMBOL_SEARCH_NAME (psym), lookup_name, NULL);
+}
+
 /* Look in PST for a symbol in DOMAIN whose name matches NAME.  Search
    the global block of PST if GLOBAL, and otherwise the static block.
    MATCH is the comparison operation that returns true iff MATCH (s,
@@ -557,7 +571,7 @@ static struct partial_symbol *
 match_partial_symbol (struct objfile *objfile,
 		      struct partial_symtab *pst, int global,
 		      const char *name, domain_enum domain,
-		      symbol_compare_ftype *match,
+		      symbol_name_match_type match_type,
 		      symbol_compare_ftype *ordered_compare)
 {
   struct partial_symbol **start, **psym;
@@ -566,7 +580,10 @@ match_partial_symbol (struct objfile *objfile,
   int do_linear_search = 1;
 
   if (length == 0)
-      return NULL;
+    return NULL;
+
+  lookup_name_info lookup_name (name, match_type);
+
   start = (global ?
 	   &objfile->global_psymbols[pst->globals_offset] :
 	   &objfile->static_psymbols[pst->statics_offset]);
@@ -588,7 +605,12 @@ match_partial_symbol (struct objfile *objfile,
 	{
 	  center = bottom + (top - bottom) / 2;
 	  gdb_assert (center < top);
-	  if (ordered_compare (SYMBOL_SEARCH_NAME (*center), name) >= 0)
+
+	  enum language lang = SYMBOL_LANGUAGE (*center);
+	  const char *lang_ln
+	    = lookup_name.language_lookup_name (lang).c_str ();
+
+	  if (ordered_compare (SYMBOL_SEARCH_NAME (*center), lang_ln) >= 0)
 	    top = center;
 	  else
 	    bottom = center + 1;
@@ -596,7 +618,7 @@ match_partial_symbol (struct objfile *objfile,
       gdb_assert (top == bottom);
 
       while (top <= real_top
-	     && match (SYMBOL_SEARCH_NAME (*top), name) == 0)
+	     && psymbol_name_matches (*top, lookup_name))
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
 				     SYMBOL_DOMAIN (*top), domain))
@@ -614,7 +636,7 @@ match_partial_symbol (struct objfile *objfile,
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*psym),
 				     SYMBOL_DOMAIN (*psym), domain)
-	      && match (SYMBOL_SEARCH_NAME (*psym), name) == 0)
+	      && psymbol_name_matches (*psym, lookup_name))
 	    return *psym;
 	}
     }
@@ -669,6 +691,9 @@ lookup_partial_symbol (struct objfile *objfile,
     return NULL;
 
   gdb::unique_xmalloc_ptr<char> search_name = psymtab_search_name (name);
+
+  lookup_name_info lookup_name (search_name.get (), symbol_name_match_type::FULL);
+
   start = (global ?
 	   &objfile->global_psymbols[pst->globals_offset] :
 	   &objfile->static_psymbols[pst->statics_offset]);
@@ -708,15 +733,13 @@ lookup_partial_symbol (struct objfile *objfile,
 
       /* For `case_sensitivity == case_sensitive_off' strcmp_iw_ordered will
 	 search more exactly than what matches SYMBOL_MATCHES_SEARCH_NAME.  */
-      while (top >= start && SYMBOL_MATCHES_SEARCH_NAME (*top,
-							 search_name.get ()))
+      while (top >= start && SYMBOL_MATCHES_SEARCH_NAME (*top, lookup_name))
 	top--;
 
       /* Fixup to have a symbol which matches SYMBOL_MATCHES_SEARCH_NAME.  */
       top++;
 
-      while (top <= real_top
-	     && SYMBOL_MATCHES_SEARCH_NAME (*top, search_name.get ()))
+      while (top <= real_top && SYMBOL_MATCHES_SEARCH_NAME (*top, lookup_name))
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*top),
 				     SYMBOL_DOMAIN (*top), domain))
@@ -734,7 +757,7 @@ lookup_partial_symbol (struct objfile *objfile,
 	{
 	  if (symbol_matches_domain (SYMBOL_LANGUAGE (*psym),
 				     SYMBOL_DOMAIN (*psym), domain)
-	      && SYMBOL_MATCHES_SEARCH_NAME (*psym, search_name.get ()))
+	      && SYMBOL_MATCHES_SEARCH_NAME (*psym, lookup_name))
 	    return *psym;
 	}
     }
@@ -1213,13 +1236,16 @@ static int
 map_block (const char *name, domain_enum domain, struct objfile *objfile,
 	   struct block *block,
 	   int (*callback) (struct block *, struct symbol *, void *),
-	   void *data, symbol_compare_ftype *match)
+	   void *data, symbol_name_match_type match)
 {
   struct block_iterator iter;
   struct symbol *sym;
 
-  for (sym = block_iter_match_first (block, name, match, &iter);
-       sym != NULL; sym = block_iter_match_next (name, match, &iter))
+  lookup_name_info lookup_name (name, match);
+
+  for (sym = block_iter_match_first (block, lookup_name, &iter);
+       sym != NULL;
+       sym = block_iter_match_next (lookup_name, &iter))
     {
       if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				 SYMBOL_DOMAIN (sym), domain))
@@ -1242,7 +1268,7 @@ psym_map_matching_symbols (struct objfile *objfile,
 			   int (*callback) (struct block *,
 					    struct symbol *, void *),
 			   void *data,
-			   symbol_compare_ftype *match,
+			   symbol_name_match_type match,
 			   symbol_compare_ftype *ordered_compare)
 {
   const int block_kind = global ? GLOBAL_BLOCK : STATIC_BLOCK;
@@ -1277,7 +1303,8 @@ psym_map_matching_symbols (struct objfile *objfile,
 
 static bool
 recursively_search_psymtabs
-  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain kind,
+  (struct partial_symtab *ps, struct objfile *objfile, enum search_domain domain,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> sym_matcher)
 {
   int keep_going = 1;
@@ -1298,7 +1325,8 @@ recursively_search_psymtabs
 	continue;
 
       r = recursively_search_psymtabs (ps->dependencies[i],
-				       objfile, kind, sym_matcher);
+				       objfile, domain, lookup_name,
+				       sym_matcher);
       if (r != 0)
 	{
 	  ps->searched_flag = PST_SEARCHED_AND_FOUND;
@@ -1332,15 +1360,16 @@ recursively_search_psymtabs
 	{
 	  QUIT;
 
-	  if ((kind == ALL_DOMAIN
-	       || (kind == VARIABLES_DOMAIN
+	  if ((domain == ALL_DOMAIN
+	       || (domain == VARIABLES_DOMAIN
 		   && PSYMBOL_CLASS (*psym) != LOC_TYPEDEF
 		   && PSYMBOL_CLASS (*psym) != LOC_BLOCK)
-	       || (kind == FUNCTIONS_DOMAIN
+	       || (domain == FUNCTIONS_DOMAIN
 		   && PSYMBOL_CLASS (*psym) == LOC_BLOCK)
-	       || (kind == TYPES_DOMAIN
+	       || (domain == TYPES_DOMAIN
 		   && PSYMBOL_CLASS (*psym) == LOC_TYPEDEF))
-	      && sym_matcher (SYMBOL_SEARCH_NAME (*psym)))
+	      && psymbol_name_matches (*psym, lookup_name)
+	      && (sym_matcher == NULL || sym_matcher (SYMBOL_SEARCH_NAME (*psym))))
 	    {
 	      /* Found a match, so notify our caller.  */
 	      result = PST_SEARCHED_AND_FOUND;
@@ -1361,9 +1390,10 @@ static void
 psym_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
-   enum search_domain kind)
+   enum search_domain domain)
 {
   struct partial_symtab *ps;
 
@@ -1405,7 +1435,8 @@ psym_expand_symtabs_matching
 	    continue;
 	}
 
-      if (recursively_search_psymtabs (ps, objfile, kind, symbol_matcher))
+      if (recursively_search_psymtabs (ps, objfile, domain,
+				       lookup_name, symbol_matcher))
 	{
 	  struct compunit_symtab *symtab =
 	    psymtab_to_symtab (objfile, ps);
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 466eb20..c1b2d6e 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2250,7 +2250,7 @@ extern const struct language_defn rust_language_defn =
   default_pass_by_reference,
   c_get_string,
   rust_watch_location_expression,
-  NULL,				/* la_get_symbol_name_cmp */
+  NULL,				/* la_get_symbol_name_matcher */
   iterate_over_symbols,
   default_search_name_hash,
   &default_varobj_ops,
diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
index 32dafa8..ff583ae 100644
--- a/gdb/symfile-debug.c
+++ b/gdb/symfile-debug.c
@@ -260,7 +260,7 @@ debug_qf_map_matching_symbols (struct objfile *objfile,
 			       int (*callback) (struct block *,
 						struct symbol *, void *),
 			       void *data,
-			       symbol_compare_ftype *match,
+			       symbol_name_match_type match,
 			       symbol_compare_ftype *ordered_compare)
 {
   const struct debug_sym_fns_data *debug_data
@@ -273,7 +273,7 @@ debug_qf_map_matching_symbols (struct objfile *objfile,
 		    domain_name (domain), global,
 		    host_address_to_string (callback),
 		    host_address_to_string (data),
-		    host_address_to_string (match),
+		    plongest ((LONGEST) match),
 		    host_address_to_string (ordered_compare));
 
   debug_data->real_sf->qf->map_matching_symbols (objfile, name,
@@ -287,6 +287,7 @@ static void
 debug_qf_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -305,6 +306,7 @@ debug_qf_expand_symtabs_matching
 
   debug_data->real_sf->qf->expand_symtabs_matching (objfile,
 						    file_matcher,
+						    lookup_name,
 						    symbol_matcher,
 						    expansion_notify,
 						    kind);
diff --git a/gdb/symfile.c b/gdb/symfile.c
index a7d8553b..c43696a 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -3796,6 +3796,7 @@ symfile_free_objfile (struct objfile *objfile)
 void
 expand_symtabs_matching
   (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind)
@@ -3806,6 +3807,7 @@ expand_symtabs_matching
   {
     if (objfile->sf)
       objfile->sf->qf->expand_symtabs_matching (objfile, file_matcher,
+						lookup_name,
 						symbol_matcher,
 						expansion_notify, kind);
   }
diff --git a/gdb/symfile.h b/gdb/symfile.h
index 14f48f3..3472aa0 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -231,7 +231,7 @@ struct quick_symbol_functions
 				int (*callback) (struct block *,
 						 struct symbol *, void *),
 				void *data,
-				symbol_compare_ftype *match,
+				symbol_name_match_type match,
 				symbol_compare_ftype *ordered_compare);
 
   /* Expand all symbol tables in OBJFILE matching some criteria.
@@ -255,6 +255,7 @@ struct quick_symbol_functions
   void (*expand_symtabs_matching)
     (struct objfile *objfile,
      gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+     const lookup_name_info &lookup_name,
      gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
      gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
      enum search_domain kind);
@@ -526,6 +527,7 @@ extern scoped_restore_tmpl<int> increment_reading_symtab (void);
 
 void expand_symtabs_matching
   (gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain kind);
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index ed2e8d2..96aa30f 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -949,6 +949,7 @@ maintenance_expand_symtabs (const char *args, int from_tty)
 	       return (!basenames
 		       && (regexp == NULL || re_exec (filename)));
 	     },
+	     lookup_name_info::match_any (),
 	     [] (const char *symname)
 	     {
 	       /* Since we're not searching on symbols, just return true.  */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 035a958..d764f67 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -946,6 +946,19 @@ symbol_search_name (const struct general_symbol_info *gsymbol)
   else
     return symbol_natural_name (gsymbol);
 }
+
+/* See symtab.h.  */
+
+bool
+symbol_matches_search_name (const struct general_symbol_info *gsymbol,
+			    const lookup_name_info &name)
+{
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (language_def (gsymbol->language),
+					name);
+  return name_match (symbol_search_name (gsymbol), name, NULL);
+}
+
 \f
 
 /* Return 1 if the two sections are the same, or if they could
@@ -1103,11 +1116,12 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
     }
   else if (slot_name != NULL && name != NULL)
     {
-      /* It's important that we use the same comparison that was done the
-	 first time through.  If the slot records a found symbol, then this
-	 means using strcmp_iw on SYMBOL_SEARCH_NAME.  See dictionary.c.
-	 It also means using symbol_matches_domain for found symbols.
-	 See block.c.
+      /* It's important that we use the same comparison that was done
+	 the first time through.  If the slot records a found symbol,
+	 then this means using the symbol name comparison function of
+	 the symbol's language with SYMBOL_SEARCH_NAME.  See
+	 dictionary.c.  It also means using symbol_matches_domain for
+	 found symbols.  See block.c.
 
 	 If the slot records a not-found symbol, then require a precise match.
 	 We could still be lax with whitespace like strcmp_iw though.  */
@@ -1122,9 +1136,11 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
       else
 	{
 	  struct symbol *sym = slot->value.found.symbol;
+	  lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
 
-	  if (strcmp_iw (slot_name, name) != 0)
+	  if (!SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
 	    return 0;
+
 	  if (!symbol_matches_domain (SYMBOL_LANGUAGE (sym),
 				      slot_domain, domain))
 	    return 0;
@@ -1743,6 +1759,30 @@ fixup_symbol_section (struct symbol *sym, struct objfile *objfile)
   return sym;
 }
 
+/* See symtab.h.  */
+
+demangle_for_lookup_info::demangle_for_lookup_info
+  (const lookup_name_info &lookup_name, language lang)
+{
+  demangle_result_storage storage;
+
+  m_demangled_name = demangle_for_lookup (lookup_name.name ().c_str (),
+					  lang, storage);
+}
+
+/* See symtab.h.  */
+
+const lookup_name_info &
+lookup_name_info::match_any ()
+{
+  /* Lookup any symbol that "" would complete.  I.e., this matches all
+     symbol names.  */
+  static const lookup_name_info lookup_name ({}, symbol_name_match_type::FULL,
+					     true);
+
+  return lookup_name;
+}
+
 /* Compute the demangled form of NAME as used by the various symbol
    lookup functions.  The result can either be the input NAME
    directly, or a pointer to a buffer owned by the STORAGE object.
@@ -2767,7 +2807,8 @@ basic_lookup_transparent_type (const char *name)
    search continues.  */
 
 void
-iterate_over_symbols (const struct block *block, const char *name,
+iterate_over_symbols (const struct block *block,
+		      const lookup_name_info &name,
 		      const domain_enum domain,
 		      gdb::function_view<symbol_found_callback_ftype> callback)
 {
@@ -4231,6 +4272,7 @@ search_symbols (const char *regexp, enum search_domain kind,
 			     return file_matches (filename, files, nfiles,
 						  basenames);
 			   },
+			   lookup_name_info::match_any (),
 			   [&] (const char *symname)
 			   {
 			     return (!preg || preg->exec (symname,
@@ -4619,13 +4661,33 @@ rbreak_command (char *regexp, int from_tty)
    information.  */
 
 static int
-compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
-{
-  int (*ncmp) (const char *, const char *, size_t);
+compare_symbol_name (const char *name,
+		     language symbol_language,
+		     const lookup_name_info &lookup_name,
+		     const char *sym_text, int sym_text_len,
+		     completion_match_result &match_res)
+{
+  const language_defn *lang;
+
+  /* If we're completing for an expression and the symbol doesn't have
+     an explicit language set, fallback to the current language.  Ada
+     minimal symbols won't have their language set to Ada, for
+     example, and if we compared using the default/C-like matcher,
+     then when completing e.g., symbols in a package named "pck", we'd
+     match internal Ada symbols like "pckS", which are invalid in an
+     Ada expression, unless you wrap them in '<' '>' to request a
+     verbatim match.  */
+  if (symbol_language == language_auto
+      && lookup_name.match_type () == symbol_name_match_type::EXPRESSION)
+    lang = current_language;
+  else
+    lang = language_def (symbol_language);
 
-  ncmp = (case_sensitivity == case_sensitive_on ? strncmp : strncasecmp);
+  symbol_name_matcher_ftype *name_match
+    = language_get_symbol_name_matcher (lang, lookup_name);
 
-  if (ncmp (name, sym_text, sym_text_len) != 0)
+  /* Clip symbols that cannot match.  */
+  if (!name_match (name, lookup_name, &match_res.match))
     return 0;
 
   if (sym_text[sym_text_len] == '(')
@@ -4643,20 +4705,32 @@ compare_symbol_name (const char *name, const char *sym_text, int sym_text_len)
   return 1;
 }
 
-/*  Test to see if the symbol specified by SYMNAME (which is already
-   demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
-   characters.  If so, add it to the current completion list.  */
+/*  See symtab.h.  */
 
-static void
+void
 completion_list_add_name (completion_tracker &tracker,
+			  language symbol_language,
 			  const char *symname,
+			  const lookup_name_info &lookup_name,
 			  const char *sym_text, int sym_text_len,
 			  const char *text, const char *word)
 {
+  completion_match_result &match_res
+    = tracker.reset_completion_match_result ();
+
   /* Clip symbols that cannot match.  */
-  if (!compare_symbol_name (symname, sym_text, sym_text_len))
+  if (!compare_symbol_name (symname, symbol_language,
+			    lookup_name,
+			    sym_text, sym_text_len,
+			    match_res))
     return;
 
+  /* Refresh SYMNAME from the match string.  It's potentially
+     different depending on language.  (E.g., on Ada, the match may be
+     the encoded symbol name wrapped in "<>").  */
+  symname = match_res.match.match ();
+  gdb_assert (symname != NULL);
+
   /* We have a match for a completion, so add SYMNAME to the current list
      of matches.  Note that the name is moved to freshly malloc'd space.  */
 
@@ -4694,11 +4768,13 @@ completion_list_add_name (completion_tracker &tracker,
 static void
 completion_list_add_symbol (completion_tracker &tracker,
 			    symbol *sym,
+			    const lookup_name_info &lookup_name,
 			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
-  completion_list_add_name (tracker, SYMBOL_NATURAL_NAME (sym),
-			    sym_text, sym_text_len, text, word);
+  completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+			    SYMBOL_NATURAL_NAME (sym),
+			    lookup_name, sym_text, sym_text_len, text, word);
 }
 
 /* completion_list_add_name wrapper for struct minimal_symbol.  */
@@ -4706,19 +4782,23 @@ completion_list_add_symbol (completion_tracker &tracker,
 static void
 completion_list_add_msymbol (completion_tracker &tracker,
 			     minimal_symbol *sym,
+			     const lookup_name_info &lookup_name,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
-  completion_list_add_name (tracker, MSYMBOL_NATURAL_NAME (sym),
-			    sym_text, sym_text_len, text, word);
+  completion_list_add_name (tracker, MSYMBOL_LANGUAGE (sym),
+			    MSYMBOL_NATURAL_NAME (sym),
+			    lookup_name, sym_text, sym_text_len, text, word);
 }
 
+
 /* ObjC: In case we are completing on a selector, look as the msymbol
    again and feed all the selectors into the mill.  */
 
 static void
 completion_list_objc_symbol (completion_tracker &tracker,
 			     struct minimal_symbol *msymbol,
+			     const lookup_name_info &lookup_name,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
@@ -4736,7 +4816,9 @@ completion_list_objc_symbol (completion_tracker &tracker,
 
   if (sym_text[0] == '[')
     /* Complete on shortened method method.  */
-    completion_list_add_name (tracker, method + 1,
+    completion_list_add_name (tracker, language_objc,
+			      method + 1,
+			      lookup_name,
 			      sym_text, sym_text_len, text, word);
 
   while ((strlen (method) + 1) >= tmplen)
@@ -4758,10 +4840,12 @@ completion_list_objc_symbol (completion_tracker &tracker,
       memcpy (tmp, method, (category - method));
       tmp[category - method] = ' ';
       memcpy (tmp + (category - method) + 1, selector, strlen (selector) + 1);
-      completion_list_add_name (tracker, tmp,
+      completion_list_add_name (tracker, language_objc, tmp,
+				lookup_name,
 				sym_text, sym_text_len, text, word);
       if (sym_text[0] == '[')
-	completion_list_add_name (tracker, tmp + 1,
+	completion_list_add_name (tracker, language_objc, tmp + 1,
+				  lookup_name,
 				  sym_text, sym_text_len, text, word);
     }
 
@@ -4773,7 +4857,8 @@ completion_list_objc_symbol (completion_tracker &tracker,
       if (tmp2 != NULL)
 	*tmp2 = '\0';
 
-      completion_list_add_name (tracker, tmp,
+      completion_list_add_name (tracker, language_objc, tmp,
+				lookup_name,
 				sym_text, sym_text_len, text, word);
     }
 }
@@ -4827,6 +4912,7 @@ language_search_unquoted_string (const char *text, const char *p)
 static void
 completion_list_add_fields (completion_tracker &tracker,
 			    struct symbol *sym,
+			    const lookup_name_info &lookup_name,
 			    const char *sym_text, int sym_text_len,
 			    const char *text, const char *word)
 {
@@ -4839,7 +4925,9 @@ completion_list_add_fields (completion_tracker &tracker,
       if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
 	for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
 	  if (TYPE_FIELD_NAME (t, j))
-	    completion_list_add_name (tracker, TYPE_FIELD_NAME (t, j),
+	    completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+				      TYPE_FIELD_NAME (t, j),
+				      lookup_name,
 				      sym_text, sym_text_len, text, word);
     }
 }
@@ -4849,6 +4937,7 @@ completion_list_add_fields (completion_tracker &tracker,
 static void
 add_symtab_completions (struct compunit_symtab *cust,
 			completion_tracker &tracker,
+			const lookup_name_info &lookup_name,
 			const char *sym_text, int sym_text_len,
 			const char *text, const char *word,
 			enum type_code code)
@@ -4871,6 +4960,7 @@ add_symtab_completions (struct compunit_symtab *cust,
 	      || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
 	    completion_list_add_symbol (tracker, sym,
+					lookup_name,
 					sym_text, sym_text_len,
 					text, word);
 	}
@@ -4879,8 +4969,8 @@ add_symtab_completions (struct compunit_symtab *cust,
 
 void
 default_collect_symbol_completion_matches_break_on
-  (completion_tracker &tracker,
-   complete_symbol_mode mode,
+  (completion_tracker &tracker, complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
    const char *text, const char *word,
    const char *break_on, enum type_code code)
 {
@@ -4971,6 +5061,9 @@ default_collect_symbol_completion_matches_break_on
     }
   gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
 
+  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
+				name_match_type, true);
+
   /* At this point scan through the misc symbol vectors and add each
      symbol you find to the list.  Eventually we want to ignore
      anything that isn't a text symbol (everything else will be
@@ -4982,34 +5075,30 @@ default_collect_symbol_completion_matches_break_on
 	{
 	  QUIT;
 
-	  completion_list_add_msymbol (tracker,
-				       msymbol, sym_text, sym_text_len,
+	  completion_list_add_msymbol (tracker, msymbol, lookup_name,
+				       sym_text, sym_text_len,
 				       text, word);
 
-	  completion_list_objc_symbol (tracker,
-				       msymbol, sym_text, sym_text_len,
-				       text, word);
+	  completion_list_objc_symbol (tracker, msymbol, lookup_name,
+				       sym_text, sym_text_len, text,
+				       word);
 	}
     }
 
   /* Add completions for all currently loaded symbol tables.  */
   ALL_COMPUNITS (objfile, cust)
-    add_symtab_completions (cust, tracker,
+    add_symtab_completions (cust, tracker, lookup_name,
 			    sym_text, sym_text_len, text, word, code);
 
   /* Look through the partial symtabs for all symbols which begin by
      matching SYM_TEXT.  Expand all CUs that you find to the list.  */
   expand_symtabs_matching (NULL,
-			   [&] (const char *name) /* symbol matcher */
-			     {
-			       return compare_symbol_name (name,
-							   sym_text,
-							   sym_text_len);
-			     },
+			   lookup_name,
+			   NULL,
 			   [&] (compunit_symtab *symtab) /* expansion notify */
 			     {
 			       add_symtab_completions (symtab,
-						       tracker,
+						       tracker, lookup_name,
 						       sym_text, sym_text_len,
 						       text, word, code);
 			     },
@@ -5032,16 +5121,16 @@ default_collect_symbol_completion_matches_break_on
 	  {
 	    if (code == TYPE_CODE_UNDEF)
 	      {
-		completion_list_add_symbol (tracker, sym,
+		completion_list_add_symbol (tracker, sym, lookup_name,
 					    sym_text, sym_text_len, text,
 					    word);
-		completion_list_add_fields (tracker, sym,
+		completion_list_add_fields (tracker, sym, lookup_name,
 					    sym_text, sym_text_len, text,
 					    word);
 	      }
 	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
-	      completion_list_add_symbol (tracker, sym,
+	      completion_list_add_symbol (tracker, sym, lookup_name,
 					  sym_text, sym_text_len, text,
 					  word);
 	  }
@@ -5060,12 +5149,12 @@ default_collect_symbol_completion_matches_break_on
     {
       if (surrounding_static_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
-	  completion_list_add_fields (tracker, sym,
+	  completion_list_add_fields (tracker, sym, lookup_name,
 				      sym_text, sym_text_len, text, word);
 
       if (surrounding_global_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
-	  completion_list_add_fields (tracker, sym,
+	  completion_list_add_fields (tracker, sym, lookup_name,
 				      sym_text, sym_text_len, text, word);
     }
 
@@ -5082,7 +5171,10 @@ default_collect_symbol_completion_matches_break_on
 				 macro_source_file *,
 				 int)
 	{
-	  completion_list_add_name (tracker, macro_name,
+	  completion_list_add_name (tracker,
+				    language_c,
+				    macro_name,
+				    lookup_name,
 				    sym_text, sym_text_len,
 				    text, word);
 	};
@@ -5110,10 +5202,12 @@ default_collect_symbol_completion_matches_break_on
 void
 default_collect_symbol_completion_matches (completion_tracker &tracker,
 					   complete_symbol_mode mode,
+					   symbol_name_match_type name_match_type,
 					   const char *text, const char *word,
 					   enum type_code code)
 {
   return default_collect_symbol_completion_matches_break_on (tracker, mode,
+							     name_match_type,
 							     text, word, "",
 							     code);
 }
@@ -5124,9 +5218,11 @@ default_collect_symbol_completion_matches (completion_tracker &tracker,
 void
 collect_symbol_completion_matches (completion_tracker &tracker,
 				   complete_symbol_mode mode,
+				   symbol_name_match_type name_match_type,
 				   const char *text, const char *word)
 {
   current_language->la_collect_symbol_completion_matches (tracker, mode,
+							  name_match_type,
 							  text, word,
 							  TYPE_CODE_UNDEF);
 }
@@ -5140,11 +5236,13 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 					enum type_code code)
 {
   complete_symbol_mode mode = complete_symbol_mode::EXPRESSION;
+  symbol_name_match_type name_match_type = symbol_name_match_type::EXPRESSION;
 
   gdb_assert (code == TYPE_CODE_UNION
 	      || code == TYPE_CODE_STRUCT
 	      || code == TYPE_CODE_ENUM);
   current_language->la_collect_symbol_completion_matches (tracker, mode,
+							  name_match_type,
 							  text, word, code);
 }
 
@@ -5154,6 +5252,7 @@ collect_symbol_completion_matches_type (completion_tracker &tracker,
 void
 collect_file_symbol_completion_matches (completion_tracker &tracker,
 					complete_symbol_mode mode,
+					symbol_name_match_type name_match_type,
 					const char *text, const char *word,
 					const char *srcfile)
 {
@@ -5210,12 +5309,15 @@ collect_file_symbol_completion_matches (completion_tracker &tracker,
 
   sym_text_len = strlen (sym_text);
 
+  lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
+				name_match_type, true);
+
   /* Go through symtabs for SRCFILE and check the externs and statics
      for symbols which match.  */
   iterate_over_symtabs (srcfile, [&] (symtab *s)
     {
       add_symtab_completions (SYMTAB_COMPUNIT (s),
-			      tracker,
+			      tracker, lookup_name,
 			      sym_text, sym_text_len,
 			      text, word, TYPE_CODE_UNDEF);
       return false;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index d5c929d..5dfe953 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -21,10 +21,12 @@
 #define SYMTAB_H 1
 
 #include <vector>
+#include <string>
 #include "gdb_vecs.h"
 #include "gdbtypes.h"
 #include "common/enum-flags.h"
 #include "common/function-view.h"
+#include "common/gdb_optional.h"
 #include "completer.h"
 
 /* Opaque declarations.  */
@@ -43,6 +45,251 @@ struct probe;
 struct common_block;
 struct obj_section;
 struct cmd_list_element;
+struct lookup_name_info;
+
+/* How to match a lookup name against a symbol search name.  */
+enum class symbol_name_match_type
+{
+  /* Wild matching.  Matches unqualified symbol names in all
+     namespace/module/packages, etc.  */
+  WILD,
+
+  /* Full matching.  The lookup name indicates a fully-qualified name,
+     and only matches symbol search names in the specified
+     namespace/module/package.  */
+  FULL,
+
+  /* Expression matching.  The same as FULL matching in most
+     languages.  The same as WILD matching in Ada.  */
+  EXPRESSION,
+};
+
+/* Hash the given symbol search name according to LANGUAGE's
+   rules.  */
+extern unsigned int search_name_hash (enum language language,
+				      const char *search_name);
+
+/* Ada-specific bits of a lookup_name_info object.  This is lazily
+   constructed on demand.  */
+
+class ada_lookup_name_info final
+{
+ public:
+  /* Construct.  */
+  explicit ada_lookup_name_info (const lookup_name_info &lookup_name);
+
+  /* Compare SYMBOL_SEARCH_NAME with our lookup name, using MATCH_TYPE
+     as name match type.  Returns true if there's a match, false
+     otherwise.  If non-NULL, store the matching results in MATCH.  */
+  bool matches (const char *symbol_search_name,
+		symbol_name_match_type match_type,
+		completion_match *match) const;
+
+  /* The Ada-encoded lookup name.  */
+  const std::string &lookup_name () const
+  { return m_encoded_name; }
+
+  /* Return true if we're supposed to be doing a wild match look
+     up.  */
+  bool wild_match_p () const
+  { return m_wild_match_p; }
+
+  /* Return true if we're looking up a name inside package
+     Standard.  */
+  bool standard_p () const
+  { return m_standard_p; }
+
+ private:
+  /* The Ada-encoded lookup name.  */
+  std::string m_encoded_name;
+
+  /* Whether the user-provided lookup name was Ada encoded.  If so,
+     then return encoded names in the 'matches' method's 'completion
+     match result' output.  */
+  bool m_encoded_p : 1;
+
+  /* True if really doing wild matching.  Even if the user requests
+     wild matching, some cases require full matching.  */
+  bool m_wild_match_p : 1;
+
+  /* True if doing a verbatim match.  This is true if the decoded
+     version of the symbol name is wrapped in '<'/'>'.  This is an
+     escape hatch users can use to look up symbols the Ada encoding
+     does not understand.  */
+  bool m_verbatim_p : 1;
+
+   /* True if the user specified a symbol name that is inside package
+      Standard.  Symbol names inside package Standard are handled
+      specially.  We always do a non-wild match of the symbol name
+      without the "standard__" prefix, and only search static and
+      global symbols.  This was primarily introduced in order to allow
+      the user to specifically access the standard exceptions using,
+      for instance, Standard.Constraint_Error when Constraint_Error is
+      ambiguous (due to the user defining its own Constraint_Error
+      entity inside its program).  */
+  bool m_standard_p : 1;
+};
+
+/* Language-specific bits of a lookup_name_info object, for languages
+   that do name searching using demangled names (C++/D/Go).  This is
+   lazily constructed on demand.  */
+
+struct demangle_for_lookup_info final
+{
+public:
+  demangle_for_lookup_info (const lookup_name_info &lookup_name,
+			    language lang);
+
+  /* The demangled lookup name.  */
+  const std::string &lookup_name () const
+  { return m_demangled_name; }
+
+private:
+  /* The demangled lookup name.  */
+  std::string m_demangled_name;
+};
+
+/* Object that aggregates all information related to a symbol lookup
+   name.  I.e., the name that is matched against the symbol's search
+   name.  Caches per-language information so that it doesn't require
+   recomputing it for every symbol comparison, like for example the
+   Ada encoded name and the symbol's name hash for a given language.
+   The object is conceptually immutable once constructed, and thus has
+   no setters.  This is to prevent some code path from tweaking some
+   property of the lookup name for some local reason and accidentally
+   altering the results of any continuing search(es).
+   lookup_name_info objects are generally passed around as a const
+   reference to reinforce that.  (They're not passed around by value
+   because they're not small.)  */
+class lookup_name_info final
+{
+ public:
+  /* Create a new object.  */
+  lookup_name_info (std::string name,
+		    symbol_name_match_type match_type,
+		    bool completion_mode = false)
+    : m_match_type (match_type),
+      m_completion_mode (completion_mode),
+      m_name (std::move (name))
+  {}
+
+  /* Getters.  See description of each corresponding field.  */
+  symbol_name_match_type match_type () const { return m_match_type; }
+  bool completion_mode () const { return m_completion_mode; }
+  const std::string &name () const { return m_name; }
+
+  /* Get the search name hash for searches in language LANG.  */
+  unsigned int search_name_hash (language lang) const
+  {
+    /* Only compute each language's hash once.  */
+    if (!m_demangled_hashes_p[lang])
+      {
+	m_demangled_hashes[lang]
+	  = ::search_name_hash (lang, language_lookup_name (lang).c_str ());
+	m_demangled_hashes_p[lang] = true;
+      }
+    return m_demangled_hashes[lang];
+  }
+
+  /* Get the search name for searches in language LANG.  */
+  const std::string &language_lookup_name (language lang) const
+  {
+    switch (lang)
+      {
+      case language_ada:
+	return ada ().lookup_name ();
+      case language_cplus:
+	return cplus ().lookup_name ();
+      case language_d:
+	return d ().lookup_name ();
+      case language_go:
+	return go ().lookup_name ();
+      default:
+	return m_name;
+      }
+  }
+
+  /* Get the Ada-specific lookup info.  */
+  const ada_lookup_name_info &ada () const
+  {
+    maybe_init (m_ada);
+    return *m_ada;
+  }
+
+  /* Get the C++-specific lookup info.  */
+  const demangle_for_lookup_info &cplus () const
+  {
+    maybe_init (m_cplus, language_cplus);
+    return *m_cplus;
+  }
+
+  /* Get the D-specific lookup info.  */
+  const demangle_for_lookup_info &d () const
+  {
+    maybe_init (m_d, language_d);
+    return *m_d;
+  }
+
+  /* Get the Go-specific lookup info.  */
+  const demangle_for_lookup_info &go () const
+  {
+    maybe_init (m_go, language_go);
+    return *m_go;
+  }
+
+  /* Get a reference to a lookup_name_info object that matches any
+     symbol name.  */
+  static const lookup_name_info &match_any ();
+
+private:
+  /* Initialize FIELD, if not initialized yet.  */
+  template<typename Field, typename... Args>
+  void maybe_init (Field &field, Args&&... args) const
+  {
+    if (!field)
+      field.emplace (*this, std::forward<Args> (args)...);
+  }
+
+  /* The lookup info as passed to the ctor.  */
+  symbol_name_match_type m_match_type;
+  bool m_completion_mode;
+  std::string m_name;
+
+  /* Language-specific info.  These fields are filled lazily the first
+     time a lookup is done in the corresponding language.  They're
+     mutable because lookup_name_info objects are typically passed
+     around by const reference (see intro), and they're conceptually
+     "cache" that can always be reconstructed from the non-mutable
+     fields.  */
+  mutable gdb::optional<ada_lookup_name_info> m_ada;
+  mutable gdb::optional<demangle_for_lookup_info> m_cplus;
+  mutable gdb::optional<demangle_for_lookup_info> m_d;
+  mutable gdb::optional<demangle_for_lookup_info> m_go;
+
+  /* The demangled hashes.  Stored in an array with one entry for each
+     possible language.  The second array records whether we've
+     already computed the each language's hash.  (These are separate
+     arrays instead of a single array of optional<unsigned> to avoid
+     alignment padding).  */
+  mutable std::array<unsigned int, nr_languages> m_demangled_hashes;
+  mutable std::array<bool, nr_languages> m_demangled_hashes_p {};
+};
+
+/* Comparison function for completion symbol lookup.
+
+   Returns true if the symbol name matches against LOOKUP_NAME.
+
+   SYMBOL_SEARCH_NAME should be a symbol's "search" name.
+
+   On success and if non-NULL, MATCH is set to point to the symbol
+   name as should be presented to the user as a completion match list
+   element.  In most languages, this is the same as the symbol's
+   search name, but in some, like Ada, the display name is dynamically
+   computed within the comparison routine.  */
+typedef bool (symbol_name_matcher_ftype)
+  (const char *symbol_search_name,
+   const lookup_name_info &lookup_name,
+   completion_match *match);
 
 /* Some of the structures in this file are space critical.
    The space-critical structures are:
@@ -269,13 +516,18 @@ extern int demangle;
    returns the same value (same pointer) as SYMBOL_LINKAGE_NAME.  */
 #define SYMBOL_SEARCH_NAME(symbol)					 \
    (symbol_search_name (&(symbol)->ginfo))
-extern const char *symbol_search_name (const struct general_symbol_info *);
+extern const char *symbol_search_name (const struct general_symbol_info *ginfo);
 
-/* Return non-zero if NAME matches the "search" name of SYMBOL.
-   Whitespace and trailing parentheses are ignored.
-   See strcmp_iw for details about its behavior.  */
-#define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
-  (strcmp_iw (SYMBOL_SEARCH_NAME (symbol), (name)) == 0)
+/* Return true if NAME matches the "search" name of SYMBOL, according
+   to the symbol's language.  */
+#define SYMBOL_MATCHES_SEARCH_NAME(symbol, name)                       \
+  symbol_matches_search_name (&(symbol)->ginfo, (name))
+
+/* Helper for SYMBOL_MATCHES_SEARCH_NAME that works with both symbols
+   and psymbols.  */
+extern bool symbol_matches_search_name
+  (const struct general_symbol_info *gsymbol,
+   const lookup_name_info &name);
 
 /* Compute the hash of the given symbol search name of a symbol of
    language LANGUAGE.  */
@@ -427,8 +679,6 @@ struct minimal_symbol
   (symbol_set_language (&(symbol)->mginfo, (language), (obstack)))
 #define MSYMBOL_SEARCH_NAME(symbol)					 \
    (symbol_search_name (&(symbol)->mginfo))
-#define MSYMBOL_MATCHES_SEARCH_NAME(symbol, name)			\
-  (strcmp_iw (MSYMBOL_SEARCH_NAME (symbol), (name)) == 0)
 #define MSYMBOL_SET_NAMES(symbol,linkage_name,len,copy_name,objfile)	\
   symbol_set_names (&(symbol)->mginfo, linkage_name, len, copy_name, objfile)
 
@@ -1512,26 +1762,30 @@ enum class complete_symbol_mode
 extern void default_collect_symbol_completion_matches_break_on
   (completion_tracker &tracker,
    complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
    const char *text, const char *word, const char *break_on,
    enum type_code code);
 extern void default_collect_symbol_completion_matches
   (completion_tracker &tracker,
    complete_symbol_mode,
+   symbol_name_match_type name_match_type,
    const char *,
    const char *,
    enum type_code);
-extern void collect_symbol_completion_matches (completion_tracker &tracker,
-					       complete_symbol_mode,
-					       const char *, const char *);
+extern void collect_symbol_completion_matches
+  (completion_tracker &tracker,
+   complete_symbol_mode mode,
+   symbol_name_match_type name_match_type,
+   const char *, const char *);
 extern void collect_symbol_completion_matches_type (completion_tracker &tracker,
 						    const char *, const char *,
 						    enum type_code);
 
-extern void collect_file_symbol_completion_matches (completion_tracker &tracker,
-						    complete_symbol_mode,
-						    const char *,
-						    const char *,
-						    const char *);
+extern void collect_file_symbol_completion_matches
+  (completion_tracker &tracker,
+   complete_symbol_mode,
+   symbol_name_match_type name_match_type,
+   const char *, const char *, const char *);
 
 extern completion_list
   make_source_files_completion_list (const char *, const char *);
@@ -1680,7 +1934,8 @@ std::vector<CORE_ADDR> find_pcs_for_symtab_line
 
 typedef bool (symbol_found_callback_ftype) (symbol *sym);
 
-void iterate_over_symbols (const struct block *block, const char *name,
+void iterate_over_symbols (const struct block *block,
+			   const lookup_name_info &name,
 			   const domain_enum domain,
 			   gdb::function_view<symbol_found_callback_ftype> callback);
 
@@ -1728,4 +1983,15 @@ void initialize_objfile_symbol (struct symbol *);
 
 struct template_symbol *allocate_template_symbol (struct objfile *);
 
+/* Test to see if the symbol of language SYMBOL_LANGUAGE specified by
+   SYMNAME (which is already demangled for C++ symbols) matches
+   SYM_TEXT in the first SYM_TEXT_LEN characters.  If so, add it to
+   the current completion list.  */
+void completion_list_add_name (completion_tracker &tracker,
+			       language symbol_language,
+			       const char *symname,
+			       const lookup_name_info &lookup_name,
+			       const char *sym_text, int sym_text_len,
+			       const char *text, const char *word);
+
 #endif /* !defined(SYMTAB_H) */
diff --git a/gdb/testsuite/gdb.ada/complete.exp b/gdb/testsuite/gdb.ada/complete.exp
index 906c85a..c3631c7 100644
--- a/gdb/testsuite/gdb.ada/complete.exp
+++ b/gdb/testsuite/gdb.ada/complete.exp
@@ -83,6 +83,16 @@ test_gdb_no_completion "exported"
 test_gdb_complete "<Exported" \
                   "p <Exported_Capitalized>"
 
+# While at it, make sure we can print the symbol too, using the '<'
+# notation.
+gdb_test "p <Exported_Capitalized>" " = 2"
+
+# Confirm that we can't print the symbol without the '<' notation.
+gdb_test "p Exported_Capitalized" \
+    "No definition of \"exported_capitalized\" in current context."
+gdb_test "p exported_capitalized" \
+    "No definition of \"exported_capitalized\" in current context."
+
 # A global symbol, created by the binder, that starts with __gnat...
 test_gdb_complete "__gnat_ada_main_progra" \
                   "p __gnat_ada_main_program_name"
diff --git a/gdb/utils.c b/gdb/utils.c
index dd74684..1af323a 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -2156,21 +2156,9 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
     }
 }
 
-/* Modes of operation for strncmp_iw_with_mode.  */
-
-enum class strncmp_iw_mode
-{
-  /* Work like strncmp, while ignoring whitespace.  */
-  NORMAL,
-
-  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
-     string1=="FOO(PARAMS)" matches string2=="FOO".  */
-  MATCH_PARAMS,
-};
-
-/* Helper for strncmp_iw and strcmp_iw.  */
+/* See utils.h.  */
 
-static int
+int
 strncmp_iw_with_mode (const char *string1, const char *string2,
 		      size_t string2_len, strncmp_iw_mode mode)
 {
diff --git a/gdb/utils.h b/gdb/utils.h
index 17d6258..e2fa430 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -31,6 +31,29 @@ extern void initialize_utils (void);
 
 extern int sevenbit_strings;
 
+/* Modes of operation for strncmp_iw_with_mode.  */
+
+enum class strncmp_iw_mode
+{
+/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
+   differences in whitespace.  Returns 0 if they match, non-zero if
+   they don't (slightly different than strcmp()'s range of return
+   values).  */
+  NORMAL,
+
+  /* Like NORMAL, but also apply the strcmp_iw hack.  I.e.,
+     string1=="FOO(PARAMS)" matches string2=="FOO".  */
+  MATCH_PARAMS,
+};
+
+/* Helper for strcmp_iw and strncmp_iw.  Exported so that languages
+   can implement both NORMAL and MATCH_PARAMS variants in a single
+   function and defer part of the work to strncmp_iw_with_mode.  */
+extern int strncmp_iw_with_mode (const char *string1,
+				 const char *string2,
+				 size_t string2_len,
+				 strncmp_iw_mode mode);
+
 /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
    differences in whitespace.  STRING2_LEN is STRING2's length.
    Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
-- 
2.5.5

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-10-19 17:36             ` Pedro Alves
@ 2017-11-01 15:38               ` Joel Brobecker
  2017-11-08 16:10                 ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Joel Brobecker @ 2017-11-01 15:38 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Keith Seitz, gdb-patches

Hi Pedro,

> This patch touches the Ada code a good deal.  It's main goal is
> to generalize Ada's FULL/WILD name matching, so that C++ can do
> something very similar.  Let me know if you'd like to review it.
> I've been holding on moving forward with this
> series because this is a central patch on top of which the
> rest of the series sits on.  

Thanks for drawing my attention to this patch. Overall, it looks
pretty neat :) and it starts a nice simplification of some of
the code in ada-lang. Thank you! I admit I didn't read the patch
in complete details, but enough to understand how it works and
convince myself that it should actually work. I've also run it
through our testsuites and found no regressions, neither with
this patch, nor the series as a whole.

So, to summarize, this looks good to me!

-- 
Joel

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

* Re: [PATCH 24/40] Per-language symbol name hashing algorithm
  2017-07-20 18:53     ` Pedro Alves
@ 2017-11-08 16:08       ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:08 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 07/20/2017 07:53 PM, Pedro Alves wrote:
> On 07/18/2017 06:33 PM, Keith Seitz wrote:
>> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>>
>>> This patch starts fixing this, by doing two things:
>>>
>>> #1 - adds a language vector method to let each language decide how to
>>>      compute a symbol name hash.
>>>
>>> #2 - makes dictionaries know the language of the symbols they hold,
>>>      and then use the dictionaries language to decide which hashing
>>>      method to use.
>>
>> Me likey! :-)
>>
> 
> Ahah, it was your idea.  :-)
> 
>> Again, just the usual trivial comments.
> 
> [snip]
> 
> Thanks much.  I added the missing comments now.
> 
> Here's the updated patch.

FYI, I've pushed this one in now.

Thanks,
Pedro Alves

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-11-01 15:38               ` Joel Brobecker
@ 2017-11-08 16:10                 ` Pedro Alves
  2017-11-08 22:15                   ` Joel Brobecker
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:10 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: Keith Seitz, gdb-patches

On 11/01/2017 03:38 PM, Joel Brobecker wrote:
> Hi Pedro,
> 
>> This patch touches the Ada code a good deal.  It's main goal is
>> to generalize Ada's FULL/WILD name matching, so that C++ can do
>> something very similar.  Let me know if you'd like to review it.
>> I've been holding on moving forward with this
>> series because this is a central patch on top of which the
>> rest of the series sits on.  
> 
> Thanks for drawing my attention to this patch. Overall, it looks
> pretty neat :) and it starts a nice simplification of some of
> the code in ada-lang. Thank you! I admit I didn't read the patch
> in complete details, but enough to understand how it works and
> convince myself that it should actually work. I've also run it
> through our testsuites and found no regressions, neither with
> this patch, nor the series as a whole.
> 
> So, to summarize, this looks good to me!

Phew!  I admit that I was worried about your internal
testsuites.  :-)

Thanks much, I've pushed this one in now (along with a few of the
follow ups, but not the whole series yet).

Thanks,
Pedro Alves

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

* Re: [PATCH 26/40] Optimize .gdb_index symbol name searching
  2017-08-08 20:32   ` Keith Seitz
@ 2017-11-08 16:14     ` Pedro Alves
  2017-11-08 16:16       ` [pushed] Reorder/reindent dw2_expand_symtabs_matching & friends (Re: [PATCH 26/40] Optimize .gdb_index symbol name searching) Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:14 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/08/2017 09:31 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
>> I got, before the previous patch (-O2, x86-64):
>>
>>  real    0m1.773s
>>  user    0m1.737s
>>  sys     0m0.040s
>>
>> and after this patch:
>>
>>  real    0m1.361s
>>  user    0m1.315s
>>  sys     0m0.040s
> 
> The results on my computer are slightly more dramatic, running with no
> optimization, your test case (using Fedora 21 system gdb w/index debuginfo)
> goes from about 15 seconds down to about 2.5 seconds. Very nice!
> 
>> That resulted in 1351355 name_components.  Each entry takes 8 bytes,
>> so that's 10810840 bytes (ignoring std::vector overhead), or ~10.3 MB.
>> That's IMO too small to worry about, given GDB was using over 7400MB
>> total at that point.  I.e., we're talking about 0.1% increase.
> 
> Indeed. I'd sacrifice that kind of memory for the kind of speed increase
> you've achieved -- in a heartbeat!
> 
>> with only 8-bit and 32-bit tables, that'd be:
>>
>>  1349057 * 1 + 2298 * 4 + 4 * 1351355 = 6763669 bytes, or ~6.5MB.
>>
>> I don't think we need to bother though.
> 
> I'm all for memory usage optimization and whatnot, but since the benefit is
> so small (55% of these new tables saved but only 0.06% of total memory),
> I prefer simplicity. So you won't get anything but agreement from me on this.
> 
>> I also timed:
>>
>>  $ time gdb --batch -q -p `pidof firefox`
>>  $ time gdb --batch -q -p `pidof firefox` -ex "b main"
>>  $ time gdb --batch -q -p `pidof firefox` -ex "set max-completion unlimited" -ex "complete b "
> 
> I'd like to reproduce this, but my computer is incapable of running this test.
> I'll take your word for it. ;-)

:-)

> 
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* dwarf2read.c 
>> 	(mapped_index::name_components): New field.
>> 	(mapped_index::symbol_name_at): New method.
> 
> Silly nit: Isn't the form most are using "(tag name) <field>: New field."?
> I know I've relied on this several times to find changes in the ChangeLog.

That made sense in C, since <part> is used to specify the part
of (foo) that changed:

  https://www.gnu.org/prep/standards/html_node/Indicating-the-Part-Changed.html#Indicating-the-Part-Changed

... and '::' does not exist in C.  But it exists in C++, and it
just feels natural to use it, since that's the way in C++
you'd name the 'foo' being referred to in '(foo)'.  If you look
at the GCCs ChangeLogs, you'll see they've been using this
form too:

 grep "::" gcc/ChangeLog  libstdc++-v3/ChangeLog

>> 	(create_addrmap_from_index): Call mapped_index ctor.
> 
> I don't see any changes to this function in the patch -- attributed to wrong
> function?

Indeed.  It should have been dwarf2_read_index.  Fixed, thanks.

> 
>> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
>> index f523326..e955131 100644
>> --- a/gdb/dwarf2read.c
>> +++ b/gdb/dwarf2read.c
>> @@ -178,6 +178,51 @@ DEF_VEC_I (offset_type);
> [snip]
>> +
>> +/* An index into a (C++) symbol name component in a symbol name as
>> +   recorded in the mapped_index's symbol table.  For each C++ symbol
>> +   in the symbol table, we record one entry for the start of each
>> +   component in the symbol in a table of name components, and then
>> +   sort the table, in order to be able to binary search symbol names,
>> +   ignoring leading namespaces, both completion and regular look up.
>> +   For example, for symbol "A::B::C", we'll have an entry that points
>> +   to "A::B::C", another that points to "B::C", and another for "C".
>> +   Note that function symbols in GDB index have no parameter
>> +   information, just the function/method names.  You can convert a
>> +   name_component to a "const char *" using the
>> +   'mapped_index::symbol_name_at(offset_type)' method.  */
> 
> missing nl?
> 

Fixed.

>> +struct name_component
>> +{
>> +  /* Offset in the symbol name where the component starts.  Stored as
>> +     a (32-bit) offset instead of a pointer to save memory and improve
>> +     locality on 64-bit architectures.  */
>> +  offset_type name_offset;
>> +
>> +  /* The symbol's index in the symbol and constant pool tables of a
>> +     mapped_index.  */
>> +  offset_type idx;
>> +};
>> +
>>  /* A description of the mapped index.  The file format is described in
>>     a comment by the code that writes the index.  */
>>  struct mapped_index
>> @@ -3390,6 +3424,7 @@ dwarf2_read_index (struct objfile *objfile)
>>    create_addrmap_from_index (objfile, &local_map);
>>  
>>    map = XOBNEW (&objfile->objfile_obstack, struct mapped_index);
>> +  map = new (map) mapped_index ();
>>    *map = local_map;
>>  
>>    dwarf2_per_objfile->index_table = map;
> 
> This function (dwarf2_read_index) is not mentioned in the ChangeLog. Could
> this actually be incorrectly listed in the ChangeLog under
> create_addrmap_from_index?

Indeed.  Fixed.

> 
>> @@ -4095,6 +4134,22 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
>>  }
>>  
>>  static void
>> +dw2_expand_marked_cus
>> +  (mapped_index &index, offset_type idx,
>> +   struct objfile *objfile,
>> +   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
>> +   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
>> +   search_domain kind);
>> +
>> +static void
>> +dw2_expand_symtabs_matching_symbol
>> +  (mapped_index &index,
>> +   const lookup_name_info &lookup_name_in,
>> +   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
>> +   enum search_domain kind,
>> +   gdb::function_view<void (offset_type)> on_match);
> 
> Isn't it rather unusual for us to have forward decls in the middle of a file?
> 

Right, I should have mentioned -- I'd like to move
the new dw2_expand_symtabs_matching_symbol and dw2_expand_marked_cus
above dw2_expand_symtabs_matching, but I didn't do that here to
make the patch easier to read and maintain.  Likewise with the
reindenting dw2_expand_marked_cus where I had marked with:

  /* XXX reindent code below before pushing.  */

I'll move the code around in the following patch (a new patch),
getting rid of the forward declarations.  Since I'm doing that
as a separate patch, it was also easier to leave the
reindentation to that patch too.

>> +
>> +static void
>>  dw2_expand_symtabs_matching
>>    (struct objfile *objfile,
>>     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
>> @@ -4186,30 +4239,214 @@ dw2_expand_symtabs_matching
> [snip]
>> +static void
>> +dw2_expand_symtabs_matching_symbol
>> +  (mapped_index &index,
>> +   const lookup_name_info &lookup_name,
>> +   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
>> +   enum search_domain kind,
>> +   gdb::function_view<void (offset_type)> match_callback)
>> +{
> [snip]
>> +
>> +      /* Sort name_comp elements by name.   */
> 
> I presume that "name_comp" is really "name_components"?

Indeed.  Fixed.

> I admit, I'm a little surprised by the number of steps involved here: push
> every element in the range into a vector, sort, then de-dup & perform callback.
> Had I implemented this, my initial attempt would have been to use a htab_up
> and take the sorting time on insertion.
> I can imagine that for very large ranges, your approach could outperform
> an htab; do you expect these ranges to be that large? Have I overlooked
> something? [I'm just curious. Not suggesting any changes need to be made.]

Yeah, this is a hot path in completion lookup, and I'm trying to squeeze
every cycle here.

But the complexity is also needed because for completion, we need the symbols
sorted, while an htab doesn't have a predictable iteration order.
We want to find the lower / upper bounds starting with a needle that
doesn't exist in the name_components vector/set.  E.g., looking for what 
symbols complete "fun", when you have symbols named 
{... "bar", "func", "function", "xfunc" ... },
we want to consider "func", "function" as potential matches without
wasting time with "bar" and "xfunc" (and others).  

So while using something like a std::set instead of a std::vector + sort
would take the sorting out of view, I don't think that'd simplify that
much, while it'd certainly perform worse.
(See <https://stackoverflow.com/questions/15638024/stdsort-vs-inserting-into-an-stdset>
for example.)

For the record, below's what I pushed.

From 3f563c840a2c891ec2868b3e08bfaecb6f7aa57f Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 8 Nov 2017 14:22:32 +0000
Subject: [PATCH] Optimize .gdb_index symbol name searching

As mentioned in the previous patch, .gdb_index name lookup got
significantly slower with the previous patch.

This patch addresses that, and in the process makes .gdb_index name
searching faster than what we had before the previous patch, even.
Using the same test:

 $ cat script.cmd
 set pagination off
 set $count = 0
 while $count < 400
   complete b string_prin
   printf "count = %d\n", $count
   set $count = $count + 1
 end

 $ time gdb --batch -q ./gdb-with-index -ex "source script.cmd"

I got, before the previous patch (-O2, x86-64):

 real    0m1.773s
 user    0m1.737s
 sys     0m0.040s

and after this patch:

 real    0m1.361s
 user    0m1.315s
 sys     0m0.040s

The basic idea here is simple: instead of always iterating over all
the symbol names in the index, we build an accelerator/sorted name
table and binary search names in it.

Later in the series, we'll want to support wild matching for C++ too,
so this mechanism already considers that.  For example, say that
you're looking up functions/methods named "func", no matter the
containing namespace/class.  If we sorted the table by qualified name,
then we obviously wouldn't be able to find those symbols with a binary
search:

  func
  ns1::a::b::func
  ns1::b::func
  ns2::func

(function symbol names in .gdb_index have no parameter info, like psymbols)

To address that out, we put an entry for each name component in the
sorted table.  something like this:

  Table Entry       Actual symbol
  ---------------------------------
  func              func

  func              ns1::a::b::func
  b::func           ns1::a::b::func
  a::b::func        ns1::a::b::func
  ns1::a::b::func   ns1::a::b::func

  func              ns1::b::func
  b::func           ns1::b::func
  ns1::b::func      ns1::b::func

  func              ns2::func
  ns2::func         ns2::func

Which sorted results in this:

  Table Entry       Actual symbol
  ---------------------------------
  a::b::func        ns1::a::b::func
  b::func           ns1::a::b::func
  b::func           ns1::b::func
  func              func
  func              ns1::a::b::func
  func              ns1::b::func
  func              ns2::func
  ns1::a::b::func   ns1::a::b::func
  ns1::b::func      ns1::b::func
  ns2::func         ns2::func

And we can binary search this.

Note that a binary search approach works for both completion and
regular lookup, while a name hashing approach only works for normal
symbol looking, since obviously "fun" and "func" have different
hashes.

At first I was a bit wary of these tables potentially growing GDB's
memory significantly.  But I did an experiment that convinced it's not
a worry at all.  I hacked gdb to count the total number of entries in
all the tables, attached that gdb to my system/Fedora's Firefox
(Fedora's debug packages uses .gdb_index), did "set max-completions
unlimited", and then hit "b [TAB]" to cause everything to expand.

That resulted in 1351355 name_components.  Each entry takes 8 bytes,
so that's 10810840 bytes (ignoring std::vector overhead), or ~10.3 MB.
That's IMO too small to worry about, given GDB was using over 7400MB
total at that point.  I.e., we're talking about 0.1% increase.

dw2_expand_symtabs_matching unit tests covering this will be added in
a follow up patch.

If the size of this table turns out to be a concern, I have an idea to
reduce the size of the table further at the expense of a bit more code
-- the vast majority of the name offsets are either 0 or fit in
8-bits:

 total name_component = 1351355, of which,
 name_component::name_offset instances need  0 bits = 679531
 name_component::name_offset instances need  8 bits = 669526
 name_component::name_offset instances need 16 bits = 2298
 name_component::name_offset instances need 32 bits = 0
 name_component::idx instances need 0 bits  = 51
 name_component::idx instances need 8 bits  = 8361
 name_component::idx instances need 16 bits = 280329
 name_component::idx instances need 32 bits = 1062614

so we could have separate tables for 0 name_offset, 8-bit name_offset
and 32-bit name_offset.  That'd give us roughly:

 679531 * 0 + 669526 * 1 + 2298 * 4 + 1062614 * 4 = 4929174, or ~4.7MB

with only 8-bit and 32-bit tables, that'd be:

 1349057 * 1 + 2298 * 4 + 4 * 1351355 = 6763669 bytes, or ~6.5MB.

I don't think we need to bother though.

I also timed:

 $ time gdb --batch -q -p `pidof firefox`
 $ time gdb --batch -q -p `pidof firefox` -ex "b main"
 $ time gdb --batch -q -p `pidof firefox` -ex "set max-completion unlimited" -ex "complete b "

and compared before previous patch vs this patch, and I didn't see a
significant difference, seemingly because time to read debug info
dominates.  The "complete b " variant of the test takes ~2min
currently...  (I have a follow up series that speeds that up
somewhat.)

gdb/ChangeLog:
2017-11-08  Pedro Alves  <palves@redhat.com>

	* dwarf2read.c (byte_swap, MAYBE_SWAP): Move higher up in file.
	(struct name_component): New.
	(mapped_index::name_components): New field.
	(mapped_index::symbol_name_at): New method.
	(dwarf2_read_index): Call mapped_index ctor.
	(dw2_map_matching_symbols): Add comment about name_components
	table.
	(dw2_expand_symtabs_matching): Factor part to...
	(dw2_expand_symtabs_matching_symbol): ... this new function.
	Build name components table, and lookup symbols in it before
	calling the name matcher.
	(dw2_expand_marked_cus): New, factored out from
	dw2_expand_symtabs_matching.
	(dwarf2_per_objfile_free): Call the mapped_index's dtor.
---
 gdb/ChangeLog    |  17 +++
 gdb/dwarf2read.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 298 insertions(+), 42 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 06ababc..deb1614 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,20 @@
+2017-11-08  Pedro Alves  <palves@redhat.com>
+
+	* dwarf2read.c (byte_swap, MAYBE_SWAP): Move higher up in file.
+	(struct name_component): New.
+	(mapped_index::name_components): New field.
+	(mapped_index::symbol_name_at): New method.
+	(dwarf2_read_index): Call mapped_index ctor.
+	(dw2_map_matching_symbols): Add comment about name_components
+	table.
+	(dw2_expand_symtabs_matching): Factor part to...
+	(dw2_expand_symtabs_matching_symbol): ... this new function.
+	Build name components table, and lookup symbols in it before
+	calling the name matcher.
+	(dw2_expand_marked_cus): New, factored out from
+	dw2_expand_symtabs_matching.
+	(dwarf2_per_objfile_free): Call the mapped_index's dtor.
+
 2017-11-08   Pedro Alves  <palves@redhat.com>
 
 	* ada-lang.c (ada_encode): Rename to ..
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index f37d51f..6f88091 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -182,6 +182,53 @@ DEF_VEC_I (offset_type);
     GDB_INDEX_CU_SET_VALUE((cu_index), (value)); \
   } while (0)
 
+#if WORDS_BIGENDIAN
+
+/* Convert VALUE between big- and little-endian.  */
+
+static offset_type
+byte_swap (offset_type value)
+{
+  offset_type result;
+
+  result = (value & 0xff) << 24;
+  result |= (value & 0xff00) << 8;
+  result |= (value & 0xff0000) >> 8;
+  result |= (value & 0xff000000) >> 24;
+  return result;
+}
+
+#define MAYBE_SWAP(V)  byte_swap (V)
+
+#else
+#define MAYBE_SWAP(V) static_cast<offset_type> (V)
+#endif /* WORDS_BIGENDIAN */
+
+/* An index into a (C++) symbol name component in a symbol name as
+   recorded in the mapped_index's symbol table.  For each C++ symbol
+   in the symbol table, we record one entry for the start of each
+   component in the symbol in a table of name components, and then
+   sort the table, in order to be able to binary search symbol names,
+   ignoring leading namespaces, both completion and regular look up.
+   For example, for symbol "A::B::C", we'll have an entry that points
+   to "A::B::C", another that points to "B::C", and another for "C".
+   Note that function symbols in GDB index have no parameter
+   information, just the function/method names.  You can convert a
+   name_component to a "const char *" using the
+   'mapped_index::symbol_name_at(offset_type)' method.  */
+
+struct name_component
+{
+  /* Offset in the symbol name where the component starts.  Stored as
+     a (32-bit) offset instead of a pointer to save memory and improve
+     locality on 64-bit architectures.  */
+  offset_type name_offset;
+
+  /* The symbol's index in the symbol and constant pool tables of a
+     mapped_index.  */
+  offset_type idx;
+};
+
 /* A description of the mapped index.  The file format is described in
    a comment by the code that writes the index.  */
 struct mapped_index
@@ -206,6 +253,15 @@ struct mapped_index
 
   /* A pointer to the constant pool.  */
   const char *constant_pool;
+
+  /* The name_component table (a sorted vector).  See name_component's
+     description above.  */
+  std::vector<name_component> name_components;
+
+  /* Convenience method to get at the name of the symbol at IDX in the
+     symbol table.  */
+  const char *symbol_name_at (offset_type idx) const
+  { return this->constant_pool + MAYBE_SWAP (this->symbol_table[idx]); }
 };
 
 typedef struct dwarf2_per_cu_data *dwarf2_per_cu_ptr;
@@ -2160,26 +2216,6 @@ line_header_eq_voidp (const void *item_lhs, const void *item_rhs)
 }
 
 \f
-#if WORDS_BIGENDIAN
-
-/* Convert VALUE between big- and little-endian.  */
-static offset_type
-byte_swap (offset_type value)
-{
-  offset_type result;
-
-  result = (value & 0xff) << 24;
-  result |= (value & 0xff00) << 8;
-  result |= (value & 0xff0000) >> 8;
-  result |= (value & 0xff000000) >> 24;
-  return result;
-}
-
-#define MAYBE_SWAP(V)  byte_swap (V)
-
-#else
-#define MAYBE_SWAP(V) static_cast<offset_type> (V)
-#endif /* WORDS_BIGENDIAN */
 
 /* Read the given attribute value as an address, taking the attribute's
    form into account.  */
@@ -3444,6 +3480,7 @@ dwarf2_read_index (struct objfile *objfile)
   create_addrmap_from_index (objfile, &local_map);
 
   map = XOBNEW (&objfile->objfile_obstack, struct mapped_index);
+  map = new (map) mapped_index ();
   *map = local_map;
 
   dwarf2_per_objfile->index_table = map;
@@ -4070,7 +4107,11 @@ dw2_map_matching_symbols (struct objfile *objfile,
 
      Since each language has its own symbol name matching algorithm,
      and we don't know which language is the right one, we must match
-     each symbol against all languages.
+     each symbol against all languages.  This would be a potential
+     performance problem if it were not mitigated by the
+     mapped_index::name_components lookup table, which significantly
+     reduces the number of times we need to call into this matcher,
+     making it a non-issue.
 
    - Symbol names in the index have no overload (parameter)
      information.  I.e., in C++, "foo(int)" and "foo(long)" both
@@ -4148,6 +4189,22 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
 }
 
 static void
+dw2_expand_marked_cus
+  (mapped_index &index, offset_type idx,
+   struct objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+   search_domain kind);
+
+static void
+dw2_expand_symtabs_matching_symbol
+  (mapped_index &index,
+   const lookup_name_info &lookup_name_in,
+   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+   enum search_domain kind,
+   gdb::function_view<void (offset_type)> on_match);
+
+static void
 dw2_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
@@ -4158,14 +4215,12 @@ dw2_expand_symtabs_matching
 {
   int i;
   offset_type iter;
-  struct mapped_index *index;
 
   dw2_setup (objfile);
 
   /* index_table is NULL if OBJF_READNOW.  */
   if (!dwarf2_per_objfile->index_table)
     return;
-  index = dwarf2_per_objfile->index_table;
 
   if (file_matcher != NULL)
     {
@@ -4239,30 +4294,212 @@ dw2_expand_symtabs_matching
 	}
     }
 
-  gdb_index_symbol_name_matcher lookup_name_matcher (lookup_name);
+  mapped_index &index = *dwarf2_per_objfile->index_table;
 
-  for (iter = 0; iter < index->symbol_table_slots; ++iter)
+  dw2_expand_symtabs_matching_symbol (index, lookup_name,
+				      symbol_matcher,
+				      kind, [&] (offset_type idx)
     {
-      offset_type idx = 2 * iter;
-      const char *name;
-      offset_type *vec, vec_len, vec_idx;
-      int global_seen = 0;
+      dw2_expand_marked_cus (index, idx, objfile, file_matcher,
+			     expansion_notify, kind);
+    });
+}
 
-      QUIT;
+/* Helper for dw2_expand_symtabs_matching that works with a
+   mapped_index instead of the containing objfile.  This is split to a
+   separate function in order to be able to unit test the
+   name_components matching using a mock mapped_index.  For each
+   symbol name that matches, calls MATCH_CALLBACK, passing it the
+   symbol's index in the mapped_index symbol table.  */
 
-      if (index->symbol_table[idx] == 0 && index->symbol_table[idx + 1] == 0)
-	continue;
+static void
+dw2_expand_symtabs_matching_symbol
+  (mapped_index &index,
+   const lookup_name_info &lookup_name,
+   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+   enum search_domain kind,
+   gdb::function_view<void (offset_type)> match_callback)
+{
+  gdb_index_symbol_name_matcher lookup_name_matcher
+    (lookup_name);
+
+  auto *name_cmp = case_sensitivity == case_sensitive_on ? strcmp : strcasecmp;
+
+  /* Build the symbol name component sorted vector, if we haven't yet.
+     The code below only knows how to break apart components of C++
+     symbol names (and other languages that use '::' as
+     namespace/module separator).  If we add support for wild matching
+     to some language that uses some other operator (E.g., Ada, Go and
+     D use '.'), then we'll need to try splitting the symbol name
+     according to that language too.  Note that Ada does support wild
+     matching, but doesn't currently support .gdb_index.  */
+  if (index.name_components.empty ())
+    {
+      for (size_t iter = 0; iter < index.symbol_table_slots; ++iter)
+	{
+	  offset_type idx = 2 * iter;
+
+	  if (index.symbol_table[idx] == 0
+	      && index.symbol_table[idx + 1] == 0)
+	    continue;
+
+	  const char *name = index.symbol_name_at (idx);
+
+	  /* Add each name component to the name component table.  */
+	  unsigned int previous_len = 0;
+	  for (unsigned int current_len = cp_find_first_component (name);
+	       name[current_len] != '\0';
+	       current_len += cp_find_first_component (name + current_len))
+	    {
+	      gdb_assert (name[current_len] == ':');
+	      index.name_components.push_back ({previous_len, idx});
+	      /* Skip the '::'.  */
+	      current_len += 2;
+	      previous_len = current_len;
+	    }
+	  index.name_components.push_back ({previous_len, idx});
+	}
 
-      name = index->constant_pool + MAYBE_SWAP (index->symbol_table[idx]);
+      /* Sort name_components elements by name.  */
+      auto name_comp_compare = [&] (const name_component &left,
+				    const name_component &right)
+	{
+	  const char *left_qualified = index.symbol_name_at (left.idx);
+	  const char *right_qualified = index.symbol_name_at (right.idx);
+
+	  const char *left_name = left_qualified + left.name_offset;
+	  const char *right_name = right_qualified + right.name_offset;
+
+	  return name_cmp (left_name, right_name) < 0;
+	};
+
+      std::sort (index.name_components.begin (),
+		 index.name_components.end (),
+		 name_comp_compare);
+    }
+
+  const char *cplus
+    = lookup_name.cplus ().lookup_name ().c_str ();
 
-      if (!lookup_name_matcher.matches (name)
-	  || (symbol_matcher != NULL && !symbol_matcher (name)))
+  /* Comparison function object for lower_bound that matches against a
+     given symbol name.  */
+  auto lookup_compare_lower = [&] (const name_component &elem,
+				   const char *name)
+    {
+      const char *elem_qualified = index.symbol_name_at (elem.idx);
+      const char *elem_name = elem_qualified + elem.name_offset;
+      return name_cmp (elem_name, name) < 0;
+    };
+
+  /* Comparison function object for upper_bound that matches against a
+     given symbol name.  */
+  auto lookup_compare_upper = [&] (const char *name,
+				   const name_component &elem)
+    {
+      const char *elem_qualified = index.symbol_name_at (elem.idx);
+      const char *elem_name = elem_qualified + elem.name_offset;
+      return name_cmp (name, elem_name) < 0;
+    };
+
+  auto begin = index.name_components.begin ();
+  auto end = index.name_components.end ();
+
+  /* Find the lower bound.  */
+  auto lower = [&] ()
+    {
+      if (lookup_name.completion_mode () && cplus[0] == '\0')
+	return begin;
+      else
+	return std::lower_bound (begin, end, cplus, lookup_compare_lower);
+    } ();
+
+  /* Find the upper bound.  */
+  auto upper = [&] ()
+    {
+      if (lookup_name.completion_mode ())
+	{
+	  /* The string frobbing below won't work if the string is
+	     empty.  We don't need it then, anyway -- if we're
+	     completing an empty string, then we want to iterate over
+	     the whole range.  */
+	  if (cplus[0] == '\0')
+	    return end;
+
+	  /* In completion mode, increment the last character because
+	     we want UPPER to point past all symbols names that have
+	     the same prefix.  */
+	  std::string after = cplus;
+
+	  gdb_assert (after.back () != 0xff);
+	  after.back ()++;
+
+	  return std::upper_bound (lower, end, after.c_str (),
+				   lookup_compare_upper);
+	}
+      else
+	return std::upper_bound (lower, end, cplus, lookup_compare_upper);
+    } ();
+
+  /* Now for each symbol name in range, check to see if we have a name
+     match, and if so, call the MATCH_CALLBACK callback.  */
+
+  /* The same symbol may appear more than once in the range though.
+     E.g., if we're looking for symbols that complete "w", and we have
+     a symbol named "w1::w2", we'll find the two name components for
+     that same symbol in the range.  To be sure we only call the
+     callback once per symbol, we first collect the symbol name
+     indexes that matched in a temporary vector and ignore
+     duplicates.  */
+  std::vector<offset_type> matches;
+  matches.reserve (std::distance (lower, upper));
+
+  for (;lower != upper; ++lower)
+    {
+      const char *qualified = index.symbol_name_at (lower->idx);
+
+      if (!lookup_name_matcher.matches (qualified)
+	  || (symbol_matcher != NULL && !symbol_matcher (qualified)))
 	continue;
 
-      /* The name was matched, now expand corresponding CUs that were
-	 marked.  */
-      vec = (offset_type *) (index->constant_pool
-			     + MAYBE_SWAP (index->symbol_table[idx + 1]));
+      matches.push_back (lower->idx);
+    }
+
+  std::sort (matches.begin (), matches.end ());
+
+  /* Finally call the callback, once per match.  */
+  ULONGEST prev = -1;
+  for (offset_type idx : matches)
+    {
+      if (prev != idx)
+	{
+	  match_callback (idx);
+	  prev = idx;
+	}
+    }
+
+  /* Above we use a type wider than idx's for 'prev', since 0 and
+     (offset_type)-1 are both possible values.  */
+  static_assert (sizeof (prev) > sizeof (offset_type), "");
+}
+
+/* Helper for dw2_expand_matching symtabs.  Called on each symbol
+   matched, to expand corresponding CUs that were marked.  IDX is the
+   index of the symbol name that matched.  */
+
+static void
+dw2_expand_marked_cus
+  (mapped_index &index, offset_type idx,
+   struct objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+   search_domain kind)
+{
+  const char *name;
+  offset_type *vec, vec_len, vec_idx;
+  bool global_seen = false;
+
+      vec = (offset_type *) (index.constant_pool
+			     + MAYBE_SWAP (index.symbol_table[idx + 1]));
       vec_len = MAYBE_SWAP (vec[0]);
       for (vec_idx = 0; vec_idx < vec_len; ++vec_idx)
 	{
@@ -4278,7 +4515,7 @@ dw2_expand_symtabs_matching
 	     and indices >= 7 may elide them for certain symbols
 	     (gold does this).  */
 	  int attrs_valid =
-	    (index->version >= 7
+	    (index.version >= 7
 	     && symbol_kind != GDB_INDEX_SYMBOL_KIND_NONE);
 
 	  /* Work around gold/15646.  */
@@ -4287,7 +4524,7 @@ dw2_expand_symtabs_matching
 	      if (!is_static && global_seen)
 		continue;
 	      if (!is_static)
-		global_seen = 1;
+		global_seen = true;
 	    }
 
 	  /* Only check the symbol's kind if it has one.  */
@@ -4338,7 +4575,6 @@ dw2_expand_symtabs_matching
 		}
 	    }
 	}
-    }
 }
 
 /* A helper for dw2_find_pc_sect_compunit_symtab which finds the most specific
@@ -23362,6 +23598,9 @@ dwarf2_per_objfile_free (struct objfile *objfile, void *d)
 
   if (data->dwz_file && data->dwz_file->dwz_bfd)
     gdb_bfd_unref (data->dwz_file->dwz_bfd);
+
+  if (data->index_table != NULL)
+    data->index_table->~mapped_index ();
 }
 
 \f
-- 
2.5.5

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

* [pushed] Reorder/reindent dw2_expand_symtabs_matching & friends (Re: [PATCH 26/40] Optimize .gdb_index symbol name searching)
  2017-11-08 16:14     ` Pedro Alves
@ 2017-11-08 16:16       ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:16 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 11/08/2017 04:14 PM, Pedro Alves wrote:
> Right, I should have mentioned -- I'd like to move
> the new dw2_expand_symtabs_matching_symbol and dw2_expand_marked_cus
> above dw2_expand_symtabs_matching, but I didn't do that here to
> make the patch easier to read and maintain.  Likewise with the
> reindenting dw2_expand_marked_cus where I had marked with:
> 
>   /* XXX reindent code below before pushing.  */
> 
> I'll move the code around in the following patch (a new patch),
> getting rid of the forward declarations.  Since I'm doing that
> as a separate patch, it was also easier to leave the
> reindentation to that patch too.
> 

Here's what I pushed for that.

From 61920122ba93d58cc2e8c78a30475c569c2506fd Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 8 Nov 2017 14:22:32 +0000
Subject: [PATCH] Reorder/reindent dw2_expand_symtabs_matching & friends

The previous patch had added dw2_expand_symtabs_matching_symbol and
dw2_expand_marked_cus forward declarations and did not reindent
dw2_expand_marked_cus to avoid moving the code around while changing
it at the same time.

gdb/ChangeLog:
2017-11-08  Pedro Alves  <palves@redhat.com>

	* dwarf2read.c (dw2_expand_marked_cus)
	(dw2_expand_symtabs_matching_symbol): Remove forward declarations.
	(dw2_expand_symtabs_matching): Move further below.
	(dw2_expand_marked_cus): Reindent.
---
 gdb/ChangeLog    |   7 ++
 gdb/dwarf2read.c | 334 ++++++++++++++++++++++++++-----------------------------
 2 files changed, 165 insertions(+), 176 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index deb1614..9776cf2 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,12 @@
 2017-11-08  Pedro Alves  <palves@redhat.com>
 
+	* dwarf2read.c (dw2_expand_marked_cus)
+	(dw2_expand_symtabs_matching_symbol): Remove forward declarations.
+	(dw2_expand_symtabs_matching): Move further below.
+	(dw2_expand_marked_cus): Reindent.
+
+2017-11-08  Pedro Alves  <palves@redhat.com>
+
 	* dwarf2read.c (byte_swap, MAYBE_SWAP): Move higher up in file.
 	(struct name_component): New.
 	(mapped_index::name_components): New field.
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 6f88091..d715082 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -4188,123 +4188,6 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
   return false;
 }
 
-static void
-dw2_expand_marked_cus
-  (mapped_index &index, offset_type idx,
-   struct objfile *objfile,
-   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
-   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
-   search_domain kind);
-
-static void
-dw2_expand_symtabs_matching_symbol
-  (mapped_index &index,
-   const lookup_name_info &lookup_name_in,
-   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
-   enum search_domain kind,
-   gdb::function_view<void (offset_type)> on_match);
-
-static void
-dw2_expand_symtabs_matching
-  (struct objfile *objfile,
-   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
-   const lookup_name_info &lookup_name,
-   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
-   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
-   enum search_domain kind)
-{
-  int i;
-  offset_type iter;
-
-  dw2_setup (objfile);
-
-  /* index_table is NULL if OBJF_READNOW.  */
-  if (!dwarf2_per_objfile->index_table)
-    return;
-
-  if (file_matcher != NULL)
-    {
-      htab_up visited_found (htab_create_alloc (10, htab_hash_pointer,
-						htab_eq_pointer,
-						NULL, xcalloc, xfree));
-      htab_up visited_not_found (htab_create_alloc (10, htab_hash_pointer,
-						    htab_eq_pointer,
-						    NULL, xcalloc, xfree));
-
-      /* The rule is CUs specify all the files, including those used by
-	 any TU, so there's no need to scan TUs here.  */
-
-      for (i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
-	{
-	  int j;
-	  struct dwarf2_per_cu_data *per_cu = dw2_get_cu (i);
-	  struct quick_file_names *file_data;
-	  void **slot;
-
-	  QUIT;
-
-	  per_cu->v.quick->mark = 0;
-
-	  /* We only need to look at symtabs not already expanded.  */
-	  if (per_cu->v.quick->compunit_symtab)
-	    continue;
-
-	  file_data = dw2_get_file_names (per_cu);
-	  if (file_data == NULL)
-	    continue;
-
-	  if (htab_find (visited_not_found.get (), file_data) != NULL)
-	    continue;
-	  else if (htab_find (visited_found.get (), file_data) != NULL)
-	    {
-	      per_cu->v.quick->mark = 1;
-	      continue;
-	    }
-
-	  for (j = 0; j < file_data->num_file_names; ++j)
-	    {
-	      const char *this_real_name;
-
-	      if (file_matcher (file_data->file_names[j], false))
-		{
-		  per_cu->v.quick->mark = 1;
-		  break;
-		}
-
-	      /* Before we invoke realpath, which can get expensive when many
-		 files are involved, do a quick comparison of the basenames.  */
-	      if (!basenames_may_differ
-		  && !file_matcher (lbasename (file_data->file_names[j]),
-				    true))
-		continue;
-
-	      this_real_name = dw2_get_real_path (objfile, file_data, j);
-	      if (file_matcher (this_real_name, false))
-		{
-		  per_cu->v.quick->mark = 1;
-		  break;
-		}
-	    }
-
-	  slot = htab_find_slot (per_cu->v.quick->mark
-				 ? visited_found.get ()
-				 : visited_not_found.get (),
-				 file_data, INSERT);
-	  *slot = file_data;
-	}
-    }
-
-  mapped_index &index = *dwarf2_per_objfile->index_table;
-
-  dw2_expand_symtabs_matching_symbol (index, lookup_name,
-				      symbol_matcher,
-				      kind, [&] (offset_type idx)
-    {
-      dw2_expand_marked_cus (index, idx, objfile, file_matcher,
-			     expansion_notify, kind);
-    });
-}
-
 /* Helper for dw2_expand_symtabs_matching that works with a
    mapped_index instead of the containing objfile.  This is split to a
    separate function in order to be able to unit test the
@@ -4498,83 +4381,182 @@ dw2_expand_marked_cus
   offset_type *vec, vec_len, vec_idx;
   bool global_seen = false;
 
-      vec = (offset_type *) (index.constant_pool
-			     + MAYBE_SWAP (index.symbol_table[idx + 1]));
-      vec_len = MAYBE_SWAP (vec[0]);
-      for (vec_idx = 0; vec_idx < vec_len; ++vec_idx)
+  vec = (offset_type *) (index.constant_pool
+			 + MAYBE_SWAP (index.symbol_table[idx + 1]));
+  vec_len = MAYBE_SWAP (vec[0]);
+  for (vec_idx = 0; vec_idx < vec_len; ++vec_idx)
+    {
+      struct dwarf2_per_cu_data *per_cu;
+      offset_type cu_index_and_attrs = MAYBE_SWAP (vec[vec_idx + 1]);
+      /* This value is only valid for index versions >= 7.  */
+      int is_static = GDB_INDEX_SYMBOL_STATIC_VALUE (cu_index_and_attrs);
+      gdb_index_symbol_kind symbol_kind =
+	GDB_INDEX_SYMBOL_KIND_VALUE (cu_index_and_attrs);
+      int cu_index = GDB_INDEX_CU_VALUE (cu_index_and_attrs);
+      /* Only check the symbol attributes if they're present.
+	 Indices prior to version 7 don't record them,
+	 and indices >= 7 may elide them for certain symbols
+	 (gold does this).  */
+      int attrs_valid =
+	(index.version >= 7
+	 && symbol_kind != GDB_INDEX_SYMBOL_KIND_NONE);
+
+      /* Work around gold/15646.  */
+      if (attrs_valid)
 	{
-	  struct dwarf2_per_cu_data *per_cu;
-	  offset_type cu_index_and_attrs = MAYBE_SWAP (vec[vec_idx + 1]);
-	  /* This value is only valid for index versions >= 7.  */
-	  int is_static = GDB_INDEX_SYMBOL_STATIC_VALUE (cu_index_and_attrs);
-	  gdb_index_symbol_kind symbol_kind =
-	    GDB_INDEX_SYMBOL_KIND_VALUE (cu_index_and_attrs);
-	  int cu_index = GDB_INDEX_CU_VALUE (cu_index_and_attrs);
-	  /* Only check the symbol attributes if they're present.
-	     Indices prior to version 7 don't record them,
-	     and indices >= 7 may elide them for certain symbols
-	     (gold does this).  */
-	  int attrs_valid =
-	    (index.version >= 7
-	     && symbol_kind != GDB_INDEX_SYMBOL_KIND_NONE);
+	  if (!is_static && global_seen)
+	    continue;
+	  if (!is_static)
+	    global_seen = true;
+	}
 
-	  /* Work around gold/15646.  */
-	  if (attrs_valid)
+      /* Only check the symbol's kind if it has one.  */
+      if (attrs_valid)
+	{
+	  switch (kind)
 	    {
-	      if (!is_static && global_seen)
+	    case VARIABLES_DOMAIN:
+	      if (symbol_kind != GDB_INDEX_SYMBOL_KIND_VARIABLE)
+		continue;
+	      break;
+	    case FUNCTIONS_DOMAIN:
+	      if (symbol_kind != GDB_INDEX_SYMBOL_KIND_FUNCTION)
 		continue;
-	      if (!is_static)
-		global_seen = true;
+	      break;
+	    case TYPES_DOMAIN:
+	      if (symbol_kind != GDB_INDEX_SYMBOL_KIND_TYPE)
+		continue;
+	      break;
+	    default:
+	      break;
 	    }
+	}
 
-	  /* Only check the symbol's kind if it has one.  */
-	  if (attrs_valid)
-	    {
-	      switch (kind)
-		{
-		case VARIABLES_DOMAIN:
-		  if (symbol_kind != GDB_INDEX_SYMBOL_KIND_VARIABLE)
-		    continue;
-		  break;
-		case FUNCTIONS_DOMAIN:
-		  if (symbol_kind != GDB_INDEX_SYMBOL_KIND_FUNCTION)
-		    continue;
-		  break;
-		case TYPES_DOMAIN:
-		  if (symbol_kind != GDB_INDEX_SYMBOL_KIND_TYPE)
-		    continue;
-		  break;
-		default:
-		  break;
-		}
-	    }
+      /* Don't crash on bad data.  */
+      if (cu_index >= (dwarf2_per_objfile->n_comp_units
+		       + dwarf2_per_objfile->n_type_units))
+	{
+	  complaint (&symfile_complaints,
+		     _(".gdb_index entry has bad CU index"
+		       " [in module %s]"), objfile_name (objfile));
+	  continue;
+	}
 
-	  /* Don't crash on bad data.  */
-	  if (cu_index >= (dwarf2_per_objfile->n_comp_units
-			   + dwarf2_per_objfile->n_type_units))
+      per_cu = dw2_get_cutu (cu_index);
+      if (file_matcher == NULL || per_cu->v.quick->mark)
+	{
+	  int symtab_was_null =
+	    (per_cu->v.quick->compunit_symtab == NULL);
+
+	  dw2_instantiate_symtab (per_cu);
+
+	  if (expansion_notify != NULL
+	      && symtab_was_null
+	      && per_cu->v.quick->compunit_symtab != NULL)
+	    expansion_notify (per_cu->v.quick->compunit_symtab);
+	}
+    }
+}
+
+static void
+dw2_expand_symtabs_matching
+  (struct objfile *objfile,
+   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
+   const lookup_name_info &lookup_name,
+   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
+   enum search_domain kind)
+{
+  int i;
+  offset_type iter;
+
+  dw2_setup (objfile);
+
+  /* index_table is NULL if OBJF_READNOW.  */
+  if (!dwarf2_per_objfile->index_table)
+    return;
+
+  if (file_matcher != NULL)
+    {
+      htab_up visited_found (htab_create_alloc (10, htab_hash_pointer,
+						htab_eq_pointer,
+						NULL, xcalloc, xfree));
+      htab_up visited_not_found (htab_create_alloc (10, htab_hash_pointer,
+						    htab_eq_pointer,
+						    NULL, xcalloc, xfree));
+
+      /* The rule is CUs specify all the files, including those used by
+	 any TU, so there's no need to scan TUs here.  */
+
+      for (i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
+	{
+	  int j;
+	  struct dwarf2_per_cu_data *per_cu = dw2_get_cu (i);
+	  struct quick_file_names *file_data;
+	  void **slot;
+
+	  QUIT;
+
+	  per_cu->v.quick->mark = 0;
+
+	  /* We only need to look at symtabs not already expanded.  */
+	  if (per_cu->v.quick->compunit_symtab)
+	    continue;
+
+	  file_data = dw2_get_file_names (per_cu);
+	  if (file_data == NULL)
+	    continue;
+
+	  if (htab_find (visited_not_found.get (), file_data) != NULL)
+	    continue;
+	  else if (htab_find (visited_found.get (), file_data) != NULL)
 	    {
-	      complaint (&symfile_complaints,
-			 _(".gdb_index entry has bad CU index"
-			   " [in module %s]"), objfile_name (objfile));
+	      per_cu->v.quick->mark = 1;
 	      continue;
 	    }
 
-	  per_cu = dw2_get_cutu (cu_index);
-	  if (file_matcher == NULL || per_cu->v.quick->mark)
+	  for (j = 0; j < file_data->num_file_names; ++j)
 	    {
-	      int symtab_was_null =
-		(per_cu->v.quick->compunit_symtab == NULL);
+	      const char *this_real_name;
 
-	      dw2_instantiate_symtab (per_cu);
+	      if (file_matcher (file_data->file_names[j], false))
+		{
+		  per_cu->v.quick->mark = 1;
+		  break;
+		}
+
+	      /* Before we invoke realpath, which can get expensive when many
+		 files are involved, do a quick comparison of the basenames.  */
+	      if (!basenames_may_differ
+		  && !file_matcher (lbasename (file_data->file_names[j]),
+				    true))
+		continue;
 
-	      if (expansion_notify != NULL
-		  && symtab_was_null
-		  && per_cu->v.quick->compunit_symtab != NULL)
+	      this_real_name = dw2_get_real_path (objfile, file_data, j);
+	      if (file_matcher (this_real_name, false))
 		{
-		  expansion_notify (per_cu->v.quick->compunit_symtab);
+		  per_cu->v.quick->mark = 1;
+		  break;
 		}
 	    }
+
+	  slot = htab_find_slot (per_cu->v.quick->mark
+				 ? visited_found.get ()
+				 : visited_not_found.get (),
+				 file_data, INSERT);
+	  *slot = file_data;
 	}
+    }
+
+  mapped_index &index = *dwarf2_per_objfile->index_table;
+
+  dw2_expand_symtabs_matching_symbol (index, lookup_name,
+				      symbol_matcher,
+				      kind, [&] (offset_type idx)
+    {
+      dw2_expand_marked_cus (index, idx, objfile, file_matcher,
+			     expansion_notify, kind);
+    });
 }
 
 /* A helper for dw2_find_pc_sect_compunit_symtab which finds the most specific
-- 
2.5.5


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

* Re: [PATCH 28/40] lookup_name_info::make_ignore_params
  2017-08-08 20:55   ` Keith Seitz
@ 2017-11-08 16:18     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:18 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/08/2017 09:55 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
>> 	unittests/lookup_name_info-selftests.c.
>> 	(SUBDIR_UNITTESTS_OBS): Add lookup_name_info-selftests.o.
>> 	* cp-support.c: Include "selftest.h".
>> 	(cp_remove_params_1): Rename from cp_remove_params.  Add
>> 	'require_param's parameter, and handle it.
>                       ^^
> typo
> 

Fixed.

>> @@ -833,12 +834,14 @@ cp_func_name (const char *full_name)
>>    return ret;
>>  }
>>  
>> -/* DEMANGLED_NAME is the name of a function, including parameters and
>> -   (optionally) a return type.  Return the name of the function without
>> -   parameters or return type, or NULL if we can not parse the name.  */
>> +/* Helper for cp_remove_params.  DEMANGLED_NAME is the name of a
>> +   function, including parameters and (optionally) a return type.
>> +   Return the name of the function without parameters or return type,
>> +   or NULL if we can not parse the name.  If COMPLETION_MODE is true,
>> +   then tolerate a non-existing or unbalanced parameter list.  */
> 
> s/COMPLETION_MODE/REQUIRE_PARAMS/ ?

Whoops, yes, it was named completion_mode at some point before
I posted this.

> 
>> -gdb::unique_xmalloc_ptr<char>
>> -cp_remove_params (const char *demangled_name)
>> +static gdb::unique_xmalloc_ptr<char>
>> +cp_remove_params_1 (const char *demangled_name, bool require_params)
>>  {
>>    bool done = false;
>>    struct demangle_component *ret_comp;
>> @@ -874,10 +877,60 @@ cp_remove_params (const char *demangled_name)
> [snip]
>> +/* DEMANGLED_NAME is the name of a function, (optionally) including
>> +   parameters and (optionally) a return type.  Return the name of the
>> +   function without parameters or return type, or NULL if we can not
>> +   parse the name.  If COMPLETION_MODE is true, then tolerate a
>> +   non-existing or unbalanced parameter list.  */
>> +
> 
> Shouldn't this comment be in cp-support.h?

Fixed.

> 
>> +gdb::unique_xmalloc_ptr<char>
>> +cp_remove_params_if_any (const char *qualified, bool completion_mode)
>> +{
>> +  /* Trying to remove parameters from the empty string fails.  If
>> +     we're completing / matching everything, avoid returning NULL
> [snip]
>> +  /* Shouldn't this parse?  Looks like a bug in
>> +     cp_demangled_name_to_comp.  */
> 
> Is there a bugzilla (or some other tracking) for this?

Indeed.  I've filed <https://sourceware.org/bugzilla/show_bug.cgi?id=22411>
now and added a reference here.

> 
>> +#if 0
>> +  CHECK ("A::foo<void(int)>::func(int)",
>> +	 "A::foo<void(int)>::func");
>> +#else
>> +  CHECK_INCOMPL ("A::foo<void(int)>::func(int)",
>> +		 "A::foo");
>> +#endif
>> +
>> +  CHECK_INCOMPL ("A::foo<void(int",
>> +		 "A::foo");
>> +
>> +#undef CHECK
>> +#undef CHECK_INCOMPL
>> +}
>> +
>> +} // namespace selftests
>> +
>> +#endif /* GDB_SELF_CHECK */
>> +
>>  /* Don't allow just "maintenance cplus".  */
>>  
>>  static  void
>> diff --git a/gdb/cp-support.h b/gdb/cp-support.h
>> index dd42415..1cef3f7 100644
>> --- a/gdb/cp-support.h
>> +++ b/gdb/cp-support.h
>> @@ -97,6 +97,9 @@ extern char *cp_func_name (const char *full_name);
>>  
>>  extern gdb::unique_xmalloc_ptr<char> cp_remove_params (const char *qualified);
>>  
>> +extern gdb::unique_xmalloc_ptr<char> cp_remove_params_if_any
>> +  (const char *qualified, bool completion_mode);
> 
> [Add comment from cp-support.c?]

Fixed.

> 
>> +
>>  extern struct symbol **make_symbol_overload_list (const char *,
>>  						  const char *);
>>  
>> diff --git a/gdb/symtab.h b/gdb/symtab.h
>> index 4c8b1f6..a452804 100644
>> --- a/gdb/symtab.h
>> +++ b/gdb/symtab.h
>> @@ -176,6 +178,16 @@ class lookup_name_info final
>>    symbol_name_match_type match_type () const { return m_match_type; }
>>    bool completion_mode () const { return m_completion_mode; }
>>    const std::string &name () const { return m_name; }
>> +  const bool ignore_parameters () const { return m_ignore_parameters; }
>> +
>> +  /* Return a version of this lookup name that is usable with
>> +     comparisons against symbols have have no parameter info, such as
>                                     ^^^^^^^^^
> typo

Fixed.

> 
>> +     psymbols and GDB index symbols.  */
>> +  lookup_name_info make_ignore_params () const
>> +  {
>> +    return lookup_name_info (m_name, m_match_type, m_completion_mode,
>> +			     true /* ignore params */);
>> +  }
>>  
>>    /* Get the search name hash for searches in language LANG.  */
>>    unsigned int search_name_hash (language lang) const
> 

Here's what I pushed.

From c62446b12b32ce57d2b40cdb0c1baa7fc1677d82 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 8 Nov 2017 14:49:10 +0000
Subject: [PATCH] lookup_name_info::make_ignore_params

A few places in the completion code look for a "(" to find a
function's parameter list, in order to strip it, because psymtabs (and
gdb index) don't include parameter info in the symbol names.

See compare_symbol_name and
default_collect_symbol_completion_matches_break_on.

This is too naive.  Consider:

 ns_overload2_test::([TAB]

We'd want to complete that to:
 ns_overload2_test::(anonymous namespace)::struct_overload2_test

Or:

 b (anonymous namespace)::[TAB]

That currently completes to:

 b (anonymous namespace)

Which is obviously broken.  This patch makes that work.

Also, the current compare_symbol_name hack means that while this
works:

 "b function([TAB]"  ->  "b function()"

This does not:

 "b function ([TAB]"

This patch fixes that.  Whitespace "ignoring" now Just Works, i.e.,
assuming a symbol named "function(int, long)", this:
 b function (   int , lon[TAB]
completes to:
 b function (   int , long)

To address all of this, this patch builds on top of the rest of the
series, and pushes the responsibility of stripping parameters from a
lookup name to the new lookup_name_info object, where we can apply
per-language rules.  Also note that we now only make a version of the
lookup name with parameters stripped out where it's actually required
to do that, in the psymtab and GDB index code.

For C++, the right way to strip parameters is with "cp_remove_params",
which uses a real parser (cp-name-parser.y) to split the name into a
component tree and then discards parameters.

The trouble for completion is that in that case we have an incomplete
name, like "foo::func(int" and thus cp_remove_params throws an error.

This patch sorts that by adding a cp_remove_params_if_any variant of
cp_remove_params that tries removing characters from the end of the
string until cp_remove_params works.  So cp_remove_params_if_any
behaves like this:

With a complete name:

   "foo::func(int)"  => foo::func(int)  # cp_remove_params_1 succeeds the first time.

With an incomplete name:

   "foo::func(int"  => NULL             # cp_remove_params fails the first time.
   "foo::func(in"   => NULL             # and again...
   "foo::func(i"    => NULL             # and again...
   "foo::func("     => NULL             # and again...
   "foo::func"      => "foo::func"      # success!

Note that even if this approach removes significant rightmost
characters, it's still OK, because this parameter stripping is only
necessary for psymtabs and gdb index, where we're determining whether
to expand a symbol table.  Say cp_remove_params_if_any returned
"foo::" above for "foo::func(int".  That'd cause us to expand more
symtabs than ideal (because we'd expand all symtabs with symbols that
start with "foo::", not just "foo::func"), but then when we actually
look for completion matches, we'd still use the original lookup name,
with parameter information ["foo::func(int"], and thus we'll return no
false positive to the user.  Whether the stripping works as intended
and doesn't strip too much is thus covered by a unit test instead of a
testsuite test.

The "if_any" part of the name refers to the fact that while
cp_remove_params returns NULL if the input name has no parameters in
the first place, like:

   "foo::func"      => NULL         # cp_remove_params

cp_remove_params_if_any still returns the function name:

   "foo::func"      => "foo::func"  # cp_remove_params_if_any

gdb/ChangeLog:
2017-11-08  Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
	unittests/lookup_name_info-selftests.c.
	(SUBDIR_UNITTESTS_OBS): Add lookup_name_info-selftests.o.
	* cp-support.c: Include "selftest.h".
	(cp_remove_params_1): Rename from cp_remove_params.  Add
	'require_param' parameter, and handle it.
	(cp_remove_params): Reimplement.
	(cp_remove_params_if_any): New.
	(selftests::quote): New.
	(selftests::check_remove_params): New.
	(selftests::test_cp_remove_params): New.
	(_initialize_cp_support): Install
	selftests::test_cp_remove_params.
	* cp-support.h (cp_remove_params_if_any): Declare.
	* dwarf2read.c :Include "selftest.h".
	(dw2_expand_symtabs_matching_symbol): Use
	lookup_name_info::make_ignore_params.
	(selftests::dw2_expand_symtabs_matching::mock_mapped_index)
	(selftests::dw2_expand_symtabs_matching::string_or_null)
	(selftests::dw2_expand_symtabs_matching::check_match)
	(selftests::dw2_expand_symtabs_matching::test_symbols)
	(selftests::dw2_expand_symtabs_matching::run_test): New.
	(_initialize_dwarf2_read): Register
	selftests::dw2_expand_symtabs_matching::run_test.
	* psymtab.c (psym_expand_symtabs_matching): Use
	lookup_name_info::make_ignore_params.
	* symtab.c (demangle_for_lookup_info::demangle_for_lookup_info):
	If the lookup name wants to ignore parameters, strip them.
	(compare_symbol_name): Remove sym_text/sym_text_len parameters and
	code handling '('.
	(completion_list_add_name): Don't pass down sym_text/sym_text_len.
	(default_collect_symbol_completion_matches_break_on): Don't try to
	strip parameters.
	* symtab.h (lookup_name_info::lookup_name_info): Add
	'ignore_parameters' parameter.
	(lookup_name_info::ignore_parameters)
	(lookup_name_info::make_ignore_params): New methods.
	(lookup_name_info::m_ignore_parameters): New field.
	* unittests/lookup_name_info-selftests.c: New file.
---
 gdb/ChangeLog                              |  42 ++++
 gdb/Makefile.in                            |   2 +
 gdb/cp-support.c                           | 187 +++++++++++++++++-
 gdb/cp-support.h                           |   8 +
 gdb/dwarf2read.c                           | 300 ++++++++++++++++++++++++++++-
 gdb/psymtab.c                              |   4 +-
 gdb/symtab.c                               |  65 ++-----
 gdb/symtab.h                               |  15 +-
 gdb/unittests/lookup_name_info-selftests.c | 111 +++++++++++
 9 files changed, 675 insertions(+), 59 deletions(-)
 create mode 100644 gdb/unittests/lookup_name_info-selftests.c

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9776cf2..8abe455 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,47 @@
 2017-11-08  Pedro Alves  <palves@redhat.com>
 
+	* Makefile.in (SUBDIR_UNITTESTS_SRCS): Add
+	unittests/lookup_name_info-selftests.c.
+	(SUBDIR_UNITTESTS_OBS): Add lookup_name_info-selftests.o.
+	* cp-support.c: Include "selftest.h".
+	(cp_remove_params_1): Rename from cp_remove_params.  Add
+	'require_param' parameter, and handle it.
+	(cp_remove_params): Reimplement.
+	(cp_remove_params_if_any): New.
+	(selftests::quote): New.
+	(selftests::check_remove_params): New.
+	(selftests::test_cp_remove_params): New.
+	(_initialize_cp_support): Install
+	selftests::test_cp_remove_params.
+	* cp-support.h (cp_remove_params_if_any): Declare.
+	* dwarf2read.c :Include "selftest.h".
+	(dw2_expand_symtabs_matching_symbol): Use
+	lookup_name_info::make_ignore_params.
+	(selftests::dw2_expand_symtabs_matching::mock_mapped_index)
+	(selftests::dw2_expand_symtabs_matching::string_or_null)
+	(selftests::dw2_expand_symtabs_matching::check_match)
+	(selftests::dw2_expand_symtabs_matching::test_symbols)
+	(selftests::dw2_expand_symtabs_matching::run_test): New.
+	(_initialize_dwarf2_read): Register
+	selftests::dw2_expand_symtabs_matching::run_test.
+	* psymtab.c (psym_expand_symtabs_matching): Use
+	lookup_name_info::make_ignore_params.
+	* symtab.c (demangle_for_lookup_info::demangle_for_lookup_info):
+	If the lookup name wants to ignore parameters, strip them.
+	(compare_symbol_name): Remove sym_text/sym_text_len parameters and
+	code handling '('.
+	(completion_list_add_name): Don't pass down sym_text/sym_text_len.
+	(default_collect_symbol_completion_matches_break_on): Don't try to
+	strip parameters.
+	* symtab.h (lookup_name_info::lookup_name_info): Add
+	'ignore_parameters' parameter.
+	(lookup_name_info::ignore_parameters)
+	(lookup_name_info::make_ignore_params): New methods.
+	(lookup_name_info::m_ignore_parameters): New field.
+	* unittests/lookup_name_info-selftests.c: New file.
+
+2017-11-08  Pedro Alves  <palves@redhat.com>
+
 	* dwarf2read.c (dw2_expand_marked_cus)
 	(dw2_expand_symtabs_matching_symbol): Remove forward declarations.
 	(dw2_expand_symtabs_matching): Move further below.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6fe9b38..5e01816 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -530,6 +530,7 @@ SUBDIR_UNITTESTS_SRCS = \
 	unittests/common-utils-selftests.c \
 	unittests/environ-selftests.c \
 	unittests/function-view-selftests.c \
+	unittests/lookup_name_info-selftests.c \
 	unittests/memrange-selftests.c \
 	unittests/offset-type-selftests.c \
 	unittests/optional-selftests.c \
@@ -542,6 +543,7 @@ SUBDIR_UNITTESTS_OBS = \
 	common-utils-selftests.o \
 	environ-selftests.o \
 	function-view-selftests.o \
+	lookup_name_info-selftests.o \
 	memrange-selftests.o \
 	offset-type-selftests.o \
 	optional-selftests.o \
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index defe509..1cab69b 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -36,6 +36,7 @@
 #include <signal.h>
 #include "gdb_setjmp.h"
 #include "safe-ctype.h"
+#include "selftest.h"
 
 #define d_left(dc) (dc)->u.s_binary.left
 #define d_right(dc) (dc)->u.s_binary.right
@@ -830,12 +831,14 @@ cp_func_name (const char *full_name)
   return ret.release ();
 }
 
-/* DEMANGLED_NAME is the name of a function, including parameters and
-   (optionally) a return type.  Return the name of the function without
-   parameters or return type, or NULL if we can not parse the name.  */
+/* Helper for cp_remove_params.  DEMANGLED_NAME is the name of a
+   function, including parameters and (optionally) a return type.
+   Return the name of the function without parameters or return type,
+   or NULL if we can not parse the name.  If REQUIRE_PARAMS is false,
+   then tolerate a non-existing or unbalanced parameter list.  */
 
-gdb::unique_xmalloc_ptr<char>
-cp_remove_params (const char *demangled_name)
+static gdb::unique_xmalloc_ptr<char>
+cp_remove_params_1 (const char *demangled_name, bool require_params)
 {
   bool done = false;
   struct demangle_component *ret_comp;
@@ -871,10 +874,56 @@ cp_remove_params (const char *demangled_name)
   /* What we have now should be a function.  Return its name.  */
   if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME)
     ret = cp_comp_to_string (d_left (ret_comp), 10);
+  else if (!require_params
+	   && (ret_comp->type == DEMANGLE_COMPONENT_NAME
+	       || ret_comp->type == DEMANGLE_COMPONENT_QUAL_NAME
+	       || ret_comp->type == DEMANGLE_COMPONENT_TEMPLATE))
+    ret = cp_comp_to_string (ret_comp, 10);
 
   return ret;
 }
 
+/* DEMANGLED_NAME is the name of a function, including parameters and
+   (optionally) a return type.  Return the name of the function
+   without parameters or return type, or NULL if we can not parse the
+   name.  */
+
+gdb::unique_xmalloc_ptr<char>
+cp_remove_params (const char *demangled_name)
+{
+  return cp_remove_params_1 (demangled_name, true);
+}
+
+/* See cp-support.h.  */
+
+gdb::unique_xmalloc_ptr<char>
+cp_remove_params_if_any (const char *demangled_name, bool completion_mode)
+{
+  /* Trying to remove parameters from the empty string fails.  If
+     we're completing / matching everything, avoid returning NULL
+     which would make callers interpret the result as an error.  */
+  if (demangled_name[0] == '\0' && completion_mode)
+    return gdb::unique_xmalloc_ptr<char> (xstrdup (""));
+
+  gdb::unique_xmalloc_ptr<char> without_params
+    = cp_remove_params_1 (demangled_name, false);
+
+  if (without_params == NULL && completion_mode)
+    {
+      std::string copy = demangled_name;
+
+      while (!copy.empty ())
+	{
+	  copy.pop_back ();
+	  without_params = cp_remove_params_1 (copy.c_str (), false);
+	  if (without_params != NULL)
+	    break;
+	}
+    }
+
+  return without_params;
+}
+
 /* Here are some random pieces of trivia to keep in mind while trying
    to take apart demangled names:
 
@@ -1600,6 +1649,129 @@ cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
   return cp_fq_symbol_name_matches;
 }
 
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+/* If non-NULL, return STR wrapped in quotes.  Otherwise, return a
+   "<null>" string (with no quotes).  */
+
+static std::string
+quote (const char *str)
+{
+  if (str != NULL)
+    return std::string (1, '\"') + str + '\"';
+  else
+    return "<null>";
+}
+
+/* Check that removing parameter info out of NAME produces EXPECTED.
+   COMPLETION_MODE indicates whether we're testing normal and
+   completion mode.  FILE and LINE are used to provide better test
+   location information in case ithe check fails.  */
+
+static void
+check_remove_params (const char *file, int line,
+		      const char *name, const char *expected,
+		      bool completion_mode)
+{
+  gdb::unique_xmalloc_ptr<char> result
+    = cp_remove_params_if_any (name, completion_mode);
+
+  if ((expected == NULL) != (result == NULL)
+      || (expected != NULL
+	  && strcmp (result.get (), expected) != 0))
+    {
+      error (_("%s:%d: make-paramless self-test failed: (completion=%d) "
+	       "\"%s\" -> %s, expected %s"),
+	     file, line, completion_mode, name,
+	     quote (result.get ()).c_str (), quote (expected).c_str ());
+    }
+}
+
+/* Entry point for cp_remove_params unit tests.  */
+
+static void
+test_cp_remove_params ()
+{
+  /* Check that removing parameter info out of NAME produces EXPECTED.
+     Checks both normal and completion modes.  */
+#define CHECK(NAME, EXPECTED)						\
+  do									\
+    {									\
+      check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, false);	\
+      check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, true);	\
+    }									\
+  while (0)
+
+  /* Similar, but used when NAME is incomplete -- i.e., is has
+     unbalanced parentheses.  In this case, looking for the exact name
+     should fail / return empty.  */
+#define CHECK_INCOMPL(NAME, EXPECTED)					\
+  do									\
+    {									\
+      check_remove_params (__FILE__, __LINE__, NAME, NULL, false);	\
+      check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, true);	\
+    }									\
+  while (0)
+
+  CHECK ("function()", "function");
+  CHECK_INCOMPL ("function(", "function");
+  CHECK ("function() const", "function");
+
+  CHECK ("(anonymous namespace)::A::B::C",
+	 "(anonymous namespace)::A::B::C");
+
+  CHECK ("A::(anonymous namespace)",
+	 "A::(anonymous namespace)");
+
+  CHECK_INCOMPL ("A::(anonymou", "A");
+
+  CHECK ("A::foo<int>()",
+	 "A::foo<int>");
+
+  CHECK_INCOMPL ("A::foo<int>(",
+		 "A::foo<int>");
+
+  CHECK ("A::foo<(anonymous namespace)::B>::func(int)",
+	 "A::foo<(anonymous namespace)::B>::func");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>::func(in",
+		 "A::foo<(anonymous namespace)::B>::func");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>::",
+		 "A::foo<(anonymous namespace)::B>");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>:",
+		 "A::foo<(anonymous namespace)::B>");
+
+  CHECK ("A::foo<(anonymous namespace)::B>",
+	 "A::foo<(anonymous namespace)::B>");
+
+  CHECK_INCOMPL ("A::foo<(anonymous namespace)::B",
+		 "A::foo");
+
+  /* Shouldn't this parse?  Looks like a bug in
+     cp_demangled_name_to_comp.  See PR c++/22411.  */
+#if 0
+  CHECK ("A::foo<void(int)>::func(int)",
+	 "A::foo<void(int)>::func");
+#else
+  CHECK_INCOMPL ("A::foo<void(int)>::func(int)",
+		 "A::foo");
+#endif
+
+  CHECK_INCOMPL ("A::foo<void(int",
+		 "A::foo");
+
+#undef CHECK
+#undef CHECK_INCOMPL
+}
+
+} // namespace selftests
+
+#endif /* GDB_SELF_CHECK */
+
 /* Don't allow just "maintenance cplus".  */
 
 static  void
@@ -1682,4 +1854,9 @@ display the offending symbol."),
 			   &maintenance_set_cmdlist,
 			   &maintenance_show_cmdlist);
 #endif
+
+#if GDB_SELF_TEST
+  selftests::register_test ("cp_remove_params",
+			    selftests::test_cp_remove_params);
+#endif
 }
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index a699a80..44d8269 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -98,6 +98,14 @@ extern char *cp_func_name (const char *full_name);
 extern gdb::unique_xmalloc_ptr<char> cp_remove_params
   (const char *demanged_name);
 
+/* DEMANGLED_NAME is the name of a function, (optionally) including
+   parameters and (optionally) a return type.  Return the name of the
+   function without parameters or return type, or NULL if we can not
+   parse the name.  If COMPLETION_MODE is true, then tolerate a
+   non-existing or unbalanced parameter list.  */
+extern gdb::unique_xmalloc_ptr<char> cp_remove_params_if_any
+  (const char *demangled_name, bool completion_mode);
+
 extern struct symbol **make_symbol_overload_list (const char *,
 						  const char *);
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index d715082..389d8f7 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -81,6 +81,7 @@
 #include <algorithm>
 #include <unordered_set>
 #include <unordered_map>
+#include "selftest.h"
 
 typedef struct symbol *symbolp;
 DEF_VEC_P (symbolp);
@@ -4198,13 +4199,15 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
 static void
 dw2_expand_symtabs_matching_symbol
   (mapped_index &index,
-   const lookup_name_info &lookup_name,
+   const lookup_name_info &lookup_name_in,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    enum search_domain kind,
    gdb::function_view<void (offset_type)> match_callback)
 {
+  lookup_name_info lookup_name_without_params
+    = lookup_name_in.make_ignore_params ();
   gdb_index_symbol_name_matcher lookup_name_matcher
-    (lookup_name);
+    (lookup_name_without_params);
 
   auto *name_cmp = case_sensitivity == case_sensitive_on ? strcmp : strcasecmp;
 
@@ -4262,7 +4265,7 @@ dw2_expand_symtabs_matching_symbol
     }
 
   const char *cplus
-    = lookup_name.cplus ().lookup_name ().c_str ();
+    = lookup_name_without_params.cplus ().lookup_name ().c_str ();
 
   /* Comparison function object for lower_bound that matches against a
      given symbol name.  */
@@ -4290,7 +4293,7 @@ dw2_expand_symtabs_matching_symbol
   /* Find the lower bound.  */
   auto lower = [&] ()
     {
-      if (lookup_name.completion_mode () && cplus[0] == '\0')
+      if (lookup_name_in.completion_mode () && cplus[0] == '\0')
 	return begin;
       else
 	return std::lower_bound (begin, end, cplus, lookup_compare_lower);
@@ -4299,7 +4302,7 @@ dw2_expand_symtabs_matching_symbol
   /* Find the upper bound.  */
   auto upper = [&] ()
     {
-      if (lookup_name.completion_mode ())
+      if (lookup_name_in.completion_mode ())
 	{
 	  /* The string frobbing below won't work if the string is
 	     empty.  We don't need it then, anyway -- if we're
@@ -4365,6 +4368,288 @@ dw2_expand_symtabs_matching_symbol
   static_assert (sizeof (prev) > sizeof (offset_type), "");
 }
 
+#if GDB_SELF_TEST
+
+namespace selftests { namespace dw2_expand_symtabs_matching {
+
+/* A wrapper around mapped_index that builds a mock mapped_index, from
+   the symbol list passed as parameter to the constructor.  */
+class mock_mapped_index
+{
+public:
+  template<size_t N>
+  mock_mapped_index (const char *(&symbols)[N])
+    : mock_mapped_index (symbols, N)
+  {}
+
+  /* Access the built index.  */
+  mapped_index &index ()
+  { return m_index; }
+
+  /* Disable copy.  */
+  mock_mapped_index(const mock_mapped_index &) = delete;
+  void operator= (const mock_mapped_index &) = delete;
+
+private:
+  mock_mapped_index (const char **symbols, size_t symbols_size)
+  {
+    /* No string can live at offset zero.  Add a dummy entry.  */
+    obstack_grow_str0 (&m_constant_pool, "");
+
+    for (size_t i = 0; i < symbols_size; i++)
+      {
+	const char *sym = symbols[i];
+	size_t offset = obstack_object_size (&m_constant_pool);
+	obstack_grow_str0 (&m_constant_pool, sym);
+	m_symbol_table.push_back (offset);
+	m_symbol_table.push_back (0);
+      };
+
+    m_index.constant_pool = (const char *) obstack_base (&m_constant_pool);
+    m_index.symbol_table = m_symbol_table.data ();
+    m_index.symbol_table_slots = m_symbol_table.size () / 2;
+  }
+
+public:
+  /* The built mapped_index.  */
+  mapped_index m_index{};
+
+  /* The storage that the built mapped_index uses for symbol and
+     constant pool tables.  */
+  std::vector<offset_type> m_symbol_table;
+  auto_obstack m_constant_pool;
+};
+
+/* Convenience function that converts a NULL pointer to a "<null>"
+   string, to pass to print routines.  */
+
+static const char *
+string_or_null (const char *str)
+{
+  return str != NULL ? str : "<null>";
+}
+
+/* Check if a lookup_name_info built from
+   NAME/MATCH_TYPE/COMPLETION_MODE matches the symbols in the mock
+   index.  EXPECTED_LIST is the list of expected matches, in expected
+   matching order.  If no match expected, then an empty list is
+   specified.  Returns true on success.  On failure prints a warning
+   indicating the file:line that failed, and returns false.  */
+
+static bool
+check_match (const char *file, int line,
+	     mock_mapped_index &mock_index,
+	     const char *name, symbol_name_match_type match_type,
+	     bool completion_mode,
+	     std::initializer_list<const char *> expected_list)
+{
+  lookup_name_info lookup_name (name, match_type, completion_mode);
+
+  bool matched = true;
+
+  auto mismatch = [&] (const char *expected_str,
+		       const char *got)
+  {
+    warning (_("%s:%d: match_type=%s, looking-for=\"%s\", "
+	       "expected=\"%s\", got=\"%s\"\n"),
+	     file, line,
+	     (match_type == symbol_name_match_type::FULL
+	      ? "FULL" : "WILD"),
+	     name, string_or_null (expected_str), string_or_null (got));
+    matched = false;
+  };
+
+  auto expected_it = expected_list.begin ();
+  auto expected_end = expected_list.end ();
+
+  dw2_expand_symtabs_matching_symbol (mock_index.index (), lookup_name,
+				      NULL, ALL_DOMAIN,
+				      [&] (offset_type idx)
+  {
+    const char *matched_name = mock_index.index ().symbol_name_at (idx);
+    const char *expected_str
+      = expected_it == expected_end ? NULL : *expected_it++;
+
+    if (expected_str == NULL || strcmp (expected_str, matched_name) != 0)
+      mismatch (expected_str, matched_name);
+  });
+
+  const char *expected_str
+  = expected_it == expected_end ? NULL : *expected_it++;
+  if (expected_str != NULL)
+    mismatch (expected_str, NULL);
+
+  return matched;
+}
+
+/* The symbols added to the mock mapped_index for testing (in
+   canonical form).  */
+static const char *test_symbols[] = {
+  "function",
+  "std::bar",
+  "std::zfunction",
+  "std::zfunction2",
+  "w1::w2",
+  "ns::foo<char*>",
+  "ns::foo<int>",
+  "ns::foo<long>",
+
+  /* A name with all sorts of complications.  Starts with "z" to make
+     it easier for the completion tests below.  */
+#define Z_SYM_NAME \
+  "z::std::tuple<(anonymous namespace)::ui*, std::bar<(anonymous namespace)::ui> >" \
+    "::tuple<(anonymous namespace)::ui*, " \
+    "std::default_delete<(anonymous namespace)::ui>, void>"
+
+  Z_SYM_NAME
+};
+
+static void
+run_test ()
+{
+  mock_mapped_index mock_index (test_symbols);
+
+  /* We let all tests run until the end even if some fails, for debug
+     convenience.  */
+  bool any_mismatch = false;
+
+  /* Create the expected symbols list (an initializer_list).  Needed
+     because lists have commas, and we need to pass them to CHECK,
+     which is a macro.  */
+#define EXPECT(...) { __VA_ARGS__ }
+
+  /* Wrapper for check_match that passes down the current
+     __FILE__/__LINE__.  */
+#define CHECK_MATCH(NAME, MATCH_TYPE, COMPLETION_MODE, EXPECTED_LIST)	\
+  any_mismatch |= !check_match (__FILE__, __LINE__,			\
+				mock_index,				\
+				NAME, MATCH_TYPE, COMPLETION_MODE,	\
+				EXPECTED_LIST)
+
+  /* Identity checks.  */
+  for (const char *sym : test_symbols)
+    {
+      /* Should be able to match all existing symbols.  */
+      CHECK_MATCH (sym, symbol_name_match_type::FULL, false,
+		   EXPECT (sym));
+
+      /* Should be able to match all existing symbols with
+	 parameters.  */
+      std::string with_params = std::string (sym) + "(int)";
+      CHECK_MATCH (with_params.c_str (), symbol_name_match_type::FULL, false,
+		   EXPECT (sym));
+
+      /* Should be able to match all existing symbols with
+	 parameters and qualifiers.  */
+      with_params = std::string (sym) + " ( int ) const";
+      CHECK_MATCH (with_params.c_str (), symbol_name_match_type::FULL, false,
+		   EXPECT (sym));
+
+      /* This should really find sym, but cp-name-parser.y doesn't
+	 know about lvalue/rvalue qualifiers yet.  */
+      with_params = std::string (sym) + " ( int ) &&";
+      CHECK_MATCH (with_params.c_str (), symbol_name_match_type::FULL, false,
+		   {});
+    }
+
+  /* Check that completion mode works at each prefix of the expected
+     symbol name.  */
+  {
+    static const char str[] = "function(int)";
+    size_t len = strlen (str);
+    std::string lookup;
+
+    for (size_t i = 1; i < len; i++)
+      {
+	lookup.assign (str, i);
+	CHECK_MATCH (lookup.c_str (), symbol_name_match_type::FULL, true,
+		     EXPECT ("function"));
+      }
+  }
+
+  /* While "w" is a prefix of both components, the match function
+     should still only be called once.  */
+  {
+    CHECK_MATCH ("w", symbol_name_match_type::FULL, true,
+		 EXPECT ("w1::w2"));
+  }
+
+  /* Same, with a "complicated" symbol.  */
+  {
+    static const char str[] = Z_SYM_NAME;
+    size_t len = strlen (str);
+    std::string lookup;
+
+    for (size_t i = 1; i < len; i++)
+      {
+	lookup.assign (str, i);
+	CHECK_MATCH (lookup.c_str (), symbol_name_match_type::FULL, true,
+		     EXPECT (Z_SYM_NAME));
+      }
+  }
+
+  /* In FULL mode, an incomplete symbol doesn't match.  */
+  {
+    CHECK_MATCH ("std::zfunction(int", symbol_name_match_type::FULL, false,
+		 {});
+  }
+
+  /* A complete symbol with parameters matches any overload, since the
+     index has no overload info.  */
+  {
+    CHECK_MATCH ("std::zfunction(int)", symbol_name_match_type::FULL, true,
+		 EXPECT ("std::zfunction", "std::zfunction2"));
+  }
+
+  /* Check that whitespace is ignored appropriately.  A symbol with a
+     template argument list. */
+  {
+    static const char expected[] = "ns::foo<int>";
+    CHECK_MATCH ("ns :: foo < int > ", symbol_name_match_type::FULL, false,
+		 EXPECT (expected));
+  }
+
+  /* Check that whitespace is ignored appropriately.  A symbol with a
+     template argument list that includes a pointer.  */
+  {
+    static const char expected[] = "ns::foo<char*>";
+    /* Try both completion and non-completion modes.  */
+    static const bool completion_mode[2] = {false, true};
+    for (size_t i = 0; i < 2; i++)
+      {
+	CHECK_MATCH ("ns :: foo < char * >", symbol_name_match_type::FULL,
+		     completion_mode[i], EXPECT (expected));
+
+	CHECK_MATCH ("ns :: foo < char * > (int)", symbol_name_match_type::FULL,
+		     completion_mode[i], EXPECT (expected));
+      }
+  }
+
+  {
+    /* Check method qualifiers are ignored.  */
+    static const char expected[] = "ns::foo<char*>";
+    CHECK_MATCH ("ns :: foo < char * >  ( int ) const",
+		 symbol_name_match_type::FULL, true, EXPECT (expected));
+    CHECK_MATCH ("ns :: foo < char * >  ( int ) &&",
+		 symbol_name_match_type::FULL, true, EXPECT (expected));
+  }
+
+  /* Test lookup names that don't match anything.  */
+  {
+    CHECK_MATCH ("doesntexist", symbol_name_match_type::FULL, false,
+		 {});
+  }
+
+  SELF_CHECK (!any_mismatch);
+
+#undef EXPECT
+#undef CHECK_MATCH
+}
+
+}} // namespace selftests::dw2_expand_symtabs_matching
+
+#endif /* GDB_SELF_TEST */
+
 /* Helper for dw2_expand_matching symtabs.  Called on each symbol
    matched, to expand corresponding CUs that were marked.  IDX is the
    index of the symbol name that matched.  */
@@ -24454,4 +24739,9 @@ Usage: save gdb-index DIRECTORY"),
 					&dwarf2_block_frame_base_locexpr_funcs);
   dwarf2_loclist_block_index = register_symbol_block_impl (LOC_BLOCK,
 					&dwarf2_block_frame_base_loclist_funcs);
+
+#if GDB_SELF_TEST
+  selftests::register_test ("dw2_expand_symtabs_matching",
+			    selftests::dw2_expand_symtabs_matching::run_test);
+#endif
 }
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index d7881d2..29d40dc 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -1390,13 +1390,15 @@ static void
 psym_expand_symtabs_matching
   (struct objfile *objfile,
    gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
-   const lookup_name_info &lookup_name,
+   const lookup_name_info &lookup_name_in,
    gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
    gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
    enum search_domain domain)
 {
   struct partial_symtab *ps;
 
+  lookup_name_info lookup_name = lookup_name_in.make_ignore_params ();
+
   /* Clear the search flags.  */
   ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, ps)
     {
diff --git a/gdb/symtab.c b/gdb/symtab.c
index aecee8f..2d09f94 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -1766,6 +1766,20 @@ demangle_for_lookup_info::demangle_for_lookup_info
 {
   demangle_result_storage storage;
 
+  if (lookup_name.ignore_parameters () && lang == language_cplus)
+    {
+      gdb::unique_xmalloc_ptr<char> without_params
+	= cp_remove_params_if_any (lookup_name.name ().c_str (),
+				   lookup_name.completion_mode ());
+
+      if (without_params != NULL)
+	{
+	  m_demangled_name = demangle_for_lookup (without_params.get (),
+						  lang, storage);
+	  return;
+	}
+    }
+
   m_demangled_name = demangle_for_lookup (lookup_name.name ().c_str (),
 					  lang, storage);
 }
@@ -4619,20 +4633,11 @@ rbreak_command (const char *regexp, int from_tty)
 }
 \f
 
-/* Evaluate if NAME matches SYM_TEXT and SYM_TEXT_LEN.
-
-   Either sym_text[sym_text_len] != '(' and then we search for any
-   symbol starting with SYM_TEXT text.
-
-   Otherwise sym_text[sym_text_len] == '(' and then we require symbol name to
-   be terminated at that point.  Partial symbol tables do not have parameters
-   information.  */
+/* Evaluate if SYMNAME matches LOOKUP_NAME.  */
 
 static int
-compare_symbol_name (const char *name,
-		     language symbol_language,
+compare_symbol_name (const char *symbol_name, language symbol_language,
 		     const lookup_name_info &lookup_name,
-		     const char *sym_text, int sym_text_len,
 		     completion_match_result &match_res)
 {
   const language_defn *lang;
@@ -4654,23 +4659,7 @@ compare_symbol_name (const char *name,
   symbol_name_matcher_ftype *name_match
     = language_get_symbol_name_matcher (lang, lookup_name);
 
-  /* Clip symbols that cannot match.  */
-  if (!name_match (name, lookup_name, &match_res.match))
-    return 0;
-
-  if (sym_text[sym_text_len] == '(')
-    {
-      /* User searches for `name(someth...'.  Require NAME to be terminated.
-	 Normally psymtabs and gdbindex have no parameter types so '\0' will be
-	 present but accept even parameters presence.  In this case this
-	 function is in fact strcmp_iw but whitespace skipping is not supported
-	 for tab completion.  */
-
-      if (name[sym_text_len] != '\0' && name[sym_text_len] != '(')
-	return 0;
-    }
-
-  return 1;
+  return name_match (symbol_name, lookup_name, &match_res.match);
 }
 
 /*  See symtab.h.  */
@@ -4687,10 +4676,7 @@ completion_list_add_name (completion_tracker &tracker,
     = tracker.reset_completion_match_result ();
 
   /* Clip symbols that cannot match.  */
-  if (!compare_symbol_name (symname, symbol_language,
-			    lookup_name,
-			    sym_text, sym_text_len,
-			    match_res))
+  if (!compare_symbol_name (symname, symbol_language, lookup_name, match_res))
     return;
 
   /* Refresh SYMNAME from the match string.  It's potentially
@@ -5014,21 +5000,6 @@ default_collect_symbol_completion_matches_break_on
 
   sym_text_len = strlen (sym_text);
 
-  /* Prepare SYM_TEXT_LEN for compare_symbol_name.  */
-
-  if (current_language->la_language == language_cplus
-      || current_language->la_language == language_fortran)
-    {
-      /* These languages may have parameters entered by user but they are never
-	 present in the partial symbol tables.  */
-
-      const char *cs = (const char *) memchr (sym_text, '(', sym_text_len);
-
-      if (cs)
-	sym_text_len = cs - sym_text;
-    }
-  gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
-
   lookup_name_info lookup_name (std::string (sym_text, sym_text_len),
 				name_match_type, true);
 
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 5dfe953..8cd3496 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -167,9 +167,11 @@ class lookup_name_info final
   /* Create a new object.  */
   lookup_name_info (std::string name,
 		    symbol_name_match_type match_type,
-		    bool completion_mode = false)
+		    bool completion_mode = false,
+		    bool ignore_parameters = false)
     : m_match_type (match_type),
       m_completion_mode (completion_mode),
+      m_ignore_parameters (ignore_parameters),
       m_name (std::move (name))
   {}
 
@@ -177,6 +179,16 @@ class lookup_name_info final
   symbol_name_match_type match_type () const { return m_match_type; }
   bool completion_mode () const { return m_completion_mode; }
   const std::string &name () const { return m_name; }
+  const bool ignore_parameters () const { return m_ignore_parameters; }
+
+  /* Return a version of this lookup name that is usable with
+     comparisons against symbols have no parameter info, such as
+     psymbols and GDB index symbols.  */
+  lookup_name_info make_ignore_params () const
+  {
+    return lookup_name_info (m_name, m_match_type, m_completion_mode,
+			     true /* ignore params */);
+  }
 
   /* Get the search name hash for searches in language LANG.  */
   unsigned int search_name_hash (language lang) const
@@ -253,6 +265,7 @@ private:
   /* The lookup info as passed to the ctor.  */
   symbol_name_match_type m_match_type;
   bool m_completion_mode;
+  bool m_ignore_parameters;
   std::string m_name;
 
   /* Language-specific info.  These fields are filled lazily the first
diff --git a/gdb/unittests/lookup_name_info-selftests.c b/gdb/unittests/lookup_name_info-selftests.c
new file mode 100644
index 0000000..b35a020
--- /dev/null
+++ b/gdb/unittests/lookup_name_info-selftests.c
@@ -0,0 +1,111 @@
+/* Self tests for lookup_name_info for GDB, the GNU debugger.
+
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "selftest.h"
+#include "symtab.h"
+
+namespace selftests {
+namespace lookup_name {
+
+/* Check that removing parameter info out of NAME produces EXPECTED.
+   COMPLETION_MODE indicates whether we're testing normal and
+   completion mode.  FILE and LINE are used to provide better test
+   location information in case the check fails.  */
+
+static void
+check_make_paramless (const char *file, int line,
+		      enum language lang,
+		      const char *name, const char *expected,
+		      bool completion_mode)
+{
+  lookup_name_info lookup_name (name, symbol_name_match_type::FULL,
+				completion_mode, true /* ignore_parameters */);
+  const std::string &result = lookup_name.language_lookup_name (lang);
+
+  if (result != expected)
+    {
+      error (_("%s:%d: make-paramless self-test failed: (completion=%d, lang=%d) "
+	       "\"%s\" -> \"%s\", expected \"%s\""),
+	     file, line, completion_mode, lang, name,
+	     result.c_str (), expected);
+    }
+}
+
+static void
+run_tests ()
+{
+  /* Helper for CHECK and CHECK_INCOMPL.  */
+#define CHECK_1(INCOMPLETE, LANG, NAME, EXPECTED)			\
+  do									\
+    {									\
+      check_make_paramless (__FILE__, __LINE__,				\
+			    LANG, NAME,					\
+			    (INCOMPLETE) ? "" : (EXPECTED), false);	\
+      check_make_paramless (__FILE__, __LINE__,				\
+			    LANG, NAME, EXPECTED, true);		\
+    }									\
+  while (0)
+
+  /* Check that removing parameter info out of NAME produces EXPECTED.
+     Checks both normal and completion modes.  */
+#define CHECK(LANG, NAME, EXPECTED)		\
+  CHECK_1(false, LANG, NAME, EXPECTED)
+
+  /* Similar, but used when NAME is incomplete -- i.e., NAME has
+     unbalanced parentheses.  In this case, looking for the exact name
+     should fail / return empty.  */
+#define CHECK_INCOMPL(LANG, NAME, EXPECTED)				\
+  CHECK_1 (true, LANG, NAME, EXPECTED)
+
+  /* None of these languages support function overloading like C++
+     does, so building a nameless lookup name ends up being just the
+     same as any other lookup name.  Mainly this serves as smoke test
+     that C++-specific code doesn't mess up with other languages that
+     use some other scoping character ('.' vs '::').  */
+  CHECK (language_ada, "pck.ada_hello", "pck__ada_hello");
+  CHECK (language_go, "pck.go_hello", "pck.go_hello");
+  CHECK (language_fortran, "mod::func", "mod::func");
+
+  /* D does support function overloading similar to C++, but we're
+     missing support for stripping parameters.  At least make sure the
+     input name is preserved unmodified.  */
+  CHECK (language_d, "pck.d_hello", "pck.d_hello");
+
+  /* Just a few basic tests to make sure
+     lookup_name_info::make_paramless is well integrated with
+     cp_remove_params_if_any.  gdb/cp-support.c has comprehensive
+     testing of C++ specifics.  */
+  CHECK (language_cplus, "function()", "function");
+  CHECK (language_cplus, "function() const", "function");
+  CHECK (language_cplus, "A::B::C()", "A::B::C");
+  CHECK (language_cplus, "A::B::C", "A::B::C");
+
+#undef CHECK
+#undef CHECK_INCOMPL
+}
+
+}} // namespace selftests::lookup_name
+
+void
+_initialize_lookup_name_info_selftests ()
+{
+  selftests::register_test ("lookup_name_info",
+			    selftests::lookup_name::run_tests);
+}
-- 
2.5.5


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

* Re: [PATCH 29/40] Simplify completion_list_add_name | remove sym_text / sym_text_len
  2017-08-08 20:59   ` Keith Seitz
@ 2017-11-08 16:19     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:19 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches


On 08/08/2017 09:59 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* ada-lang.c (ada_make_symbol_completion_list): Remove text and
>> 	text_len locals and don't pass them down.
>> 	* symtab.c (completion_list_add_name): Remove
>> 	sym_text/sym_text_len parameters and adjust.
>> 	(completion_list_add_symbol, completion_list_add_msymbol)
>> 	(completion_list_objc_symbol, completion_list_add_fields)
>> 	(add_symtab_completions): Likewise.
>> 	(default_collect_symbol_completion_matches_break_on)
>> 	(collect_file_symbol_completion_matches): Remove sym_text_len
>> 	local and don't pass it down.
>> 	* symtab.h (completion_list_add_name): Remove
>> 	sym_text/sym_text_len parameters.
> 
> LGTM
> 

I've pushed this one in as well.

Thanks,
Pedro Alves

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

* Re: [PATCH 30/40] Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints
  2017-08-08 21:07   ` Keith Seitz
@ 2017-11-08 16:20     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:20 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/08/2017 10:07 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
>> however, we're currently passing down search_domain::ALL_DOMAIN when
>> we know we're looking for functions, for no good reason.  This patch
>> fixes that.
> 
> I have a similar patch on the compile branch.

That one I had no idea...  :-P

> 
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* linespec.c (iterate_over_all_matching_symtabs): Add
>> 	search_domain parameter.  Pass it down to expand_symtabs_matching.
>> 	(decode_objc): Request FUNCTIONS_DOMAIN symbols only.
>> 	(lookup_prefix_sym): Adjust by passing ALL_DOMAIN as
>> 	search_domain.
>> 	(add_all_symbol_names_from_pspace): Add search_domain parameter.
>> 	Pass it down.
>> 	(find_method, find_function_symbols): Request FUNCTIONS_DOMAIN
>> 	symbols.
>> 	(add_matching_symbols_to_info): Add search_domain parameter.  Pass
>> 	it down.
> 
> LGTM

Thanks, pushed.

Pedro Alves

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

* Re: [PATCH 33/40] Make the linespec/location completer ignore data symbols
  2017-08-09 15:42   ` Keith Seitz
@ 2017-11-08 16:22     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-08 16:22 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/09/2017 04:42 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>> diff --git a/gdb/symtab.c b/gdb/symtab.c
>> index bbc317d..52cf70a 100644
>> --- a/gdb/symtab.c
>> +++ b/gdb/symtab.c
>> @@ -5034,11 +5034,44 @@ completion_list_add_fields (completion_tracker &tracker,
>>      }
>>  }
>>  
>> +/* See symtab.h.  */
>> +
>> +bool
>> +symbol_is_function_or_method (symbol *sym)
>> +{
>> +  switch (TYPE_CODE (SYMBOL_TYPE (sym)))
>> +    {
>> +    case TYPE_CODE_FUNC:
>> +    case TYPE_CODE_METHOD:
>> +      return true;
>> +    default:
>> +      return false;
>> +    }
>> +}
>> +
>> +/* Return whether MSYMBOL is a function/method.  */
>> +
> 
> I think "See symtab.h." is more customary here?

Indeed, fixed.

> 
>> +bool
>> +symbol_is_function_or_method (minimal_symbol *msymbol)
>> +{
>> +  switch (MSYMBOL_TYPE (msymbol))
>> +    {
>> +    case mst_text:
>> +    case mst_text_gnu_ifunc:
>> +    case mst_solib_trampoline:
>> +    case mst_file_text:
>> +      return true;
>> +    default:
>> +      return false;
>> +    }
>> +}
>> +
>>  /* Add matching symbols from SYMTAB to the current completion list.  */
>>  
>>  static void
>>  add_symtab_completions (struct compunit_symtab *cust,
>>  			completion_tracker &tracker,
>> +			complete_symbol_mode mode,
>>  			const lookup_name_info &lookup_name,
>>  			const char *text, const char *word,
>>  			enum type_code code)
> 
> Otherwise, LGTM.

I pushed this one in too, even though I haven't pushed patches
#31 nor #32 yet.  (Because I haven't yet addressed your reviews).
(I don't really recall why I originally ordered this one after.
There seems to be no good reason.)

Thanks,
Pedro Alves

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

* Re: [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching
  2017-11-08 16:10                 ` Pedro Alves
@ 2017-11-08 22:15                   ` Joel Brobecker
  0 siblings, 0 replies; 183+ messages in thread
From: Joel Brobecker @ 2017-11-08 22:15 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Keith Seitz, gdb-patches

> Phew!  I admit that I was worried about your internal
> testsuites.  :-)

:-). For full disclosure, I am operating under a bit of a degraded
mode, at the moment (testsuite not clean to start with when running
against the FSF/master), but if there are regressions, I will take care
of investigating them when they show up. Not to worry!

-- 
Joel

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

* Re: [PATCH 26/40] Optimize .gdb_index symbol name searching
  2017-06-02 12:39 ` [PATCH 26/40] Optimize .gdb_index symbol name searching Pedro Alves
  2017-08-08 20:32   ` Keith Seitz
@ 2017-11-18  5:23   ` Simon Marchi
  2017-11-20  0:33     ` Pedro Alves
  1 sibling, 1 reply; 183+ messages in thread
From: Simon Marchi @ 2017-11-18  5:23 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 2017-06-02 08:22 AM, Pedro Alves wrote:
> @@ -4186,30 +4239,214 @@ dw2_expand_symtabs_matching
>  	}
>      }
>  
> -  gdb_index_symbol_name_matcher lookup_name_matcher (lookup_name);
> +  mapped_index &index = *dwarf2_per_objfile->index_table;
>  
> -  for (iter = 0; iter < index->symbol_table_slots; ++iter)
> +  dw2_expand_symtabs_matching_symbol (index, lookup_name,
> +				      symbol_matcher,
> +				      kind, [&] (offset_type idx)
>      {
> -      offset_type idx = 2 * iter;
> -      const char *name;
> -      offset_type *vec, vec_len, vec_idx;
> -      int global_seen = 0;
> +      dw2_expand_marked_cus (index, idx, objfile, file_matcher,
> +			     expansion_notify, kind);
> +    });
> +}
>  
> -      QUIT;
> +/* Helper for dw2_expand_symtabs_matching that works with a
> +   mapped_index instead of the containing objfile.  This is split to a
> +   separate function in order to be able to unit test the
> +   name_components matching using a mock mapped_index.  For each
> +   symbol name that matches, calls MATCH_CALLBACK, passing it the
> +   symbol's index in the mapped_index symbol table.  */
>  
> -      if (index->symbol_table[idx] == 0 && index->symbol_table[idx + 1] == 0)
> -	continue;
> +static void
> +dw2_expand_symtabs_matching_symbol
> +  (mapped_index &index,
> +   const lookup_name_info &lookup_name,
> +   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
> +   enum search_domain kind,
> +   gdb::function_view<void (offset_type)> match_callback)
> +{
> +  gdb_index_symbol_name_matcher lookup_name_matcher
> +    (lookup_name);
> +
> +  auto *name_cmp = case_sensitivity == case_sensitive_on ? strcmp : strcasecmp;
> +
> +  /* Build the symbol name component sorted vector, if we haven't yet.
> +     The code below only knows how to break apart components of C++
> +     symbol names (and other languages that use '::' as
> +     namespace/module separator).  If we add support for wild matching
> +     to some language that uses some other operator (E.g., Ada, Go and
> +     D use '.'), then we'll need to try splitting the symbol name
> +     according to that language too.  Note that Ada does support wild
> +     matching, but doesn't currently support .gdb_index.  */
> +  if (index.name_components.empty ())
> +    {
> +      for (size_t iter = 0; iter < index.symbol_table_slots; ++iter)
> +	{
> +	  offset_type idx = 2 * iter;
> +
> +	  if (index.symbol_table[idx] == 0
> +	      && index.symbol_table[idx + 1] == 0)
> +	    continue;
> +
> +	  const char *name = index.symbol_name_at (idx);
> +
> +	  /* Add each name component to the name component table.  */
> +	  unsigned int previous_len = 0;
> +	  for (unsigned int current_len = cp_find_first_component (name);
> +	       name[current_len] != '\0';
> +	       current_len += cp_find_first_component (name + current_len))
> +	    {
> +	      gdb_assert (name[current_len] == ':');
> +	      index.name_components.push_back ({previous_len, idx});
> +	      /* Skip the '::'.  */
> +	      current_len += 2;
> +	      previous_len = current_len;
> +	    }
> +	  index.name_components.push_back ({previous_len, idx});
> +	}
> +
> +      /* Sort name_comp elements by name.   */
> +      auto name_comp_compare = [&] (const name_component &left,
> +				    const name_component &right)
> +	{
> +	  const char *left_qualified = index.symbol_name_at (left.idx);
> +	  const char *right_qualified = index.symbol_name_at (right.idx);
> +
> +	  const char *left_name = left_qualified + left.name_offset;
> +	  const char *right_name = right_qualified + right.name_offset;
> +
> +	  return name_cmp (left_name, right_name) < 0;
> +	};
> +
> +      std::sort (index.name_components.begin (),
> +		 index.name_components.end (),
> +		 name_comp_compare);
> +    }
> +
> +  const char *cplus
> +    = lookup_name.cplus ().lookup_name ().c_str ();
>  
> -      name = index->constant_pool + MAYBE_SWAP (index->symbol_table[idx]);
> +  /* Comparison function object for lower_bound that matches against a
> +     given symbol name.  */
> +  auto lookup_compare_lower = [&] (const name_component &elem,
> +				   const char *name)
> +    {
> +      const char *elem_qualified = index.symbol_name_at (elem.idx);
> +      const char *elem_name = elem_qualified + elem.name_offset;
> +      return name_cmp (elem_name, name) < 0;
> +    };
> +
> +  /* Comparison function object for upper_bound that matches against a
> +     given symbol name.  */
> +  auto lookup_compare_upper = [&] (const char *name,
> +				   const name_component &elem)
> +    {
> +      const char *elem_qualified = index.symbol_name_at (elem.idx);
> +      const char *elem_name = elem_qualified + elem.name_offset;
> +      return name_cmp (name, elem_name) < 0;
> +    };
> +
> +  auto begin = index.name_components.begin ();
> +  auto end = index.name_components.end ();
> +
> +  /* Find the lower bound.  */
> +  auto lower = [&] ()
> +    {
> +      if (lookup_name.completion_mode () && cplus[0] == '\0')
> +	return begin;
> +      else
> +	return std::lower_bound (begin, end, cplus, lookup_compare_lower);
> +    } ();
>  
> -      if (!lookup_name_matcher.matches (name)
> -	  || (symbol_matcher != NULL && !symbol_matcher (name)))
> +  /* Find the upper bound.  */
> +  auto upper = [&] ()
> +    {
> +      if (lookup_name.completion_mode ())
> +	{
> +	  /* The string frobbing below won't work if the string is
> +	     empty.  We don't need it then, anyway -- if we're
> +	     completing an empty string, then we want to iterate over
> +	     the whole range.  */
> +	  if (cplus[0] == '\0')
> +	    return end;
> +
> +	  /* In completion mode, increment the last character because
> +	     we want UPPER to point past all symbols names that have
> +	     the same prefix.  */
> +	  std::string after = cplus;
> +
> +	  gdb_assert (after.back () != 0xff);

Hi Pedro,

With Clang, I get this warning:

/home/simark/src/binutils-gdb/gdb/dwarf2read.c:4316:30: error: comparison of constant 255 with expression of type '__gnu_cxx::__alloc_traits<std::allocator<char> >::value_type' (aka 'char') is always true [-Werror,-Wtautological-constant-out-of-range-compare]
          gdb_assert (after.back () != 0xff);
                      ~~~~~~~~~~~~~ ^  ~~~~
/home/simark/src/binutils-gdb/gdb/common/gdb_assert.h:34:13: note: expanded from macro 'gdb_assert'
  ((void) ((expr) ? 0 :                                                       \
            ^~~~

Simon

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

* Re: [PATCH 26/40] Optimize .gdb_index symbol name searching
  2017-11-18  5:23   ` [PATCH 26/40] Optimize .gdb_index symbol name searching Simon Marchi
@ 2017-11-20  0:33     ` Pedro Alves
  2017-11-20  0:42       ` [PATCH 2/3] Unit test name-component bounds searching directly Pedro Alves
                         ` (2 more replies)
  0 siblings, 3 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-20  0:33 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

On 11/18/2017 05:23 AM, Simon Marchi wrote:
> On 2017-06-02 08:22 AM, Pedro Alves wrote:

>> +	  /* In completion mode, increment the last character because
>> +	     we want UPPER to point past all symbols names that have
>> +	     the same prefix.  */
>> +	  std::string after = cplus;
>> +
>> +	  gdb_assert (after.back () != 0xff);
> 
> Hi Pedro,
> 

Hi!

> With Clang, I get this warning:
> 
> /home/simark/src/binutils-gdb/gdb/dwarf2read.c:4316:30: error: comparison of constant 255 with expression of type '__gnu_cxx::__alloc_traits<std::allocator<char> >::value_type' (aka 'char') is always true [-Werror,-Wtautological-constant-out-of-range-compare]
>           gdb_assert (after.back () != 0xff);
>                       ~~~~~~~~~~~~~ ^  ~~~~
> /home/simark/src/binutils-gdb/gdb/common/gdb_assert.h:34:13: note: expanded from macro 'gdb_assert'
>   ((void) ((expr) ? 0 :                                                       \
>             ^~~~
> 

Bleh, I should have known that'd come back and bite.  :-P

I'm sending a 3 patch series in response to this email that fixes that,
and also other things that I ran into in course of fixing it.

Thanks,
Pedro Alves

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

* [PATCH 3/3] Fix mapped_index::find_name_components_bounds upper bound computation
  2017-11-20  0:33     ` Pedro Alves
  2017-11-20  0:42       ` [PATCH 2/3] Unit test name-component bounds searching directly Pedro Alves
  2017-11-20  0:42       ` [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers Pedro Alves
@ 2017-11-20  0:42       ` Pedro Alves
  2017-11-20  3:17         ` Simon Marchi
  2 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-20  0:42 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

Here we want to find where we'd insert "after", so we want
std::lower_bound, not std::upper_bound.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* dwarf2read.c (mapped_index::find_name_components_bounds)
	<completion mode, upper bound>: Use std::lower_bound instead of
	std::upper_bound.
	(test_mapped_index_find_name_component_bounds): Remove incorrect
	"t1_fund" from expected symbols.
---
 gdb/dwarf2read.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 53548ca..c4d1254 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -4319,8 +4319,8 @@ mapped_index::find_name_components_bounds
 	  std::string after = make_sort_after_prefix_name (cplus);
 	  if (after.empty ())
 	    return end;
-	  return std::upper_bound (lower, end, after.c_str (),
-				   lookup_compare_upper);
+	  return std::lower_bound (lower, end, after.c_str (),
+				   lookup_compare_lower);
 	}
       else
 	return std::upper_bound (lower, end, cplus, lookup_compare_upper);
@@ -4659,7 +4659,6 @@ test_mapped_index_find_name_component_bounds ()
     static const char *expected_syms[] = {
       "t1_func",
       "t1_func1",
-      "t1_fund", /* This one's incorrect.  */
     };
 
     SELF_CHECK (check_find_bounds_finds (mock_index.index (),
-- 
2.5.5

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

* [PATCH 2/3] Unit test name-component bounds searching directly
  2017-11-20  0:33     ` Pedro Alves
@ 2017-11-20  0:42       ` Pedro Alves
  2017-11-20  3:16         ` Simon Marchi
  2017-11-20  0:42       ` [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers Pedro Alves
  2017-11-20  0:42       ` [PATCH 3/3] Fix mapped_index::find_name_components_bounds upper bound computation Pedro Alves
  2 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-20  0:42 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

This commit factors out the name-components-vector building and bounds
searching out of dw2_expand_symtabs_matching_symbol into separate
functions, and adds unit tests that:

 - expose both the latent bug mentioned in the previous commit, and
   also,

 - for completeness exercise the 0xff character handling fixed in the
   previous commit more directly.

The actual fix for the now-exposed bug is left for the following
patch.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* dwarf2read.c (mapped_index::name_components_casing): New field.
	(mapped_index) <build_name_components,
	find_name_components_bounds): Declare new methods.
	(mapped_index::find_name_components_bounds)
	(mapped_index::build_name_components): New methods, factored out
	from dw2_expand_symtabs_matching_symbol.
	(check_find_bounds_finds, test_find_bounds): New.
	(run_test): Rename to ...
	(test_dw2_expand_symtabs_matching_symbol): ... this.
	(run_test): Reimplement.
---
 gdb/dwarf2read.c | 287 +++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 204 insertions(+), 83 deletions(-)

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index b08e81c..53548ca 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -255,11 +255,24 @@ struct mapped_index
   /* The name_component table (a sorted vector).  See name_component's
      description above.  */
   std::vector<name_component> name_components;
+  /* How NAME_COMPONENTS is sorted.  */
+  enum case_sensitivity name_components_casing;
 
   /* Convenience method to get at the name of the symbol at IDX in the
      symbol table.  */
   const char *symbol_name_at (offset_type idx) const
   { return this->constant_pool + MAYBE_SWAP (this->symbol_table[idx]); }
+
+  /* Build the symbol name component sorted vector, if we haven't
+     yet.  */
+  void build_name_components ();
+
+  /* Returns the lower (inclusive) and upper (exclusive) bounds of the
+     possible matches for LN_NO_PARAMS in the name component
+     vector.  */
+  std::pair<std::vector<name_component>::const_iterator,
+	    std::vector<name_component>::const_iterator>
+    find_name_components_bounds (const lookup_name_info &ln_no_params) const;
 };
 
 typedef struct dwarf2_per_cu_data *dwarf2_per_cu_ptr;
@@ -4242,80 +4255,15 @@ make_sort_after_prefix_name (const char *search_name)
   return after;
 }
 
-/* Helper for dw2_expand_symtabs_matching that works with a
-   mapped_index instead of the containing objfile.  This is split to a
-   separate function in order to be able to unit test the
-   name_components matching using a mock mapped_index.  For each
-   symbol name that matches, calls MATCH_CALLBACK, passing it the
-   symbol's index in the mapped_index symbol table.  */
+/* See declaration.  */
 
-static void
-dw2_expand_symtabs_matching_symbol
-  (mapped_index &index,
-   const lookup_name_info &lookup_name_in,
-   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
-   enum search_domain kind,
-   gdb::function_view<void (offset_type)> match_callback)
+std::pair<std::vector<name_component>::const_iterator,
+	  std::vector<name_component>::const_iterator>
+mapped_index::find_name_components_bounds
+  (const lookup_name_info &lookup_name_without_params) const
 {
-  lookup_name_info lookup_name_without_params
-    = lookup_name_in.make_ignore_params ();
-  gdb_index_symbol_name_matcher lookup_name_matcher
-    (lookup_name_without_params);
-
-  auto *name_cmp = case_sensitivity == case_sensitive_on ? strcmp : strcasecmp;
-
-  /* Build the symbol name component sorted vector, if we haven't yet.
-     The code below only knows how to break apart components of C++
-     symbol names (and other languages that use '::' as
-     namespace/module separator).  If we add support for wild matching
-     to some language that uses some other operator (E.g., Ada, Go and
-     D use '.'), then we'll need to try splitting the symbol name
-     according to that language too.  Note that Ada does support wild
-     matching, but doesn't currently support .gdb_index.  */
-  if (index.name_components.empty ())
-    {
-      for (size_t iter = 0; iter < index.symbol_table_slots; ++iter)
-	{
-	  offset_type idx = 2 * iter;
-
-	  if (index.symbol_table[idx] == 0
-	      && index.symbol_table[idx + 1] == 0)
-	    continue;
-
-	  const char *name = index.symbol_name_at (idx);
-
-	  /* Add each name component to the name component table.  */
-	  unsigned int previous_len = 0;
-	  for (unsigned int current_len = cp_find_first_component (name);
-	       name[current_len] != '\0';
-	       current_len += cp_find_first_component (name + current_len))
-	    {
-	      gdb_assert (name[current_len] == ':');
-	      index.name_components.push_back ({previous_len, idx});
-	      /* Skip the '::'.  */
-	      current_len += 2;
-	      previous_len = current_len;
-	    }
-	  index.name_components.push_back ({previous_len, idx});
-	}
-
-      /* Sort name_components elements by name.  */
-      auto name_comp_compare = [&] (const name_component &left,
-				    const name_component &right)
-	{
-	  const char *left_qualified = index.symbol_name_at (left.idx);
-	  const char *right_qualified = index.symbol_name_at (right.idx);
-
-	  const char *left_name = left_qualified + left.name_offset;
-	  const char *right_name = right_qualified + right.name_offset;
-
-	  return name_cmp (left_name, right_name) < 0;
-	};
-
-      std::sort (index.name_components.begin (),
-		 index.name_components.end (),
-		 name_comp_compare);
-    }
+  auto *name_cmp
+    = this->name_components_casing == case_sensitive_on ? strcmp : strcasecmp;
 
   const char *cplus
     = lookup_name_without_params.cplus ().lookup_name ().c_str ();
@@ -4325,7 +4273,7 @@ dw2_expand_symtabs_matching_symbol
   auto lookup_compare_lower = [&] (const name_component &elem,
 				   const char *name)
     {
-      const char *elem_qualified = index.symbol_name_at (elem.idx);
+      const char *elem_qualified = this->symbol_name_at (elem.idx);
       const char *elem_name = elem_qualified + elem.name_offset;
       return name_cmp (elem_name, name) < 0;
     };
@@ -4335,18 +4283,18 @@ dw2_expand_symtabs_matching_symbol
   auto lookup_compare_upper = [&] (const char *name,
 				   const name_component &elem)
     {
-      const char *elem_qualified = index.symbol_name_at (elem.idx);
+      const char *elem_qualified = this->symbol_name_at (elem.idx);
       const char *elem_name = elem_qualified + elem.name_offset;
       return name_cmp (name, elem_name) < 0;
     };
 
-  auto begin = index.name_components.begin ();
-  auto end = index.name_components.end ();
+  auto begin = this->name_components.begin ();
+  auto end = this->name_components.end ();
 
   /* Find the lower bound.  */
   auto lower = [&] ()
     {
-      if (lookup_name_in.completion_mode () && cplus[0] == '\0')
+      if (lookup_name_without_params.completion_mode () && cplus[0] == '\0')
 	return begin;
       else
 	return std::lower_bound (begin, end, cplus, lookup_compare_lower);
@@ -4355,7 +4303,7 @@ dw2_expand_symtabs_matching_symbol
   /* Find the upper bound.  */
   auto upper = [&] ()
     {
-      if (lookup_name_in.completion_mode ())
+      if (lookup_name_without_params.completion_mode ())
 	{
 	  /* In completion mode, we want UPPER to point past all
 	     symbols names that have the same prefix.  I.e., with
@@ -4378,6 +4326,97 @@ dw2_expand_symtabs_matching_symbol
 	return std::upper_bound (lower, end, cplus, lookup_compare_upper);
     } ();
 
+  return {lower, upper};
+}
+
+/* See declaration.  */
+
+void
+mapped_index::build_name_components ()
+{
+  if (!this->name_components.empty ())
+    return;
+
+  this->name_components_casing = case_sensitivity;
+  auto *name_cmp
+    = this->name_components_casing == case_sensitive_on ? strcmp : strcasecmp;
+
+  /* The code below only knows how to break apart components of C++
+     symbol names (and other languages that use '::' as
+     namespace/module separator).  If we add support for wild matching
+     to some language that uses some other operator (E.g., Ada, Go and
+     D use '.'), then we'll need to try splitting the symbol name
+     according to that language too.  Note that Ada does support wild
+     matching, but doesn't currently support .gdb_index.  */
+  for (size_t iter = 0; iter < this->symbol_table_slots; ++iter)
+    {
+      offset_type idx = 2 * iter;
+
+      if (this->symbol_table[idx] == 0
+	  && this->symbol_table[idx + 1] == 0)
+	continue;
+
+      const char *name = this->symbol_name_at (idx);
+
+      /* Add each name component to the name component table.  */
+      unsigned int previous_len = 0;
+      for (unsigned int current_len = cp_find_first_component (name);
+	   name[current_len] != '\0';
+	   current_len += cp_find_first_component (name + current_len))
+	{
+	  gdb_assert (name[current_len] == ':');
+	  this->name_components.push_back ({previous_len, idx});
+	  /* Skip the '::'.  */
+	  current_len += 2;
+	  previous_len = current_len;
+	}
+      this->name_components.push_back ({previous_len, idx});
+    }
+
+  /* Sort name_components elements by name.  */
+  auto name_comp_compare = [&] (const name_component &left,
+				const name_component &right)
+    {
+      const char *left_qualified = this->symbol_name_at (left.idx);
+      const char *right_qualified = this->symbol_name_at (right.idx);
+
+      const char *left_name = left_qualified + left.name_offset;
+      const char *right_name = right_qualified + right.name_offset;
+
+      return name_cmp (left_name, right_name) < 0;
+    };
+
+  std::sort (this->name_components.begin (),
+	     this->name_components.end (),
+	     name_comp_compare);
+}
+
+/* Helper for dw2_expand_symtabs_matching that works with a
+   mapped_index instead of the containing objfile.  This is split to a
+   separate function in order to be able to unit test the
+   name_components matching using a mock mapped_index.  For each
+   symbol name that matches, calls MATCH_CALLBACK, passing it the
+   symbol's index in the mapped_index symbol table.  */
+
+static void
+dw2_expand_symtabs_matching_symbol
+  (mapped_index &index,
+   const lookup_name_info &lookup_name_in,
+   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
+   enum search_domain kind,
+   gdb::function_view<void (offset_type)> match_callback)
+{
+  lookup_name_info lookup_name_without_params
+    = lookup_name_in.make_ignore_params ();
+  gdb_index_symbol_name_matcher lookup_name_matcher
+    (lookup_name_without_params);
+
+  /* Build the symbol name component sorted vector, if we haven't
+     yet.  */
+  index.build_name_components ();
+
+  auto bounds = index.find_name_components_bounds (lookup_name_without_params);
+
   /* Now for each symbol name in range, check to see if we have a name
      match, and if so, call the MATCH_CALLBACK callback.  */
 
@@ -4389,17 +4428,17 @@ dw2_expand_symtabs_matching_symbol
      indexes that matched in a temporary vector and ignore
      duplicates.  */
   std::vector<offset_type> matches;
-  matches.reserve (std::distance (lower, upper));
+  matches.reserve (std::distance (bounds.first, bounds.second));
 
-  for (;lower != upper; ++lower)
+  for (; bounds.first != bounds.second; ++bounds.first)
     {
-      const char *qualified = index.symbol_name_at (lower->idx);
+      const char *qualified = index.symbol_name_at (bounds.first->idx);
 
       if (!lookup_name_matcher.matches (qualified)
 	  || (symbol_matcher != NULL && !symbol_matcher (qualified)))
 	continue;
 
-      matches.push_back (lower->idx);
+      matches.push_back (bounds.first->idx);
     }
 
   std::sort (matches.begin (), matches.end ());
@@ -4576,8 +4615,83 @@ static const char *test_symbols[] = {
   "\377\377123",
 };
 
+/* Returns true if mapped_index::find_name_component_bounds method
+   finds EXPECTED_SYMS in INDEX when looking for SEARCH_NAME, in
+   completion mode.  */
+
+static bool
+check_find_bounds_finds (mapped_index &index,
+			 const char *search_name,
+			 gdb::array_view<const char *> expected_syms)
+{
+  lookup_name_info lookup_name (search_name,
+				symbol_name_match_type::FULL, true);
+
+  auto bounds = index.find_name_components_bounds (lookup_name);
+
+  size_t distance = std::distance (bounds.first, bounds.second);
+  if (distance != expected_syms.size ())
+    return false;
+
+  for (size_t exp_elem = 0; exp_elem < distance; exp_elem++)
+    {
+      auto nc_elem = bounds.first + exp_elem;
+      const char *qualified = index.symbol_name_at (nc_elem->idx);
+      if (strcmp (qualified, expected_syms[exp_elem]) != 0)
+	return false;
+    }
+
+  return true;
+}
+
+/* Test the lower-level mapped_index::find_name_component_bounds
+   method.  */
+
 static void
-run_test ()
+test_mapped_index_find_name_component_bounds ()
+{
+  mock_mapped_index mock_index (test_symbols);
+
+  mock_index.index ().build_name_components ();
+
+  /* Test the lower-level find_bounds function in completion mode.  */
+  {
+    static const char *expected_syms[] = {
+      "t1_func",
+      "t1_func1",
+      "t1_fund", /* This one's incorrect.  */
+    };
+
+    SELF_CHECK (check_find_bounds_finds (mock_index.index (),
+					 "t1_func", expected_syms));
+  }
+
+  /* Check that the increment-last-char in the name matching algorithm
+     for completion doesn't get confused with ANSI-1 'ÿ' / 0xff.  */
+  {
+    static const char *expected_syms[] = {
+      "\377",
+      "\377\377123",
+    };
+
+    SELF_CHECK (check_find_bounds_finds (mock_index.index (),
+					 "\377", expected_syms));
+  }
+
+  {
+    static const char *expected_syms[] = {
+      "\377\377123",
+    };
+
+    SELF_CHECK (check_find_bounds_finds (mock_index.index (),
+					 "\377\377", expected_syms));
+  }
+}
+
+/* Test dw2_expand_symtabs_matching_symbol.  */
+
+static void
+test_dw2_expand_symtabs_matching_symbol ()
 {
   mock_mapped_index mock_index (test_symbols);
 
@@ -4734,6 +4848,13 @@ run_test ()
 #undef CHECK_MATCH
 }
 
+static void
+run_test ()
+{
+  test_mapped_index_find_name_component_bounds ();
+  test_dw2_expand_symtabs_matching_symbol ();
+}
+
 }} // namespace selftests::dw2_expand_symtabs_matching
 
 #endif /* GDB_SELF_TEST */
-- 
2.5.5

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

* [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers
  2017-11-20  0:33     ` Pedro Alves
  2017-11-20  0:42       ` [PATCH 2/3] Unit test name-component bounds searching directly Pedro Alves
@ 2017-11-20  0:42       ` Pedro Alves
  2017-11-20  1:38         ` Simon Marchi
  2017-11-20  0:42       ` [PATCH 3/3] Fix mapped_index::find_name_components_bounds upper bound computation Pedro Alves
  2 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-20  0:42 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

The find-upper-bound-for-completion algorithm in the name components
accelerator table in dwarf2read.c increments a char in a string, and
asserts that it's not incrementing a 0xff char, but that's incorrect.

First, we shouldn't be calling gdb_assert on input.

Then, if "char" is signed, comparing a caracther with "0xff" will
never yield true, which is caught by Clang with:

  error: comparison of constant 255 with expression of type '....' (aka 'char') is always true [-Werror,-Wtautological-constant-out-of-range-compare]
	    gdb_assert (after.back () != 0xff);
			~~~~~~~~~~~~~ ^  ~~~~

And then, 0xff is a valid character on non-UTF-8/ASCII character sets.
E.g., it's 'ÿ' in Latin1.  While GCC nor Clang support !ASCII &&
!UTF-8 characters in identifiers (GCC supports UTF-8 characters only
via UCNs, see https://gcc.gnu.org/onlinedocs/cpp/Character-sets.html),
but other compilers might (Visual Studio?), so it doesn't hurt to
handle it correctly.  Testing is covered by extending the
dw2_expand_symtabs_matching unit tests with relevant cases.

However, without further changes, the unit tests still fail...  The
problem is that cp-name-parser.y assumes that identifiers are ASCII
(via ISALPHA/ISALNUM).  This commit fixes that too, so that we can
unit test the dwarf2read.c changes.  (The regular C/C++ lexer in
c-lang.y needs a similar treatment, but I'm leaving that for another
patch.)

While doing this, I noticed a thinko in the computation of the upper
bound for completion in dw2_expand_symtabs_matching_symbol.  We're
using std::upper_bound but we should use std::lower_bound.  I extended
the unit test with a case that I thought would expose it, this one:

 +  /* These are used to check that the increment-last-char in the
 +     matching algorithm for completion doesn't match "t1_fund" when
 +     completing "t1_func".  */
 +  "t1_func",
 +  "t1_func1",
 +  "t1_fund",
 +  "t1_fund1",

The algorithm actually returns "t1_fund1" as lower bound, so "t1_fund"
matches incorrectly.  But turns out the problem is masked because
later here:

  for (;lower != upper; ++lower)
    {
      const char *qualified = index.symbol_name_at (lower->idx);

      if (!lookup_name_matcher.matches (qualified)

the lookup_name_matcher.matches check above filters out "t1_fund"
because that doesn't start with "t1_func".

I'll fix the latent bug in follow up patches, after factoring things
out a bit in a way that allows unit testing the relevant code more
directly.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cp-name-parser.y (cp_ident_is_alpha, cp_ident_is_alnum): New.
	(symbol_end): Use cp_ident_is_alnum.
	(yylex): Use cp_ident_is_alpha and cp_ident_is_alnum.
	* dwarf2read.c (make_sort_after_prefix_name): New function.
	(dw2_expand_symtabs_matching_symbol): Use it.
	(test_symbols): Add more symbols.
	(run_test): Add tests.
---
 gdb/cp-name-parser.y |  28 ++++++++++--
 gdb/dwarf2read.c     | 119 ++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 129 insertions(+), 18 deletions(-)

diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y
index 33ecf13..fdfbf15 100644
--- a/gdb/cp-name-parser.y
+++ b/gdb/cp-name-parser.y
@@ -1304,6 +1304,28 @@ d_binary (const char *name, struct demangle_component *lhs, struct demangle_comp
 		      fill_comp (DEMANGLE_COMPONENT_BINARY_ARGS, lhs, rhs));
 }
 
+/* Like ISALPHA, but also returns true for the union of all UTF-8
+   multi-byte sequence bytes and non-ASCII characters in
+   extended-ASCII charsets (e.g., Latin1).  I.e., returns true if the
+   high bit is set.  Note that not all UTF-8 ranges are allowed in C++
+   identifiers, but we don't need to be pedantic so for simplicity we
+   ignore that here.  Plus this avoids the complication of actually
+   knowing what was the right encoding.  */
+
+static inline bool
+cp_ident_is_alpha (unsigned char ch)
+{
+  return ISALPHA (ch) || ch >= 0x80;
+}
+
+/* Similarly, but Like ISALNUM.  */
+
+static inline bool
+cp_ident_is_alnum (unsigned char ch)
+{
+  return ISALNUM (ch) || ch >= 0x80;
+}
+
 /* Find the end of a symbol name starting at LEXPTR.  */
 
 static const char *
@@ -1311,7 +1333,7 @@ symbol_end (const char *lexptr)
 {
   const char *p = lexptr;
 
-  while (*p && (ISALNUM (*p) || *p == '_' || *p == '$' || *p == '.'))
+  while (*p && (cp_ident_is_alnum (*p) || *p == '_' || *p == '$' || *p == '.'))
     p++;
 
   return p;
@@ -1791,7 +1813,7 @@ yylex (void)
       return ERROR;
     }
 
-  if (!(c == '_' || c == '$' || ISALPHA (c)))
+  if (!(c == '_' || c == '$' || cp_ident_is_alpha (c)))
     {
       /* We must have come across a bad character (e.g. ';').  */
       yyerror (_("invalid character"));
@@ -1802,7 +1824,7 @@ yylex (void)
   namelen = 0;
   do
     c = tokstart[++namelen];
-  while (ISALNUM (c) || c == '_' || c == '$');
+  while (cp_ident_is_alnum (c) || c == '_' || c == '$');
 
   lexptr += namelen;
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 5437d21..b08e81c 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -4188,6 +4188,60 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
   return false;
 }
 
+/* Starting from a search name, return the string that finds the upper
+   bound of all strings that start with SEARCH_NAME in a sorted name
+   list.  Returns the empty string to indicate that the upper bound is
+   the end of the list.  */
+
+static std::string
+make_sort_after_prefix_name (const char *search_name)
+{
+  /* When looking to complete "func", we find the upper bound of all
+     symbols that start with "func" by looking for where we'd insert
+     "func"-with-last-character-incremented, i.e. "fund".  */
+  std::string after = search_name;
+
+  /* Mind 0xff though, which is a valid character in non-UTF-8 source
+     character sets (e.g. Latin1 'ÿ'), and we can't rule out compilers
+     allowing it in identifiers.  If we run into it, increment the
+     previous character instead and shorten the string.  If the very
+     first character turns out to be 0xff, then the upper bound is the
+     end of the list.
+
+     E.g., with these symbols:
+
+      func
+      func1
+      fund
+
+     completing "func" looks for symbols between "func" and
+     "func"-with-last-character-incremented, i.e. "fund" (exclusive),
+     which finds "func" and "func1", but not "fund".
+
+     And with:
+
+      funcÿ     (Latin1 'ÿ' [0xff])
+      funcÿ1
+      fund
+
+     completing "funcÿ", look for symbols between "funcÿ" and "fund"
+     (exclusive), which finds "funcÿ" and "funcÿ1", but not "fund".
+
+     And with:
+
+      ÿÿ        (Latin1 'ÿ' [0xff])
+      ÿÿ1
+
+     completing "ÿ" or "ÿÿ" looks for symbols between between "ÿÿ" and
+     the end of the list.
+  */
+  while (!after.empty () && (unsigned char) after.back () == 0xff)
+    after.pop_back ();
+  if (!after.empty ())
+    after.back () = (unsigned char) after.back () + 1;
+  return after;
+}
+
 /* Helper for dw2_expand_symtabs_matching that works with a
    mapped_index instead of the containing objfile.  This is split to a
    separate function in order to be able to unit test the
@@ -4303,21 +4357,20 @@ dw2_expand_symtabs_matching_symbol
     {
       if (lookup_name_in.completion_mode ())
 	{
-	  /* The string frobbing below won't work if the string is
-	     empty.  We don't need it then, anyway -- if we're
-	     completing an empty string, then we want to iterate over
-	     the whole range.  */
-	  if (cplus[0] == '\0')
+	  /* In completion mode, we want UPPER to point past all
+	     symbols names that have the same prefix.  I.e., with
+	     these symbols, and completing "func":
+
+	      function        << lower bound
+	      function1
+	      other_function  << upper bound
+
+	     We find the upper bound by looking for the insertion
+	     point of "func"-with-last-character-incremented,
+	     i.e. "fund".  */
+	  std::string after = make_sort_after_prefix_name (cplus);
+	  if (after.empty ())
 	    return end;
-
-	  /* In completion mode, increment the last character because
-	     we want UPPER to point past all symbols names that have
-	     the same prefix.  */
-	  std::string after = cplus;
-
-	  gdb_assert (after.back () != 0xff);
-	  after.back ()++;
-
 	  return std::upper_bound (lower, end, after.c_str (),
 				   lookup_compare_upper);
 	}
@@ -4493,6 +4546,22 @@ static const char *test_symbols[] = {
   "ns::foo<int>",
   "ns::foo<long>",
 
+  /* These are used to check that the increment-last-char in the
+     matching algorithm for completion doesn't match "t1_fund" when
+     completing "t1_func".  */
+  "t1_func",
+  "t1_func1",
+  "t1_fund",
+  "t1_fund1",
+
+  /* A UTF-8 name with multi-byte sequences to make sure that
+     cp-name-parser understands this as a single identifier ("função"
+     is "function" in PT).  */
+  u8"u8função",
+
+  /* \377 / 0xff is Latin1 'ÿ'.  */
+  "yfunc\377",
+
   /* A name with all sorts of complications.  Starts with "z" to make
      it easier for the completion tests below.  */
 #define Z_SYM_NAME \
@@ -4500,7 +4569,11 @@ static const char *test_symbols[] = {
     "::tuple<(anonymous namespace)::ui*, " \
     "std::default_delete<(anonymous namespace)::ui>, void>"
 
-  Z_SYM_NAME
+  Z_SYM_NAME,
+
+  /* \377 / 0xff is Latin1 'ÿ'.  */
+  "\377",
+  "\377\377123",
 };
 
 static void
@@ -4551,6 +4624,22 @@ run_test ()
 		   {});
     }
 
+  /* Check that the name matching algorithm for completion doesn't get
+     confused with ANSI-1 'ÿ' / 0xff.  */
+  {
+    static const char str[] = "\377";
+    CHECK_MATCH (str, symbol_name_match_type::FULL, true,
+		 EXPECT ("\377", "\377\377123"));
+  }
+
+  /* Check that the increment-last-char in the matching algorithm for
+     completion doesn't match "t1_fund" when completing "t1_func".  */
+  {
+    static const char str[] = "t1_func";
+    CHECK_MATCH (str, symbol_name_match_type::FULL, true,
+		 EXPECT ("t1_func", "t1_func1"));
+  }
+
   /* Check that completion mode works at each prefix of the expected
      symbol name.  */
   {
-- 
2.5.5

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

* Re: [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers
  2017-11-20  0:42       ` [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers Pedro Alves
@ 2017-11-20  1:38         ` Simon Marchi
  2017-11-20 11:56           ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Simon Marchi @ 2017-11-20  1:38 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 2017-11-19 07:41 PM, Pedro Alves wrote:
> The find-upper-bound-for-completion algorithm in the name components
> accelerator table in dwarf2read.c increments a char in a string, and
> asserts that it's not incrementing a 0xff char, but that's incorrect.
> 
> First, we shouldn't be calling gdb_assert on input.
> 
> Then, if "char" is signed, comparing a caracther with "0xff" will
> never yield true, which is caught by Clang with:
> 
>   error: comparison of constant 255 with expression of type '....' (aka 'char') is always true [-Werror,-Wtautological-constant-out-of-range-compare]
> 	    gdb_assert (after.back () != 0xff);
> 			~~~~~~~~~~~~~ ^  ~~~~
> 
> And then, 0xff is a valid character on non-UTF-8/ASCII character sets.
> E.g., it's 'ÿ' in Latin1.  While GCC nor Clang support !ASCII &&
> !UTF-8 characters in identifiers (GCC supports UTF-8 characters only
> via UCNs, see https://gcc.gnu.org/onlinedocs/cpp/Character-sets.html),
> but other compilers might (Visual Studio?), so it doesn't hurt to
> handle it correctly.  Testing is covered by extending the
> dw2_expand_symtabs_matching unit tests with relevant cases.
> 
> However, without further changes, the unit tests still fail...  The
> problem is that cp-name-parser.y assumes that identifiers are ASCII
> (via ISALPHA/ISALNUM).  This commit fixes that too, so that we can
> unit test the dwarf2read.c changes.  (The regular C/C++ lexer in
> c-lang.y needs a similar treatment, but I'm leaving that for another
> patch.)
> 
> While doing this, I noticed a thinko in the computation of the upper
> bound for completion in dw2_expand_symtabs_matching_symbol.  We're
> using std::upper_bound but we should use std::lower_bound.  I extended
> the unit test with a case that I thought would expose it, this one:
> 
>  +  /* These are used to check that the increment-last-char in the
>  +     matching algorithm for completion doesn't match "t1_fund" when
>  +     completing "t1_func".  */
>  +  "t1_func",
>  +  "t1_func1",
>  +  "t1_fund",
>  +  "t1_fund1",
> 
> The algorithm actually returns "t1_fund1" as lower bound, so "t1_fund"
> matches incorrectly.  But turns out the problem is masked because
> later here:
> 
>   for (;lower != upper; ++lower)
>     {
>       const char *qualified = index.symbol_name_at (lower->idx);
> 
>       if (!lookup_name_matcher.matches (qualified)
> 
> the lookup_name_matcher.matches check above filters out "t1_fund"
> because that doesn't start with "t1_func".
> 
> I'll fix the latent bug in follow up patches, after factoring things
> out a bit in a way that allows unit testing the relevant code more
> directly.

Everything you said makes sense to me, the patch looks good to me.  I noted
one comment and a typo below.

> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* cp-name-parser.y (cp_ident_is_alpha, cp_ident_is_alnum): New.
> 	(symbol_end): Use cp_ident_is_alnum.
> 	(yylex): Use cp_ident_is_alpha and cp_ident_is_alnum.
> 	* dwarf2read.c (make_sort_after_prefix_name): New function.
> 	(dw2_expand_symtabs_matching_symbol): Use it.
> 	(test_symbols): Add more symbols.
> 	(run_test): Add tests.
> ---
>  gdb/cp-name-parser.y |  28 ++++++++++--
>  gdb/dwarf2read.c     | 119 ++++++++++++++++++++++++++++++++++++++++++++-------
>  2 files changed, 129 insertions(+), 18 deletions(-)
> 
> diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y
> index 33ecf13..fdfbf15 100644
> --- a/gdb/cp-name-parser.y
> +++ b/gdb/cp-name-parser.y
> @@ -1304,6 +1304,28 @@ d_binary (const char *name, struct demangle_component *lhs, struct demangle_comp
>  		      fill_comp (DEMANGLE_COMPONENT_BINARY_ARGS, lhs, rhs));
>  }
>  
> +/* Like ISALPHA, but also returns true for the union of all UTF-8
> +   multi-byte sequence bytes and non-ASCII characters in
> +   extended-ASCII charsets (e.g., Latin1).  I.e., returns true if the
> +   high bit is set.  Note that not all UTF-8 ranges are allowed in C++
> +   identifiers, but we don't need to be pedantic so for simplicity we
> +   ignore that here.  Plus this avoids the complication of actually
> +   knowing what was the right encoding.  */
> +
> +static inline bool
> +cp_ident_is_alpha (unsigned char ch)
> +{
> +  return ISALPHA (ch) || ch >= 0x80;
> +}
> +
> +/* Similarly, but Like ISALNUM.  */
> +
> +static inline bool
> +cp_ident_is_alnum (unsigned char ch)
> +{
> +  return ISALNUM (ch) || ch >= 0x80;
> +}
> +
>  /* Find the end of a symbol name starting at LEXPTR.  */
>  
>  static const char *
> @@ -1311,7 +1333,7 @@ symbol_end (const char *lexptr)
>  {
>    const char *p = lexptr;
>  
> -  while (*p && (ISALNUM (*p) || *p == '_' || *p == '$' || *p == '.'))
> +  while (*p && (cp_ident_is_alnum (*p) || *p == '_' || *p == '$' || *p == '.'))
>      p++;
>  
>    return p;
> @@ -1791,7 +1813,7 @@ yylex (void)
>        return ERROR;
>      }
>  
> -  if (!(c == '_' || c == '$' || ISALPHA (c)))
> +  if (!(c == '_' || c == '$' || cp_ident_is_alpha (c)))
>      {
>        /* We must have come across a bad character (e.g. ';').  */
>        yyerror (_("invalid character"));
> @@ -1802,7 +1824,7 @@ yylex (void)
>    namelen = 0;
>    do
>      c = tokstart[++namelen];
> -  while (ISALNUM (c) || c == '_' || c == '$');
> +  while (cp_ident_is_alnum (c) || c == '_' || c == '$');
>  
>    lexptr += namelen;
>  
> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
> index 5437d21..b08e81c 100644
> --- a/gdb/dwarf2read.c
> +++ b/gdb/dwarf2read.c
> @@ -4188,6 +4188,60 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
>    return false;
>  }
>  
> +/* Starting from a search name, return the string that finds the upper
> +   bound of all strings that start with SEARCH_NAME in a sorted name
> +   list.  Returns the empty string to indicate that the upper bound is
> +   the end of the list.  */
> +
> +static std::string
> +make_sort_after_prefix_name (const char *search_name)
> +{
> +  /* When looking to complete "func", we find the upper bound of all
> +     symbols that start with "func" by looking for where we'd insert
> +     "func"-with-last-character-incremented, i.e. "fund".  */
> +  std::string after = search_name;
> +
> +  /* Mind 0xff though, which is a valid character in non-UTF-8 source
> +     character sets (e.g. Latin1 'ÿ'), and we can't rule out compilers
> +     allowing it in identifiers.  If we run into it, increment the
> +     previous character instead and shorten the string.  If the very
> +     first character turns out to be 0xff, then the upper bound is the
> +     end of the list.

It's a bit of a nit, but I think this explanation could be a bit more
precise, and maybe simpler.  Maybe you could just say that you strip all
trailing 0xff characters, and increment the last non-0xff character of
the string.  If the string is composed only of 0xff characters, then the
upper bound is the end of the list.

The "If the very first character turns out to be 0xff" threw me off a bit,
because if you have the string "\xffa\xff", the upper bound will be "\xffb",
not the end of the list, despite the very first character being 0xff.

> +
> +     E.g., with these symbols:
> +
> +      func
> +      func1
> +      fund
> +
> +     completing "func" looks for symbols between "func" and
> +     "func"-with-last-character-incremented, i.e. "fund" (exclusive),
> +     which finds "func" and "func1", but not "fund".
> +
> +     And with:
> +
> +      funcÿ     (Latin1 'ÿ' [0xff])
> +      funcÿ1
> +      fund
> +
> +     completing "funcÿ", look for symbols between "funcÿ" and "fund"

looks

Simon

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

* Re: [PATCH 2/3] Unit test name-component bounds searching directly
  2017-11-20  0:42       ` [PATCH 2/3] Unit test name-component bounds searching directly Pedro Alves
@ 2017-11-20  3:16         ` Simon Marchi
  2017-11-20 14:17           ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Simon Marchi @ 2017-11-20  3:16 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 2017-11-19 07:41 PM, Pedro Alves wrote:
> This commit factors out the name-components-vector building and bounds
> searching out of dw2_expand_symtabs_matching_symbol into separate
> functions, and adds unit tests that:
> 
>  - expose both the latent bug mentioned in the previous commit, and
>    also,
> 
>  - for completeness exercise the 0xff character handling fixed in the
>    previous commit more directly.
> 
> The actual fix for the now-exposed bug is left for the following
> patch.
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* dwarf2read.c (mapped_index::name_components_casing): New field.
> 	(mapped_index) <build_name_components,
> 	find_name_components_bounds): Declare new methods.
> 	(mapped_index::find_name_components_bounds)
> 	(mapped_index::build_name_components): New methods, factored out
> 	from dw2_expand_symtabs_matching_symbol.
> 	(check_find_bounds_finds, test_find_bounds): New.
> 	(run_test): Rename to ...
> 	(test_dw2_expand_symtabs_matching_symbol): ... this.
> 	(run_test): Reimplement.
> ---
>  gdb/dwarf2read.c | 287 +++++++++++++++++++++++++++++++++++++++----------------
>  1 file changed, 204 insertions(+), 83 deletions(-)
> 
> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
> index b08e81c..53548ca 100644
> --- a/gdb/dwarf2read.c
> +++ b/gdb/dwarf2read.c
> @@ -255,11 +255,24 @@ struct mapped_index
>    /* The name_component table (a sorted vector).  See name_component's
>       description above.  */
>    std::vector<name_component> name_components;
> +  /* How NAME_COMPONENTS is sorted.  */
> +  enum case_sensitivity name_components_casing;

Missing newline above?

Why is it important to store this in the structure now, rather than

It's not really related to this patch, but it made me think about this edge
case.  Given that we build the vector only once, isn't it a problem if the
user switches case sensitivity during a debug session?  For example, the vector
is built with case sensitivity one:

- func_A
- func_B
- func_a
- func_b

Then we look to complete "func_a": we'll get func_a.  The user then switches
"set case-sensitive off".  If we look to complete "func_a" in that vector,
we will search (with lower_bound) with a different sorting criterion that the
one that was used for sorting, which I guess will give funny results.  I
haven't actually tried, I'm just speculating.

If that's indeed a problem, keeping the case_sensitivity that was used to
sort the vector could be useful, since if we detect that it changed, we
can re-sort it.

Otherwise, the patch LGTM.

Simon

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

* Re: [PATCH 3/3] Fix mapped_index::find_name_components_bounds upper bound computation
  2017-11-20  0:42       ` [PATCH 3/3] Fix mapped_index::find_name_components_bounds upper bound computation Pedro Alves
@ 2017-11-20  3:17         ` Simon Marchi
  0 siblings, 0 replies; 183+ messages in thread
From: Simon Marchi @ 2017-11-20  3:17 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 2017-11-19 07:41 PM, Pedro Alves wrote:
> Here we want to find where we'd insert "after", so we want
> std::lower_bound, not std::upper_bound.
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* dwarf2read.c (mapped_index::find_name_components_bounds)
> 	<completion mode, upper bound>: Use std::lower_bound instead of
> 	std::upper_bound.
> 	(test_mapped_index_find_name_component_bounds): Remove incorrect
> 	"t1_fund" from expected symbols.
> ---
>  gdb/dwarf2read.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
> 
> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
> index 53548ca..c4d1254 100644
> --- a/gdb/dwarf2read.c
> +++ b/gdb/dwarf2read.c
> @@ -4319,8 +4319,8 @@ mapped_index::find_name_components_bounds
>  	  std::string after = make_sort_after_prefix_name (cplus);
>  	  if (after.empty ())
>  	    return end;
> -	  return std::upper_bound (lower, end, after.c_str (),
> -				   lookup_compare_upper);
> +	  return std::lower_bound (lower, end, after.c_str (),
> +				   lookup_compare_lower);
>  	}
>        else
>  	return std::upper_bound (lower, end, cplus, lookup_compare_upper);
> @@ -4659,7 +4659,6 @@ test_mapped_index_find_name_component_bounds ()
>      static const char *expected_syms[] = {
>        "t1_func",
>        "t1_func1",
> -      "t1_fund", /* This one's incorrect.  */
>      };
>  
>      SELF_CHECK (check_find_bounds_finds (mock_index.index (),
> 

LGTM.

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

* Re: [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers
  2017-11-20  1:38         ` Simon Marchi
@ 2017-11-20 11:56           ` Pedro Alves
  2017-11-20 16:50             ` Simon Marchi
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-20 11:56 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

On 11/20/2017 01:38 AM, Simon Marchi wrote:

> Everything you said makes sense to me, the patch looks good to me.  I noted
> one comment and a typo below.

Thanks, see below.

>> +/* Starting from a search name, return the string that finds the upper
>> +   bound of all strings that start with SEARCH_NAME in a sorted name
>> +   list.  Returns the empty string to indicate that the upper bound is
>> +   the end of the list.  */
>> +
>> +static std::string
>> +make_sort_after_prefix_name (const char *search_name)
>> +{
>> +  /* When looking to complete "func", we find the upper bound of all
>> +     symbols that start with "func" by looking for where we'd insert
>> +     "func"-with-last-character-incremented, i.e. "fund".  */
>> +  std::string after = search_name;
>> +
>> +  /* Mind 0xff though, which is a valid character in non-UTF-8 source
>> +     character sets (e.g. Latin1 'ÿ'), and we can't rule out compilers
>> +     allowing it in identifiers.  If we run into it, increment the
>> +     previous character instead and shorten the string.  If the very
>> +     first character turns out to be 0xff, then the upper bound is the
>> +     end of the list.
> 
> It's a bit of a nit, but I think this explanation could be a bit more
> precise, and maybe simpler.  Maybe you could just say that you strip all
> trailing 0xff characters, and increment the last non-0xff character of
> the string.  If the string is composed only of 0xff characters, then the
> upper bound is the end of the list.

My problem with that is that it wouldn't explain _why_ we strip
the 0xffs.

> 
> The "If the very first character turns out to be 0xff" threw me off a bit,
> because if you have the string "\xffa\xff", the upper bound will be "\xffb",
> not the end of the list, despite the very first character being 0xff.

I like that example.  How about the following.  It's even longer, but
I think it's justified.

/* Starting from a search name, return the string that finds the upper
   bound of all strings that start with SEARCH_NAME in a sorted name
   list.  Returns the empty string to indicate that the upper bound is
   the end of the list.  */

static std::string
make_sort_after_prefix_name (const char *search_name)
{
  /* When looking to complete "func", we find the upper bound of all
     symbols that start with "func" by looking for where we'd insert
     the closest string that would follow "func" in lexicographical
     order.  Usually, that's "func"-with-last-character-incremented,
     i.e. "fund".  Mind non-ASCII characters, though.  Usually those
     will be UTF-8 multi-byte sequences, but we can't be certain.
     Especially mind the 0xff character, which is a valid character in
     non-UTF-8 source character sets (e.g. Latin1 'ÿ'), and we can't
     rule out compilers allowing it in identifiers.  Note that
     conveniently, strcmp/strcasecmp are specified to compare
     characters interpreted as unsigned char.  So what we do is treat
     the whole string as a base 255 number composed of a sequence of
     base 255 "digits" and add 1 to it.  I.e., adding 1 to 0xff wraps
     to 0, and carries 1 to the following more-significant position.
     If the very first character carries/overflows, then the upper
     bound is the end of the list.  Also the string after the empty
     string is also the empty string.

     Some examples of this operation:

       SEARCH_NAME  => "+1" RESULT

       "abc"        => "abd"
       "ab\xff"     => "ac"
       "\xffa\xff"  => "\xffb"
       "\xff"       => ""
       "\xff\xff"   => ""
       ""           => ""

     Then, with these symbols for example:

      func
      func1
      fund

     completing "func" looks for symbols between "func" and
     "func"-with-last-character-incremented, i.e. "fund" (exclusive),
     which finds "func" and "func1", but not "fund".

     And with:

      funcÿ     (Latin1 'ÿ' [0xff])
      funcÿ1
      fund

     completing "funcÿ" looks for symbols between "funcÿ" and "fund"
     (exclusive), which finds "funcÿ" and "funcÿ1", but not "fund".

     And with:

      ÿÿ        (Latin1 'ÿ' [0xff])
      ÿÿ1

     completing "ÿ" or "ÿÿ" looks for symbols between between "ÿÿ" and
     the end of the list.
  */
  std::string after = search_name;
  while (!after.empty () && (unsigned char) after.back () == 0xff)
    after.pop_back ();
  if (!after.empty ())
    after.back () = (unsigned char) after.back () + 1;
  return after;
}

>> +     completing "funcÿ", look for symbols between "funcÿ" and "fund"
> 
> looks

Fixed above.

Thanks,
Pedro Alves

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

* Re: [PATCH 2/3] Unit test name-component bounds searching directly
  2017-11-20  3:16         ` Simon Marchi
@ 2017-11-20 14:17           ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-20 14:17 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

On 11/20/2017 03:16 AM, Simon Marchi wrote:

>> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
>> index b08e81c..53548ca 100644
>> --- a/gdb/dwarf2read.c
>> +++ b/gdb/dwarf2read.c
>> @@ -255,11 +255,24 @@ struct mapped_index
>>    /* The name_component table (a sorted vector).  See name_component's
>>       description above.  */
>>    std::vector<name_component> name_components;
>> +  /* How NAME_COMPONENTS is sorted.  */
>> +  enum case_sensitivity name_components_casing;
> 
> Missing newline above?
> 
> Why is it important to store this in the structure now, rather than

The alternative was to either pass around a name_cmp pointer to the new
methods or to have both new methods look at the "case_sensitive_on" global.
I played with both directions, and given the TOCTOU-like issue you mentioned
below too, it felt like saving the order that the vector is sorted in
was a better direction.

> 
> It's not really related to this patch, but it made me think about this edge
> case.  Given that we build the vector only once, isn't it a problem if the
> user switches case sensitivity during a debug session?  For example, the vector
> is built with case sensitivity one:
> 
> - func_A
> - func_B
> - func_a
> - func_b
> 
> Then we look to complete "func_a": we'll get func_a.  The user then switches
> "set case-sensitive off".  If we look to complete "func_a" in that vector,
> we will search (with lower_bound) with a different sorting criterion that the
> one that was used for sorting, which I guess will give funny results.  I
> haven't actually tried, I'm just speculating.
> 
> If that's indeed a problem, keeping the case_sensitivity that was used to
> sort the vector could be useful, since if we detect that it changed, we
> can re-sort it.

Exactly.

Last I tried, when I was writing this originally, I
recall that performance drops with "set case-sensitive off",
naturally because strcasecmp has to do more work than strcmp
(i.e., call tolower before comparison).  I'm wondering whether we
could/should still always do case-insensitive sorting, as that may help
mixed language scenarios where one of the languages is case
insensitive, like Ada (Ada doesn't support indexes today).  Maybe
we could do something about the performance hit, by say,
avoiding strcasecmp by instead using safe-type.h/TOLOWER, and
also lowercasing the search string only once (with strcasecmp,
we're lowercasing it for each and every comparison).  Maybe with
that the performance hit is less significant.  But maybe not, considering
that strcmp/strncmp tend to be implemented in assembly making use
of vectorization, etc.  If the accel table stored the the strings in
lowercase form already, then we could use the faster strcmp always.
However, the disadvantage is that the current table only stores offsets into
exiting strings, and saving lowercased version of the strings require
deep copies of the symbol names, which may have a noticeable
memory cost...  Trade offs, trade offs...

> Otherwise, the patch LGTM.

Thanks,
Pedro Alves

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

* Re: [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers
  2017-11-20 11:56           ` Pedro Alves
@ 2017-11-20 16:50             ` Simon Marchi
  2017-11-21  0:11               ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Simon Marchi @ 2017-11-20 16:50 UTC (permalink / raw)
  To: Pedro Alves, Simon Marchi, gdb-patches

On 2017-11-20 06:56 AM, Pedro Alves wrote:
>>> +/* Starting from a search name, return the string that finds the upper
>>> +   bound of all strings that start with SEARCH_NAME in a sorted name
>>> +   list.  Returns the empty string to indicate that the upper bound is
>>> +   the end of the list.  */
>>> +
>>> +static std::string
>>> +make_sort_after_prefix_name (const char *search_name)
>>> +{
>>> +  /* When looking to complete "func", we find the upper bound of all
>>> +     symbols that start with "func" by looking for where we'd insert
>>> +     "func"-with-last-character-incremented, i.e. "fund".  */
>>> +  std::string after = search_name;
>>> +
>>> +  /* Mind 0xff though, which is a valid character in non-UTF-8 source
>>> +     character sets (e.g. Latin1 'ÿ'), and we can't rule out compilers
>>> +     allowing it in identifiers.  If we run into it, increment the
>>> +     previous character instead and shorten the string.  If the very
>>> +     first character turns out to be 0xff, then the upper bound is the
>>> +     end of the list.
>>
>> It's a bit of a nit, but I think this explanation could be a bit more
>> precise, and maybe simpler.  Maybe you could just say that you strip all
>> trailing 0xff characters, and increment the last non-0xff character of
>> the string.  If the string is composed only of 0xff characters, then the
>> upper bound is the end of the list.
> 
> My problem with that is that it wouldn't explain _why_ we strip
> the 0xffs.

Right, the comment should say why, not how.

>>
>> The "If the very first character turns out to be 0xff" threw me off a bit,
>> because if you have the string "\xffa\xff", the upper bound will be "\xffb",
>> not the end of the list, despite the very first character being 0xff.
> 
> I like that example.  How about the following.  It's even longer, but
> I think it's justified.
> 
> /* Starting from a search name, return the string that finds the upper
>    bound of all strings that start with SEARCH_NAME in a sorted name
>    list.  Returns the empty string to indicate that the upper bound is
>    the end of the list.  */
> 
> static std::string
> make_sort_after_prefix_name (const char *search_name)
> {
>   /* When looking to complete "func", we find the upper bound of all
>      symbols that start with "func" by looking for where we'd insert
>      the closest string that would follow "func" in lexicographical
>      order.  Usually, that's "func"-with-last-character-incremented,
>      i.e. "fund".  Mind non-ASCII characters, though.  Usually those
>      will be UTF-8 multi-byte sequences, but we can't be certain.
>      Especially mind the 0xff character, which is a valid character in
>      non-UTF-8 source character sets (e.g. Latin1 'ÿ'), and we can't
>      rule out compilers allowing it in identifiers.  Note that
>      conveniently, strcmp/strcasecmp are specified to compare
>      characters interpreted as unsigned char.  So what we do is treat
>      the whole string as a base 255 number composed of a sequence of
>      base 255 "digits" and add 1 to it.  I.e., adding 1 to 0xff wraps
>      to 0, and carries 1 to the following more-significant position.
>      If the very first character carries/overflows, then the upper
>      bound is the end of the list.  Also the string after the empty
>      string is also the empty string.

Making an analogy with base-10 arithmetic is actually what made me
understand it.  The number after 149 is not 140, it's 150.  We're
doing the string equivalent of that.  Your explanation with base-255
numbers is very good.  It doesn't really work for all-0xff strings,
because adding one (with carry) to "\xff\xff" would give "\x01\x00\x00",
but it doesn't really matter for the explanation :).

Simon

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

* Re: [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers
  2017-11-20 16:50             ` Simon Marchi
@ 2017-11-21  0:11               ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-21  0:11 UTC (permalink / raw)
  To: Simon Marchi, Simon Marchi, gdb-patches

On 11/20/2017 04:50 PM, Simon Marchi wrote:
> On 2017-11-20 06:56 AM, Pedro Alves wrote:

>> static std::string
>> make_sort_after_prefix_name (const char *search_name)
>> {
>>   /* When looking to complete "func", we find the upper bound of all
>>      symbols that start with "func" by looking for where we'd insert
>>      the closest string that would follow "func" in lexicographical
>>      order.  Usually, that's "func"-with-last-character-incremented,
>>      i.e. "fund".  Mind non-ASCII characters, though.  Usually those
>>      will be UTF-8 multi-byte sequences, but we can't be certain.
>>      Especially mind the 0xff character, which is a valid character in
>>      non-UTF-8 source character sets (e.g. Latin1 'ÿ'), and we can't
>>      rule out compilers allowing it in identifiers.  Note that
>>      conveniently, strcmp/strcasecmp are specified to compare
>>      characters interpreted as unsigned char.  So what we do is treat
>>      the whole string as a base 255 number composed of a sequence of
>>      base 255 "digits" and add 1 to it.  I.e., adding 1 to 0xff wraps
>>      to 0, and carries 1 to the following more-significant position.
>>      If the very first character carries/overflows, then the upper
>>      bound is the end of the list.  Also the string after the empty
>>      string is also the empty string.
> 
> Making an analogy with base-10 arithmetic is actually what made me
> understand it.  The number after 149 is not 140, it's 150.  We're
> doing the string equivalent of that.  Your explanation with base-255
> numbers is very good.  It doesn't really work for all-0xff strings,
> because adding one (with carry) to "\xff\xff" would give "\x01\x00\x00",
> but it doesn't really matter for the explanation :).

:-)  I've extended the text slightly to hopefully make that
even clearer.

Oh, and I realized that it's based 256, not 255.  
Classical off-by-one.  :-P

I also fixed the \xffa\ff in the example: we need to split the
string after ff, because otherwise 'a' is interpreted as a hex digit.

Below's what I pushed now.

Thanks for the reviews!

From e1ef7d7a5166f846b14bea5a77acb0dec76661a8 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Tue, 21 Nov 2017 00:02:46 +0000
Subject: [PATCH] 0xff chars in name components table; cp-name-parser lex UTF-8
 identifiers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The find-upper-bound-for-completion algorithm in the name components
accelerator table in dwarf2read.c increments a char in a string, and
asserts that it's not incrementing a 0xff char, but that's incorrect.

First, we shouldn't be calling gdb_assert on input.

Then, if "char" is signed, comparing a caracther with "0xff" will
never yield true, which is caught by Clang with:

  error: comparison of constant 255 with expression of type '....' (aka 'char') is always true [-Werror,-Wtautological-constant-out-of-range-compare]
	    gdb_assert (after.back () != 0xff);
			~~~~~~~~~~~~~ ^  ~~~~

And then, 0xff is a valid character on non-UTF-8/ASCII character sets.
E.g., it's 'ÿ' in Latin1.  While GCC nor Clang support !ASCII &&
!UTF-8 characters in identifiers (GCC supports UTF-8 characters only
via UCNs, see https://gcc.gnu.org/onlinedocs/cpp/Character-sets.html),
but other compilers might (Visual Studio?), so it doesn't hurt to
handle it correctly.  Testing is covered by extending the
dw2_expand_symtabs_matching unit tests with relevant cases.

However, without further changes, the unit tests still fail...  The
problem is that cp-name-parser.y assumes that identifiers are ASCII
(via ISALPHA/ISALNUM).  This commit fixes that too, so that we can
unit test the dwarf2read.c changes.  (The regular C/C++ lexer in
c-lang.y needs a similar treatment, but I'm leaving that for another
patch.)

While doing this, I noticed a thinko in the computation of the upper
bound for completion in dw2_expand_symtabs_matching_symbol.  We're
using std::upper_bound but we should use std::lower_bound.  I extended
the unit test with a case that I thought would expose it, this one:

 +  /* These are used to check that the increment-last-char in the
 +     matching algorithm for completion doesn't match "t1_fund" when
 +     completing "t1_func".  */
 +  "t1_func",
 +  "t1_func1",
 +  "t1_fund",
 +  "t1_fund1",

The algorithm actually returns "t1_fund1" as lower bound, so "t1_fund"
matches incorrectly.  But turns out the problem is masked because
later here:

  for (;lower != upper; ++lower)
    {
      const char *qualified = index.symbol_name_at (lower->idx);

      if (!lookup_name_matcher.matches (qualified)

the lookup_name_matcher.matches check above filters out "t1_fund"
because that doesn't start with "t1_func".

I'll fix the latent bug in follow up patches, after factoring things
out a bit in a way that allows unit testing the relevant code more
directly.

gdb/ChangeLog:
2017-11-21  Pedro Alves  <palves@redhat.com>

	* cp-name-parser.y (cp_ident_is_alpha, cp_ident_is_alnum): New.
	(symbol_end): Use cp_ident_is_alnum.
	(yylex): Use cp_ident_is_alpha and cp_ident_is_alnum.
	* dwarf2read.c (make_sort_after_prefix_name): New function.
	(dw2_expand_symtabs_matching_symbol): Use it.
	(test_symbols): Add more symbols.
	(run_test): Add tests.
---
 gdb/ChangeLog        |  10 ++++
 gdb/cp-name-parser.y |  28 +++++++++--
 gdb/dwarf2read.c     | 136 +++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 157 insertions(+), 17 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 31e447a..1a38573 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,13 @@
+2017-11-21  Pedro Alves  <palves@redhat.com>
+
+	* cp-name-parser.y (cp_ident_is_alpha, cp_ident_is_alnum): New.
+	(symbol_end): Use cp_ident_is_alnum.
+	(yylex): Use cp_ident_is_alpha and cp_ident_is_alnum.
+	* dwarf2read.c (make_sort_after_prefix_name): New function.
+	(dw2_expand_symtabs_matching_symbol): Use it.
+	(test_symbols): Add more symbols.
+	(run_test): Add tests.
+
 2017-11-17  Tom Tromey  <tom@tromey.com>
 
 	* symtab.h (enum symbol_subclass_kind): New.
diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y
index 33ecf13..fdfbf15 100644
--- a/gdb/cp-name-parser.y
+++ b/gdb/cp-name-parser.y
@@ -1304,6 +1304,28 @@ d_binary (const char *name, struct demangle_component *lhs, struct demangle_comp
 		      fill_comp (DEMANGLE_COMPONENT_BINARY_ARGS, lhs, rhs));
 }
 
+/* Like ISALPHA, but also returns true for the union of all UTF-8
+   multi-byte sequence bytes and non-ASCII characters in
+   extended-ASCII charsets (e.g., Latin1).  I.e., returns true if the
+   high bit is set.  Note that not all UTF-8 ranges are allowed in C++
+   identifiers, but we don't need to be pedantic so for simplicity we
+   ignore that here.  Plus this avoids the complication of actually
+   knowing what was the right encoding.  */
+
+static inline bool
+cp_ident_is_alpha (unsigned char ch)
+{
+  return ISALPHA (ch) || ch >= 0x80;
+}
+
+/* Similarly, but Like ISALNUM.  */
+
+static inline bool
+cp_ident_is_alnum (unsigned char ch)
+{
+  return ISALNUM (ch) || ch >= 0x80;
+}
+
 /* Find the end of a symbol name starting at LEXPTR.  */
 
 static const char *
@@ -1311,7 +1333,7 @@ symbol_end (const char *lexptr)
 {
   const char *p = lexptr;
 
-  while (*p && (ISALNUM (*p) || *p == '_' || *p == '$' || *p == '.'))
+  while (*p && (cp_ident_is_alnum (*p) || *p == '_' || *p == '$' || *p == '.'))
     p++;
 
   return p;
@@ -1791,7 +1813,7 @@ yylex (void)
       return ERROR;
     }
 
-  if (!(c == '_' || c == '$' || ISALPHA (c)))
+  if (!(c == '_' || c == '$' || cp_ident_is_alpha (c)))
     {
       /* We must have come across a bad character (e.g. ';').  */
       yyerror (_("invalid character"));
@@ -1802,7 +1824,7 @@ yylex (void)
   namelen = 0;
   do
     c = tokstart[++namelen];
-  while (ISALNUM (c) || c == '_' || c == '$');
+  while (cp_ident_is_alnum (c) || c == '_' || c == '$');
 
   lexptr += namelen;
 
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 5437d21..96c1393 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -4188,6 +4188,79 @@ gdb_index_symbol_name_matcher::matches (const char *symbol_name)
   return false;
 }
 
+/* Starting from a search name, return the string that finds the upper
+   bound of all strings that start with SEARCH_NAME in a sorted name
+   list.  Returns the empty string to indicate that the upper bound is
+   the end of the list.  */
+
+static std::string
+make_sort_after_prefix_name (const char *search_name)
+{
+  /* When looking to complete "func", we find the upper bound of all
+     symbols that start with "func" by looking for where we'd insert
+     the closest string that would follow "func" in lexicographical
+     order.  Usually, that's "func"-with-last-character-incremented,
+     i.e. "fund".  Mind non-ASCII characters, though.  Usually those
+     will be UTF-8 multi-byte sequences, but we can't be certain.
+     Especially mind the 0xff character, which is a valid character in
+     non-UTF-8 source character sets (e.g. Latin1 'ÿ'), and we can't
+     rule out compilers allowing it in identifiers.  Note that
+     conveniently, strcmp/strcasecmp are specified to compare
+     characters interpreted as unsigned char.  So what we do is treat
+     the whole string as a base 256 number composed of a sequence of
+     base 256 "digits" and add 1 to it.  I.e., adding 1 to 0xff wraps
+     to 0, and carries 1 to the following more-significant position.
+     If the very first character in SEARCH_NAME ends up incremented
+     and carries/overflows, then the upper bound is the end of the
+     list.  The string after the empty string is also the empty
+     string.
+
+     Some examples of this operation:
+
+       SEARCH_NAME  => "+1" RESULT
+
+       "abc"              => "abd"
+       "ab\xff"           => "ac"
+       "\xff" "a" "\xff"  => "\xff" "b"
+       "\xff"             => ""
+       "\xff\xff"         => ""
+       ""                 => ""
+
+     Then, with these symbols for example:
+
+      func
+      func1
+      fund
+
+     completing "func" looks for symbols between "func" and
+     "func"-with-last-character-incremented, i.e. "fund" (exclusive),
+     which finds "func" and "func1", but not "fund".
+
+     And with:
+
+      funcÿ     (Latin1 'ÿ' [0xff])
+      funcÿ1
+      fund
+
+     completing "funcÿ" looks for symbols between "funcÿ" and "fund"
+     (exclusive), which finds "funcÿ" and "funcÿ1", but not "fund".
+
+     And with:
+
+      ÿÿ        (Latin1 'ÿ' [0xff])
+      ÿÿ1
+
+     completing "ÿ" or "ÿÿ" looks for symbols between between "ÿÿ" and
+     the end of the list.
+  */
+  std::string after = search_name;
+  while (!after.empty () && (unsigned char) after.back () == 0xff)
+    after.pop_back ();
+  if (!after.empty ())
+    after.back () = (unsigned char) after.back () + 1;
+  return after;
+}
+
 /* Helper for dw2_expand_symtabs_matching that works with a
    mapped_index instead of the containing objfile.  This is split to a
    separate function in order to be able to unit test the
@@ -4303,21 +4376,20 @@ dw2_expand_symtabs_matching_symbol
     {
       if (lookup_name_in.completion_mode ())
 	{
-	  /* The string frobbing below won't work if the string is
-	     empty.  We don't need it then, anyway -- if we're
-	     completing an empty string, then we want to iterate over
-	     the whole range.  */
-	  if (cplus[0] == '\0')
+	  /* In completion mode, we want UPPER to point past all
+	     symbols names that have the same prefix.  I.e., with
+	     these symbols, and completing "func":
+
+	      function        << lower bound
+	      function1
+	      other_function  << upper bound
+
+	     We find the upper bound by looking for the insertion
+	     point of "func"-with-last-character-incremented,
+	     i.e. "fund".  */
+	  std::string after = make_sort_after_prefix_name (cplus);
+	  if (after.empty ())
 	    return end;
-
-	  /* In completion mode, increment the last character because
-	     we want UPPER to point past all symbols names that have
-	     the same prefix.  */
-	  std::string after = cplus;
-
-	  gdb_assert (after.back () != 0xff);
-	  after.back ()++;
-
 	  return std::upper_bound (lower, end, after.c_str (),
 				   lookup_compare_upper);
 	}
@@ -4493,6 +4565,26 @@ static const char *test_symbols[] = {
   "ns::foo<int>",
   "ns::foo<long>",
 
+  /* These are used to check that the increment-last-char in the
+     matching algorithm for completion doesn't match "t1_fund" when
+     completing "t1_func".  */
+  "t1_func",
+  "t1_func1",
+  "t1_fund",
+  "t1_fund1",
+
+  /* A UTF-8 name with multi-byte sequences to make sure that
+     cp-name-parser understands this as a single identifier ("função"
+     is "function" in PT).  */
+  u8"u8função",
+
+  /* \377 (0xff) is Latin1 'ÿ'.  */
+  "yfunc\377",
+
+  /* \377 (0xff) is Latin1 'ÿ'.  */
+  "\377",
+  "\377\377123",
+
   /* A name with all sorts of complications.  Starts with "z" to make
      it easier for the completion tests below.  */
 #define Z_SYM_NAME \
@@ -4551,6 +4643,22 @@ run_test ()
 		   {});
     }
 
+  /* Check that the name matching algorithm for completion doesn't get
+     confused with Latin1 'ÿ' / 0xff.  */
+  {
+    static const char str[] = "\377";
+    CHECK_MATCH (str, symbol_name_match_type::FULL, true,
+		 EXPECT ("\377", "\377\377123"));
+  }
+
+  /* Check that the increment-last-char in the matching algorithm for
+     completion doesn't match "t1_fund" when completing "t1_func".  */
+  {
+    static const char str[] = "t1_func";
+    CHECK_MATCH (str, symbol_name_match_type::FULL, true,
+		 EXPECT ("t1_func", "t1_func1"));
+  }
+
   /* Check that completion mode works at each prefix of the expected
      symbol name.  */
   {
-- 
2.5.5

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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-08-08 23:48   ` Keith Seitz
@ 2017-11-22 16:48     ` Pedro Alves
  2017-11-24 16:48       ` Pedro Alves
  2017-11-28  0:02       ` Keith Seitz
  0 siblings, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-22 16:48 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

Hi Keith,

Thanks again for the review!  And sorry for the belated
replies...  I'm finally coming around to this, seeing about getting
it into GDB 8.1, now that the lookup_name_info prereq is merged.
(Joel wants to branch next week.)

Please see question below.

On 08/09/2017 12:48 AM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
>> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
>> index 70c0e02..328b6db 100644
>> --- a/gdb/breakpoint.c
>> +++ b/gdb/breakpoint.c
>> @@ -15694,7 +15694,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
>>  syntax to specify location parameters.\n\
>>  Example: To specify the start of the label named \"the_top\" in the\n\
>>  function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
>> --function fact -label the_top\".\n"
>> +-function fact -label the_top\".\n\
>> +For C++, \"-function\" matches all functions with the given name ignoring\n\
>                                                                    ^
> comma
> 

Added.

>> +missing leading specifiers (namespaces and classes).  You can override\n\
>> +that by instead specifying a fully qualified name using \"-qualified\".\n"
> 
> I think this would read better if it read: "This behavior may be overridden
> by using the \"-qualified\" flag and specifying a fully qualified name."
> [I am not a fan of using informal writing in documentation.]

How about the even simpler:

@@ -15295,7 +15295,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
 syntax to specify location parameters.\n\
 Example: To specify the start of the label named \"the_top\" in the\n\
 function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
--function fact -label the_top\".\n"
+-function fact -label the_top\".\n\
+For C++, \"-function\" matches functions and methods by name, ignoring\n\
+missing leading specifiers (namespaces and classes).\n\
+\"-qualified\" matches functions and methods by fully qualified name.\n"
 
> 
>>  /* This help string is used for the break, hbreak, tbreak and thbreak
>>     commands.  It is defined as a macro to prevent duplication.
>> diff --git a/gdb/completer.c b/gdb/completer.c
>> index eabbce7..99e40a3 100644
>> --- a/gdb/completer.c
>> +++ b/gdb/completer.c
>> @@ -609,6 +612,7 @@ static const char *const explicit_options[] =
>>    {
>>      "-source",
>>      "-function",
>> +    "-qualified",
>>      "-line",
>>      "-label",
>>      NULL
> 
> The "-qualified" option can be used with linespecs, too, right?

Not really, no.

> 
> (gdb) b -qualified A::b   (a linespec location)
> (gdb) b -qualified -function A::b  (an explicit location)
> 
> Actually, I see that it does not work (yet?). Consider:
> 
>      1	struct A
>      2	{
>      3	  int doit ()
>      4	  {
>      5	    int i;
>      6	
>      7	    for (i = 0; i < 10; ++i)
>      8	      {
>      9	        switch (i)
>     10		  {
>     11		  top: 
>     12	          case 5:
>     13	            ++i;
>     14	            goto top;
>     15	          default:  break;
>     16		  }
>     17	      }
>     18	    return i;
>     19	  }
>     20	};
> 
> (gdb) b A::doit:top
> Breakpoint 1 at 0x400633: file simple-label.cc, line 11.
> (gdb) b -function A::doit -label top
> Note: breakpoint 1 also set at pc 0x400633.
> Breakpoint 2 at 0x400633: file simple-label.cc, line 11.
> (gdb) b -qualified A::doit:top
> Function "A::doit:top" not defined.
> Make breakpoint pending on future shared library load? (y or [n]) n
> (gdb) b -qualified A::doit -label top
> Note: breakpoints 1 and 2 also set at pc 0x400633.
> Breakpoint 3 at 0x400633: file simple-label.cc, line 11.
> 
> Is there a reason to exclude linespecs? Perhaps naively, I would have thought to
> make -qualified a parsing option instead of a replacement for -function.

I went this route because it seemed to me that a separate
option would end up being less convenient, because it forces you
to type/combine two options:

 (gdb) b -q -f A::doit:top

and I thought that since you're requiring an option, and
explicit linespecs are just superior to regular linespecs, that
it'd be OK to move users to explicit linespecs if they need
a qualified name.

Also, I thought that a separate option may cause confusion
like this:

 (gdb) b -f func[TAB]    # completes ignoring missing qualifiers
 *hmm, not what I wanted.
 *press up
 *add "-q"
 *move left 3 chars, to complete the function again.
 *press TAB again
 (gdb) b -f func[TAB] -q   # still completes ignoring missing qualifiers
 *oh right, I need to put -q before -f ...
 (gdb) b -q -f func[TAB]   # _now_ completes a fully qualified name.

Dunno.

Do you see "-qualified" being an alternative to "-function"
instead of a flag as a blocker?

Please let me know.

> 
>> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
>> index 397c738..84d8a6b 100644
>> --- a/gdb/cp-support.c
>> +++ b/gdb/cp-support.c
>> @@ -1671,19 +1760,151 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
>>    return false;
>>  }
>>  
>> +/* C++ symbol_name_matcher_ftype implementation for wild matches.
>> +   Defers work to cp_symbol_name_ncmp.  */
>                      ^^^^^^^^^^^^^^^^^^^
> 
> I think that's supposed to be cp_symbol_name_matches_1.

Indeed.

>> --- a/gdb/cp-support.h
>> +++ b/gdb/cp-support.h
>> @@ -110,6 +110,8 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
>>  extern struct type *cp_lookup_rtti_type (const char *name,
>>  					 struct block *block);
>>  
>> +extern unsigned int cp_search_name_hash (const char *search_name);
>> +
> 
> Shouldn't the comment from cp-support.c be here?

Fixed.

> 
>>  extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
>>    (const lookup_name_info &lookup_name);
>>  
> [snip]
> 
> WDYT?

Here's the current/updated patch.

From 311e7f279219840722871c57244ecbf405a46f7e Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 22 Nov 2017 16:33:22 +0000
Subject: [PATCH] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and
 wild matching]

Finally, this patch teaches GDB about setting breakpoints ignoring
namespaces, etc. by default.

Here's a contrived example:

  (gdb) b func<tab>
  (anonymous namespace)::A::function()            Bn::(anonymous namespace)::B::function()        function(int, int)
  (anonymous namespace)::B::function()            Bn::(anonymous namespace)::function()           gdb::(anonymous namespace)::A::function()
  (anonymous namespace)::B::function() const      Bn::(anonymous namespace)::function(int, int)   gdb::(anonymous namespace)::function()
  (anonymous namespace)::function()               Bn::B::func()                                   gdb::(anonymous namespace)::function(int, int)
  (anonymous namespace)::function(int, int)       Bn::B::function()                               gdb::A::func()
  A::func()                                       Bn::func()                                      gdb::A::function()
  A::function()                                   Bn::function()                                  gdb::func()
  B::func()                                       Bn::function(int, int)                          gdb::function()
  B::function()                                   Bn::function(long)                              gdb::function(int, int)
  B::function() const                             func()                                          gdb::function(long)
  B::function_const() const                       function()
  (gdb) b function
  Breakpoint 1 at 0x4005ce: function. (26 locations)

  (gdb) b B::function<tab>
  (anonymous namespace)::B::function()        B::function() const                         Bn::B::function()
  (anonymous namespace)::B::function() const  B::function_const() const
  B::function()                               Bn::(anonymous namespace)::B::function()
  (gdb) b B::function
  Breakpoint 1 at 0x40072c: B::function. (6 locations)

Notice that the symbols that the completer finds matches the number of
symbols that settting a breakpoint finds.  The testsuite patch will
add extensive and comprehensive tests to make sure of that in many
cases.

To get back the original behavior of interpreting the function name as
a fully-qualified name, you use the new "-qualified" explicit linespec
option.  For example:

 (gdb) b B::function
 (anonymous namespace)::B::function()        B::function() const                         Bn::B::function()
 (anonymous namespace)::B::function() const  B::function_const() const
 B::function()                               Bn::(anonymous namespace)::B::function()

vs:

 (gdb) b -qualified B::function
 B::function()              B::function() const        B::function_const() const

I've chosen "qualified" because "f" is already taken, for "-function".

To better understand how this is the better default, consider what
happens when we get to the point when _all_ of GDB is wrapped under
"namespace gdb {}".  I have a patch series that does that, and when I
started debugging that GDB, I immediately became frustrated.  You now
have to write "b gdb::internal_error", "b gdb::foo", "b gdb::bar",
etc. etc., which gets annoying pretty quickly.  OTOH, consider how
this makes it very easy to set breakpoints in classes wrapped in
anonymous namespaces.  You just don't think of them, GDB finds the
symbols for you automatically.

Implementation-wise, what the patch does is:

  - make C++ symbol name hashing only consider the last component of a
    symbol name.

  - add a symbol name matcher for symbol_name_match_type::WILD.

  - add unit tests.

  - adjust a few tests to use "-qualified" when they mean it.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* breakpoint.c (LOCATION_HELP_STRING): Mention "-qualified".
	* c-lang.c (cplus_language_defn): Install cp_search_name_hash.
	* completer.c (explicit_location_match_type::MATCH_QUALIFIED): New
	enumerator.
	(explicit_options): Add "-qualified".
	(collect_explicit_location_matches): Handle MATCH_QUALIFIED.
	* cp-support.c (cp_search_name_hash, cp_symbol_name_matches_1)
	(cp_symbol_name_matches): New functions.
	(cp_get_symbol_name_matcher): Return different matchers depending
	on the lookup name's match type.
	(selftests::test_cp_symbol_name_matches): New function.
	(_initialize_cp_support): Register it.
	* cp-support.h (cp_search_name_hash): New declaration.
	* dwarf2read.c
	(selftests::dw2_expand_symtabs_matching::test_symbols): Add
	symbols.
	(selftests::dw2_expand_symtabs_matching::run_test): Add wild
	matching tests.
	* linespec.c (linespec_parse_basic): Lookup function symbols using
	symbol_name_match_type::WILD.
	(convert_explicit_location_to_linespec): New
	symbol_name_match_type parameter.  Pass it down to
	find_linespec_symbols.
	(convert_explicit_location_to_sals): Pass the location's name
	match type to convert_explicit_location_to_linespec.
	(linespec_complete_function): New symbol_name_match_type
	parameter.  Use it.
	(complete_linespec_component): Complete symbols using
	symbol_name_match_type::WILD.
	(linespec_complete_label): New symbol_name_match_type parameter.
	Use it.
	(linespec_complete): Complete symbols using
	symbol_name_match_type::WILD.
	(find_function_symbols, find_linespec_symbols): New
	symbol_name_match_type parameter.  Pass it down.
	* linespec.h (linespec_complete_function)
	(linespec_complete_label): New symbol_name_match_type parameter.
	* location.c (explicit_to_string_internal)
	(string_to_explicit_location): Handle "-qualified".
	* location.h (explicit_location::func_name_match_type): New field.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/langs.exp: Use -qualified.
	* gdb.cp/meth-typedefs.exp: Use -qualified, and add tests without
	it.
	* gdb.cp/namespace.exp: Use -qualified.
---
 gdb/breakpoint.c                       |   5 +-
 gdb/c-lang.c                           |   2 +-
 gdb/completer.c                        |   7 ++
 gdb/cp-support.c                       | 223 ++++++++++++++++++++++++++++++++-
 gdb/cp-support.h                       |   7 ++
 gdb/dwarf2read.c                       |  48 +++++++
 gdb/linespec.c                         |  24 ++--
 gdb/linespec.h                         |   9 +-
 gdb/location.c                         |  18 ++-
 gdb/location.h                         |   3 +
 gdb/testsuite/gdb.base/langs.exp       |   2 +-
 gdb/testsuite/gdb.cp/meth-typedefs.exp |  39 +++++-
 gdb/testsuite/gdb.cp/namespace.exp     |   2 +-
 13 files changed, 366 insertions(+), 23 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index d8d0ed0..2d794ff 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -15295,7 +15295,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
 syntax to specify location parameters.\n\
 Example: To specify the start of the label named \"the_top\" in the\n\
 function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
--function fact -label the_top\".\n"
+-function fact -label the_top\".\n\
+For C++, \"-function\" matches functions and methods by name, ignoring\n\
+missing leading specifiers (namespaces and classes).\n\
+\"-qualified\" matches functions and methods by fully qualified name.\n"
 
 /* This help string is used for the break, hbreak, tbreak and thbreak
    commands.  It is defined as a macro to prevent duplication.
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 49077c7..8d96f94 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -1016,7 +1016,7 @@ extern const struct language_defn cplus_language_defn =
   c_watch_location_expression,
   cp_get_symbol_name_matcher,
   iterate_over_symbols,
-  default_search_name_hash,
+  cp_search_name_hash,
   &cplus_varobj_ops,
   NULL,
   NULL,
diff --git a/gdb/completer.c b/gdb/completer.c
index 0c50459..aa0f105 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -76,6 +76,9 @@ enum explicit_location_match_type
     /* The name of a function or method.  */
     MATCH_FUNCTION,
 
+    /* The fully-qualified name of a function or method.  */
+    MATCH_QUALIFIED,
+
     /* A line number.  */
     MATCH_LINE,
 
@@ -602,6 +605,7 @@ static const char *const explicit_options[] =
   {
     "-source",
     "-function",
+    "-qualified",
     "-line",
     "-label",
     NULL
@@ -653,9 +657,11 @@ collect_explicit_location_matches (completion_tracker &tracker,
       break;
 
     case MATCH_FUNCTION:
+    case MATCH_QUALIFIED:
       {
 	const char *function = string_or_empty (explicit_loc->function_name);
 	linespec_complete_function (tracker, function,
+				    explicit_loc->func_name_match_type,
 				    explicit_loc->source_filename);
       }
       break;
@@ -670,6 +676,7 @@ collect_explicit_location_matches (completion_tracker &tracker,
 	linespec_complete_label (tracker, language,
 				 explicit_loc->source_filename,
 				 explicit_loc->function_name,
+				 explicit_loc->func_name_match_type,
 				 label);
       }
       break;
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index a917ada..5f077ac 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1615,7 +1615,92 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
   return *demangled != NULL;
 }
 
-/* C++ symbol_name_matcher_ftype implementation.  */
+/* See cp-support.h.  */
+
+unsigned int
+cp_search_name_hash (const char *search_name)
+{
+  /* cp_entire_prefix_len assumes a fully-qualified name with no
+     leading "::".  */
+  if (startswith (search_name, "::"))
+    search_name += 2;
+
+  unsigned int prefix_len = cp_entire_prefix_len (search_name);
+  if (prefix_len != 0)
+    search_name += prefix_len + 2;
+
+  return default_search_name_hash (search_name);
+}
+
+/* Helper for cp_symbol_name_matches (i.e., symbol_name_matcher_ftype
+   implementation for symbol_name_match_type::WILD matching).  Split
+   to a separate function for unit-testing convenience.
+
+   If SYMBOL_SEARCH_NAME has more scopes than LOOKUP_NAME, we try to
+   match ignoring the extra leading scopes of SYMBOL_SEARCH_NAME.
+   This allows conveniently setting breakpoints on functions/methods
+   inside any namespace/class without specifying the fully-qualified
+   name.
+
+   E.g., these match:
+
+    [symbol search name]   [lookup name]
+    foo::bar::func         foo::bar::func
+    foo::bar::func         bar::func
+    foo::bar::func         func
+
+   While these don't:
+
+    [symbol search name]   [lookup name]
+    foo::zbar::func        bar::func
+    foo::bar::func         foo::func
+
+   See more examples in the test_cp_symbol_name_matches selftest
+   function below.
+
+   See symbol_name_matcher_ftype for description of SYMBOL_SEARCH_NAME
+   and COMP_MATCH_RES.  .
+
+   LOOKUP_NAME/LOOKUP_NAME_LEN is the name we're looking up.
+
+   See strncmp_iw_with_mode for description of MODE.
+*/
+
+static bool
+cp_symbol_name_matches_1 (const char *symbol_search_name,
+			  const char *lookup_name,
+			  size_t lookup_name_len,
+			  strncmp_iw_mode mode,
+			  completion_match_result *comp_match_res)
+{
+  const char *sname = symbol_search_name;
+
+  while (true)
+    {
+      if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len,
+				mode) == 0)
+	{
+	  if (comp_match_res != NULL)
+	    {
+	      comp_match_res->match.set_match (symbol_search_name);
+	      comp_match_res->match_for_lcd.set_match (sname);
+	    }
+	  return true;
+	}
+
+      unsigned int len = cp_find_first_component (sname);
+
+      if (sname[len] == '\0')
+	return false;
+
+      gdb_assert (sname[len] == ':');
+      /* Skip the '::'.  */
+      sname += len + 2;
+    }
+}
+
+/* C++ symbol_name_matcher_ftype implementation for fully qualified
+   matches.  */
 
 static bool
 cp_fq_symbol_name_matches (const char *symbol_search_name,
@@ -1644,18 +1729,150 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
   return false;
 }
 
+/* C++ symbol_name_matcher_ftype implementation for wild matches.
+   Defers work to cp_symbol_name_matches_1.  */
+
+static bool
+cp_symbol_name_matches (const char *symbol_search_name,
+			const lookup_name_info &lookup_name,
+			completion_match_result *comp_match_res)
+{
+  const std::string &name = lookup_name.cplus ().lookup_name ();
+
+  strncmp_iw_mode mode = (lookup_name.completion_mode ()
+			  ? strncmp_iw_mode::NORMAL
+			  : strncmp_iw_mode::MATCH_PARAMS);
+
+  return cp_symbol_name_matches_1 (symbol_search_name,
+				   name.c_str (), name.size (),
+				   mode, comp_match_res);
+}
+
 /* See cp-support.h.  */
 
 symbol_name_matcher_ftype *
 cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
 {
-  return cp_fq_symbol_name_matches;
+  switch (lookup_name.match_type ())
+    {
+    case symbol_name_match_type::FULL:
+    case symbol_name_match_type::EXPRESSION:
+      return cp_fq_symbol_name_matches;
+    case symbol_name_match_type::WILD:
+      return cp_symbol_name_matches;
+    }
+
+  gdb_assert_not_reached ("");
 }
 
 #if GDB_SELF_TEST
 
 namespace selftests {
 
+void
+test_cp_symbol_name_matches ()
+{
+#define CHECK_MATCH(SYMBOL, INPUT)					\
+  SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL,				\
+					INPUT, sizeof (INPUT) - 1,	\
+					strncmp_iw_mode::MATCH_PARAMS,	\
+					NULL))
+
+#define CHECK_NOT_MATCH(SYMBOL, INPUT)					\
+  SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL,			\
+					 INPUT, sizeof (INPUT) - 1,	\
+					 strncmp_iw_mode::MATCH_PARAMS,	\
+					 NULL))
+
+  /* Like CHECK_MATCH, and also check that INPUT (and all substrings
+     that start at index 0) completes to SYMBOL.  */
+#define CHECK_MATCH_C(SYMBOL, INPUT)					\
+  CHECK_MATCH (SYMBOL, INPUT);						\
+  for (size_t i = 0; i < sizeof (INPUT) - 1; i++)			\
+    SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL, INPUT, i,		\
+					  strncmp_iw_mode::NORMAL,	\
+					  NULL))
+
+  /* Like CHECK_NOT_MATCH, and also check that INPUT does NOT complete
+     to SYMBOL.  */
+#define CHECK_NOT_MATCH_C(SYMBOL, INPUT)				\
+  CHECK_NOT_MATCH (SYMBOL, INPUT);					\
+  SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL, INPUT,			\
+					 sizeof (INPUT) - 1,	\
+					 strncmp_iw_mode::NORMAL,	\
+					 NULL))
+
+  /* Lookup name without parens matches all overloads.  */
+  CHECK_MATCH_C ("function()", "function");
+  CHECK_MATCH_C ("function(int)", "function");
+
+  /* Check whitespace around parameters is ignored.  */
+  CHECK_MATCH_C ("function()", "function ()");
+  CHECK_MATCH_C ("function ( )", "function()");
+  CHECK_MATCH_C ("function ()", "function( )");
+  CHECK_MATCH_C ("func(int)", "func( int )");
+  CHECK_MATCH_C ("func(int)", "func ( int ) ");
+  CHECK_MATCH_C ("func ( int )", "func( int )");
+  CHECK_MATCH_C ("func ( int )", "func ( int ) ");
+
+  /* Check symbol name prefixes aren't incorrectly matched.  */
+  CHECK_NOT_MATCH ("func", "function");
+  CHECK_NOT_MATCH ("function", "func");
+  CHECK_NOT_MATCH ("function()", "func");
+
+  /* Check that if the lookup name includes parameters, only the right
+     overload matches.  */
+  CHECK_MATCH_C ("function(int)", "function(int)");
+  CHECK_NOT_MATCH_C ("function(int)", "function()");
+
+  /* Tests matching symbols in some scope.  */
+  CHECK_MATCH_C ("foo::function()", "function");
+  CHECK_MATCH_C ("foo::function(int)", "function");
+  CHECK_MATCH_C ("foo::bar::function()", "function");
+  CHECK_MATCH_C ("bar::function()", "bar::function");
+  CHECK_MATCH_C ("foo::bar::function()", "bar::function");
+  CHECK_MATCH_C ("foo::bar::function(int)", "bar::function");
+
+  /* Same, with parameters in the lookup name.  */
+  CHECK_MATCH_C ("foo::function()", "function()");
+  CHECK_MATCH_C ("foo::bar::function()", "function()");
+  CHECK_MATCH_C ("foo::function(int)", "function(int)");
+  CHECK_MATCH_C ("foo::function()", "foo::function()");
+  CHECK_MATCH_C ("foo::bar::function()", "bar::function()");
+  CHECK_MATCH_C ("foo::bar::function(int)", "bar::function(int)");
+  CHECK_NOT_MATCH_C ("foo::bar::function(int)", "bar::function()");
+
+  CHECK_MATCH_C ("(anonymous namespace)::bar::function(int)",
+		 "bar::function(int)");
+  CHECK_MATCH_C ("foo::(anonymous namespace)::bar::function(int)",
+		 "function(int)");
+
+  /* Lookup scope wider than symbol scope, should not match.  */
+  CHECK_NOT_MATCH_C ("function()", "bar::function");
+  CHECK_NOT_MATCH_C ("function()", "bar::function()");
+
+  /* An explicit global scope forces a fully qualified match.  */
+  CHECK_NOT_MATCH_C ("foo::function()", "::function");
+  CHECK_NOT_MATCH_C ("foo::function()", "::function()");
+  CHECK_NOT_MATCH_C ("foo::function(int)", "::function()");
+  CHECK_NOT_MATCH_C ("foo::function(int)", "::function(int)");
+
+  CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi()");
+  CHECK_MATCH_C ("abc::def::ghi ( )", "abc::def::ghi()");
+  CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi ( )");
+  CHECK_MATCH_C ("function()", "function()");
+  CHECK_MATCH_C ("foo::function()", "function()");
+  CHECK_MATCH_C ("foo::bar::function()", "function()");
+  CHECK_MATCH_C ("bar::function()", "bar::function()");
+  CHECK_MATCH_C ("foo::bar::function()", "bar::function");
+  CHECK_MATCH_C ("(anonymous namespace)::bar::function(int)",
+		 "function(int)");
+  CHECK_MATCH_C ("foo::(anonymous namespace)::bar::function(int)",
+		 "function(int)");
+  CHECK_NOT_MATCH_C ("function()", "bar::function");
+  CHECK_NOT_MATCH_C ("foo::function()", "::function");
+}
+
 /* If non-NULL, return STR wrapped in quotes.  Otherwise, return a
    "<null>" string (with no quotes).  */
 
@@ -1859,6 +2076,8 @@ display the offending symbol."),
 #endif
 
 #if GDB_SELF_TEST
+  selftests::register_test ("cp_symbol_name_matches",
+			    selftests::test_cp_symbol_name_matches);
   selftests::register_test ("cp_remove_params",
 			    selftests::test_cp_remove_params);
 #endif
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 44d8269..010fc9b 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -116,6 +116,13 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
 extern struct type *cp_lookup_rtti_type (const char *name,
 					 struct block *block);
 
+/* Produce an unsigned hash value from SEARCH_NAME that is compatible
+   with cp_symbol_name_matches.  Only the last component in
+   "foo::bar::function()" is considered for hashing purposes (i.e.,
+   the entire prefix is skipped), so that later on looking up for
+   "function" or "bar::function" in all namespaces is possible.  */
+extern unsigned int cp_search_name_hash (const char *search_name);
+
 /* Implement the "la_get_symbol_name_matcher" language_defn method for
    C++.  */
 extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 686fa10..65f7019 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -4608,6 +4608,8 @@ static const char *test_symbols[] = {
   "ns::foo<char*>",
   "ns::foo<int>",
   "ns::foo<long>",
+  "ns2::tmpl<int>::foo2",
+  "(anonymous namespace)::A::B::C",
 
   /* These are used to check that the increment-last-char in the
      matching algorithm for completion doesn't match "t1_fund" when
@@ -4794,6 +4796,8 @@ test_dw2_expand_symtabs_matching_symbol ()
   {
     CHECK_MATCH ("w", symbol_name_match_type::FULL, true,
 		 EXPECT ("w1::w2"));
+    CHECK_MATCH ("w", symbol_name_match_type::WILD, true,
+		 EXPECT ("w1::w2"));
   }
 
   /* Same, with a "complicated" symbol.  */
@@ -4821,6 +4825,10 @@ test_dw2_expand_symtabs_matching_symbol ()
   {
     CHECK_MATCH ("std::zfunction(int)", symbol_name_match_type::FULL, true,
 		 EXPECT ("std::zfunction", "std::zfunction2"));
+    CHECK_MATCH ("zfunction(int)", symbol_name_match_type::WILD, true,
+		 EXPECT ("std::zfunction", "std::zfunction2"));
+    CHECK_MATCH ("zfunc", symbol_name_match_type::WILD, true,
+		 EXPECT ("std::zfunction", "std::zfunction2"));
   }
 
   /* Check that whitespace is ignored appropriately.  A symbol with a
@@ -4829,6 +4837,8 @@ test_dw2_expand_symtabs_matching_symbol ()
     static const char expected[] = "ns::foo<int>";
     CHECK_MATCH ("ns :: foo < int > ", symbol_name_match_type::FULL, false,
 		 EXPECT (expected));
+    CHECK_MATCH ("foo < int > ", symbol_name_match_type::WILD, false,
+		 EXPECT (expected));
   }
 
   /* Check that whitespace is ignored appropriately.  A symbol with a
@@ -4841,9 +4851,13 @@ test_dw2_expand_symtabs_matching_symbol ()
       {
 	CHECK_MATCH ("ns :: foo < char * >", symbol_name_match_type::FULL,
 		     completion_mode[i], EXPECT (expected));
+	CHECK_MATCH ("foo < char * >", symbol_name_match_type::WILD,
+		     completion_mode[i], EXPECT (expected));
 
 	CHECK_MATCH ("ns :: foo < char * > (int)", symbol_name_match_type::FULL,
 		     completion_mode[i], EXPECT (expected));
+	CHECK_MATCH ("foo < char * > (int)", symbol_name_match_type::WILD,
+		     completion_mode[i], EXPECT (expected));
       }
   }
 
@@ -4854,14 +4868,48 @@ test_dw2_expand_symtabs_matching_symbol ()
 		 symbol_name_match_type::FULL, true, EXPECT (expected));
     CHECK_MATCH ("ns :: foo < char * >  ( int ) &&",
 		 symbol_name_match_type::FULL, true, EXPECT (expected));
+    CHECK_MATCH ("foo < char * >  ( int ) const",
+		 symbol_name_match_type::WILD, true, EXPECT (expected));
+    CHECK_MATCH ("foo < char * >  ( int ) &&",
+		 symbol_name_match_type::WILD, true, EXPECT (expected));
   }
 
   /* Test lookup names that don't match anything.  */
   {
+    CHECK_MATCH ("bar2", symbol_name_match_type::WILD, false,
+		 {});
+
     CHECK_MATCH ("doesntexist", symbol_name_match_type::FULL, false,
 		 {});
   }
 
+  /* Some wild matching tests, exercising "(anonymous namespace)",
+     which should not be confused with a parameter list.  */
+  {
+    static const char *syms[] = {
+      "A::B::C",
+      "B::C",
+      "C",
+      "A :: B :: C ( int )",
+      "B :: C ( int )",
+      "C ( int )",
+    };
+
+    for (const char *s : syms)
+      {
+	CHECK_MATCH (s, symbol_name_match_type::WILD, false,
+		     EXPECT ("(anonymous namespace)::A::B::C"));
+      }
+  }
+
+  {
+    static const char expected[] = "ns2::tmpl<int>::foo2";
+    CHECK_MATCH ("tmp", symbol_name_match_type::WILD, true,
+		 EXPECT (expected));
+    CHECK_MATCH ("tmpl<", symbol_name_match_type::WILD, true,
+		 EXPECT (expected));
+  }
+
   SELF_CHECK (!any_mismatch);
 
 #undef EXPECT
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 3f7f171..c11ca1d 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -359,6 +359,7 @@ static VEC (symbolp) *find_label_symbols (struct linespec_state *self,
 static void find_linespec_symbols (struct linespec_state *self,
 				   VEC (symtab_ptr) *file_symtabs,
 				   const char *name,
+				   symbol_name_match_type name_match_type,
 				   VEC (symbolp) **symbols,
 				   VEC (bound_minimal_symbol_d) **minsyms);
 
@@ -1868,6 +1869,7 @@ linespec_parse_basic (linespec_parser *parser)
 
 	  linespec_complete_function (tmp_tracker,
 				      parser->completion_word,
+				      symbol_name_match_type::WILD,
 				      source_filename);
 
 	  if (tmp_tracker.have_completions ())
@@ -1892,6 +1894,7 @@ linespec_parse_basic (linespec_parser *parser)
   /* Try looking it up as a function/method.  */
   find_linespec_symbols (PARSER_STATE (parser),
 			 PARSER_RESULT (parser)->file_symtabs, name,
+			 symbol_name_match_type::WILD,
 			 &symbols, &minimal_symbols);
 
   if (symbols != NULL || minimal_symbols != NULL)
@@ -2383,6 +2386,7 @@ convert_explicit_location_to_linespec (struct linespec_state *self,
 				       linespec_p result,
 				       const char *source_filename,
 				       const char *function_name,
+				       symbol_name_match_type fname_match_type,
 				       const char *label_name,
 				       struct line_offset line_offset)
 {
@@ -2412,8 +2416,8 @@ convert_explicit_location_to_linespec (struct linespec_state *self,
   if (function_name != NULL)
     {
       find_linespec_symbols (self, result->file_symtabs,
-			     function_name, &symbols,
-			     &minimal_symbols);
+			     function_name, fname_match_type,
+			     &symbols, &minimal_symbols);
 
       if (symbols == NULL && minimal_symbols == NULL)
 	symbol_not_found_error (function_name,
@@ -2453,6 +2457,7 @@ convert_explicit_location_to_sals (struct linespec_state *self,
   convert_explicit_location_to_linespec (self, result,
 					 explicit_loc->source_filename,
 					 explicit_loc->function_name,
+					 explicit_loc->func_name_match_type,
 					 explicit_loc->label_name,
 					 explicit_loc->line_offset);
   return convert_linespec_to_sals (self, result);
@@ -2828,10 +2833,10 @@ linespec_lex_to_end (const char **stringp)
 void
 linespec_complete_function (completion_tracker &tracker,
 			    const char *function,
+			    symbol_name_match_type func_match_type,
 			    const char *source_filename)
 {
   complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
-  symbol_name_match_type func_match_type = symbol_name_match_type::WILD;
 
   if (source_filename != NULL)
     {
@@ -2870,7 +2875,8 @@ complete_linespec_component (linespec_parser *parser,
     {
       completion_list fn_list;
 
-      linespec_complete_function (tracker, text, source_filename);
+      linespec_complete_function (tracker, text, symbol_name_match_type::WILD,
+				  source_filename);
       if (source_filename == NULL)
 	{
 	  /* Haven't seen a source component, like in "b
@@ -2940,6 +2946,7 @@ linespec_complete_label (completion_tracker &tracker,
 			 const struct language_defn *language,
 			 const char *source_filename,
 			 const char *function_name,
+			 symbol_name_match_type func_name_match_type,
 			 const char *label_name)
 {
   linespec_parser parser;
@@ -2956,6 +2963,7 @@ linespec_complete_label (completion_tracker &tracker,
 					     PARSER_RESULT (&parser),
 					     source_filename,
 					     function_name,
+					     func_name_match_type,
 					     NULL, unknown_offset);
     }
   CATCH (ex, RETURN_MASK_ERROR)
@@ -3039,7 +3047,7 @@ linespec_complete (completion_tracker &tracker, const char *text)
       VEC (bound_minimal_symbol_d) *minimal_symbols;
       find_linespec_symbols (PARSER_STATE (&parser),
 			     PARSER_RESULT (&parser)->file_symtabs,
-			     func_name,
+			     func_name, symbol_name_match_type::WILD,
 			     &function_symbols, &minimal_symbols);
 
       PARSER_RESULT (&parser)->function_symbols = function_symbols;
@@ -3936,6 +3944,7 @@ symtabs_from_filename (const char *filename,
 static void
 find_function_symbols (struct linespec_state *state,
 		       VEC (symtab_ptr) *file_symtabs, const char *name,
+		       symbol_name_match_type name_match_type,
 		       VEC (symbolp) **symbols,
 		       VEC (bound_minimal_symbol_d) **minsyms)
 {
@@ -3955,8 +3964,7 @@ find_function_symbols (struct linespec_state *state,
     add_all_symbol_names_from_pspace (&info, state->search_pspace,
 				      symbol_names, FUNCTIONS_DOMAIN);
   else
-    add_matching_symbols_to_info (name, symbol_name_match_type::WILD,
-				  FUNCTIONS_DOMAIN,
+    add_matching_symbols_to_info (name, name_match_type, FUNCTIONS_DOMAIN,
 				  &info, state->search_pspace);
 
   do_cleanups (cleanup);
@@ -3985,6 +3993,7 @@ static void
 find_linespec_symbols (struct linespec_state *state,
 		       VEC (symtab_ptr) *file_symtabs,
 		       const char *lookup_name,
+		       symbol_name_match_type name_match_type,
 		       VEC (symbolp) **symbols,
 		       VEC (bound_minimal_symbol_d) **minsyms)
 {
@@ -4002,6 +4011,7 @@ find_linespec_symbols (struct linespec_state *state,
      2) break class::method where method is in class (and not a baseclass)  */
 
   find_function_symbols (state, file_symtabs, lookup_name,
+			 name_match_type,
 			 symbols, minsyms);
 
   /* If we were unable to locate a symbol of the same name, try dividing
diff --git a/gdb/linespec.h b/gdb/linespec.h
index b955728..7add83e 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -182,12 +182,14 @@ extern const char * const linespec_keywords[];
 extern void linespec_complete (completion_tracker &tracker,
 			       const char *text);
 
-/* Complete a function symbol, in linespec mode.  If SOURCE_FILENAME
-   is non-NULL, limits completion to the list of functions defined in
-   source files that match SOURCE_FILENAME.  */
+/* Complete a function symbol, in linespec mode, according to
+   FUNC_MATCH_TYPE.  If SOURCE_FILENAME is non-NULL, limits completion
+   to the list of functions defined in source files that match
+   SOURCE_FILENAME.  */
 
 extern void linespec_complete_function (completion_tracker &tracker,
 					const char *function,
+					symbol_name_match_type func_match_type,
 					const char *source_filename);
 
 /* Complete a label symbol, in linespec mode.  Only labels of
@@ -199,6 +201,7 @@ extern void linespec_complete_label (completion_tracker &tracker,
 				     const struct language_defn *language,
 				     const char *source_filename,
 				     const char *function_name,
+				     symbol_name_match_type name_match_type,
 				     const char *label_name);
 
 /* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
diff --git a/gdb/location.c b/gdb/location.c
index c78778e..e353bf7 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -245,7 +245,13 @@ explicit_to_string_internal (int as_linespec,
       if (need_space)
 	buf.putc (space);
       if (!as_linespec)
-	buf.puts ("-function ");
+	{
+	  if (explicit_loc->func_name_match_type
+	      == symbol_name_match_type::FULL)
+	    buf.puts ("-qualified ");
+	  else
+	    buf.puts ("-function ");
+	}
       buf.puts (explicit_loc->function_name);
       need_space = 1;
     }
@@ -775,6 +781,16 @@ string_to_explicit_location (const char **argp,
 	  set_oarg (explicit_location_lex_one_function (argp, language,
 							completion_info));
 	  EL_EXPLICIT (location)->function_name = oarg.release ();
+	  EL_EXPLICIT (location)->func_name_match_type
+	    = symbol_name_match_type::WILD;
+	}
+      else if (strncmp (opt.get (), "-qualified", len) == 0)
+	{
+	  set_oarg (explicit_location_lex_one_function (argp, language,
+							completion_info));
+	  EL_EXPLICIT (location)->function_name = oarg.release ();
+	  EL_EXPLICIT (location)->func_name_match_type
+	    = symbol_name_match_type::FULL;
 	}
       else if (strncmp (opt.get (), "-line", len) == 0)
 	{
diff --git a/gdb/location.h b/gdb/location.h
index d954eac..74fd868 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -79,6 +79,9 @@ struct explicit_location
   /* The function name.  Malloc'd.  */
   char *function_name;
 
+  /* Whether the function name is fully-qualified or not.  */
+  symbol_name_match_type func_name_match_type;
+
   /* The name of a label.  Malloc'd.  */
   char *label_name;
 
diff --git a/gdb/testsuite/gdb.base/langs.exp b/gdb/testsuite/gdb.base/langs.exp
index 8dcd5ee..03c690c 100644
--- a/gdb/testsuite/gdb.base/langs.exp
+++ b/gdb/testsuite/gdb.base/langs.exp
@@ -38,7 +38,7 @@ if [get_compiler_info] {
     return -1
 }
 
-gdb_test_multiple "b langs0" "break on nonexistent function in langs.exp" {
+gdb_test_multiple "b -qualified langs0" "break on nonexistent function in langs.exp" {
 	-re "Function \"langs0\" not defined\..*Make breakpoint pending on future shared library load.*y or .n.. $" {
 
 		gdb_test "n" "" "break on nonexistent function in langs.exp"
diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp
index 08f1464..50690ab 100644
--- a/gdb/testsuite/gdb.cp/meth-typedefs.exp
+++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp
@@ -145,15 +145,42 @@ foreach test $methods {
     set func [lindex $test 0]
     set result [lindex $test 1]
 
-    gdb_test "list $func" $result
-    gdb_test "list '$func'" $result
-    if {[gdb_breakpoint $func]} {
-      pass "break $func"
+    gdb_test "list -qualified $func" $result
+    gdb_test "list -qualified '$func'" $result
+    if {[gdb_breakpoint "-qualified $func"]} {
+      pass "break -qualified $func"
     }
-    if {[gdb_breakpoint '$func']} {
-      pass "break '$func'"
+    if {[gdb_breakpoint "-qualified '$func'"]} {
+      pass "break -qualified '$func'"
     }
 }
 
+# The tests above use -qualified to explicitly pick the one "test"
+# symbol each test cares about.  Now check that both "break test(..)"
+# and "list test(..)" without -qualified find "test(..)" in all the 3
+# scopes that have the this particular overload.
+set func "test(aenum, astruct const&, aunion const***)"
+set func_re "test\\(anon_enum, anon_struct const&, anon_union const\\*\\*\\*\\)"
+set line1 [gdb_get_line_number " A::FOO::$func"]
+set line2 [gdb_get_line_number " B::$func"]
+set line3 [gdb_get_line_number " $func"]
+
+foreach f [list "$func" "'$func'"] {
+    set any "\[^\r\n\]*"
+    gdb_test \
+	"list $f" \
+	[multi_line \
+	     "file: \".*$srcfile\", line number: $line1, symbol: \"A::foo::$func_re\"" \
+	     "$line1${any}A::FOO::test${any}" \
+	     "file: \".*$srcfile\", line number: $line2, symbol: \"B::$func_re\"" \
+	     "$line2${any}B::test${any}" \
+	     "file: \".*$srcfile\", line number: $line3, symbol: \"$func_re\"" \
+	     "$line3${any}// test${any}"] \
+	"list $f"
+
+    delete_breakpoints
+    gdb_test "break $f" "\\(3 locations\\)"
+}
+
 gdb_exit
 return 0
diff --git a/gdb/testsuite/gdb.cp/namespace.exp b/gdb/testsuite/gdb.cp/namespace.exp
index 640ee4f..4a6b863 100644
--- a/gdb/testsuite/gdb.cp/namespace.exp
+++ b/gdb/testsuite/gdb.cp/namespace.exp
@@ -120,7 +120,7 @@ gdb_test "break AAA::xyzq" \
 
 # Break on a function in the global namespace.
 
-gdb_test "break ::ensureOtherRefs" \
+gdb_test "break -qualified ::ensureOtherRefs" \
     "Breakpoint.*at $hex: file.*$srcfile2, line $decimal\\."
 
 # Call a function in a nested namespace
-- 
2.5.5

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

* Re: [PATCH 35/40] Comprehensive C++ linespec/completer tests
  2017-08-09 17:30   ` Keith Seitz
@ 2017-11-24 16:25     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-24 16:25 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/09/2017 06:30 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> Exercises all sorts of aspects fixed by the previous patches.
> 
> SUPER!
> 
>> Grows the gdb.linespec/ tests like this:
>>
>>   -# of expected passes           573
>>   +# of expected passes           4458
> 
> /me drools

:-)

> 
>> gdb/testsuite/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* gdb.linespec/cpcompletion.exp: New file.
>> 	* gdb.linespec/cpls-hyphen.cc: New file.
>> 	* gdb.linespec/cpls.cc: New file.
>> 	* gdb.linespec/cpls2.cc: New file.
>> 	* gdb.linespec/explicit.exp: Load completion-support.exp.  Adjust
>> 	test to use test_gdb_complete_unique.  Add label completion,
>> 	keyword completion and explicit location completion tests.
>> 	* lib/completion-support.exp: New file.
> 
>> diff --git a/gdb/testsuite/gdb.linespec/cpls-hyphen.cc b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
>> new file mode 100644
>> index 0000000..fdc063f
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
>> @@ -0,0 +1,14 @@
>> +int
>> +ns_hyphen_function (int i)
>> +{
>> +  if (i > 0)
>> +    {
>> +    label1:
>> +      return i + 20;
>> +    }
>> +  else
>> +    {
>> +    label2:
>> +      return i + 10;
>> +    }
>> +}
> 
> Does this file not require a copyright header?

It does, missed it.  Thanks.

> 
>> diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp
>> index 65d78ca..998b70a 100644
>> --- a/gdb/testsuite/gdb.linespec/explicit.exp
>> +++ b/gdb/testsuite/gdb.linespec/explicit.exp
>> @@ -326,10 +329,202 @@ namespace eval $testfile {
> [snip]
>>  
>> +	# Follows completion tests that require having no symbols
>> +	# loaded.
> 
> "The following completion tests," perhaps?

Adjusted.

> 
>> +	gdb_exit
>> +	gdb_start
>> +
>> +	# The match list you get when you complete with no options
>> +	# specified at all.
>> +	set completion_list {
>> +	    "-function"
>> +	    "-label"
>> +	    "-line"
>> +	    "-probe"
>> +	    "-probe-dtrace"
>> +	    "-probe-stap"
>> +	    "-qualified"
>> +	    "-source"
>> +	}
>> +	with_test_prefix "complete with no arguments and no symbols" {
>> +	    test_gdb_complete_multiple "b " "" "-" $completion_list
>> +	    test_gdb_complete_multiple "b " "-" "" $completion_list
>> +	}
>>      }
>>      # End of completion tests.
>>  
>> diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
>> new file mode 100644
>> index 0000000..ef78269
>> --- /dev/null
>> +++ b/gdb/testsuite/lib/completion-support.exp
>> @@ -0,0 +1,513 @@
> [snip]
>> +
>> +# Test that completing INPUT_LINE with TAB completes to
>> +# COMPLETE_LINE_RE.  APPEND_CHAR_RE is the character expected to be
>> +# appended after EXPECTED_OUTPUT.  Normally that's a whitespace, but
>> +# in some cases it's some other character, like a colon.
>> +
>> +proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re } {
>> +
>> +    set test "tab complete \"$input_line\""
>> +    send_gdb "$input_line\t"
>> +    gdb_test_multiple "" "$test" {
>> +	-re "^$complete_line_re$append_char_re$" {
>> +	    pass "$test"
>> +	}
>> +    }
>> +
>> +    clear_input_line $test
>> +}
>> +
>> +# Test that completing INPUT_LINE with TAB completes to "INPUT_LINE +
>> +# ADD_COMPLETED_LINE" and that is displays the completion matches in
>                                   ^^
> 
> s/is/it/

Fixed.

> 
>> +# COMPLETION_LIST.
>> +
> 
>> +proc test_gdb_complete_tab_multiple { input_line add_completed_line \
>> +					  completion_list } {
>> +    global gdb_prompt
>> +    global bell_re
> [snip]
>> +
>> +proc test_gdb_complete_menu { line expected_output } {
>> +
> 
> Is this used? Maybe in a subsequent patch?

Whoops, no it's isn't used, because I couldn't make it
work last time I tried, I don't recall exactly why.
This was meant to test the "ESC, ?" sequence, which triggers
a completion too.  I've removed this for now.

> 
>> +    set test "menu complete $line"
>> +#    send_gdb "$expr\033?"
>> +#    send_gdb "$expr^\[?"
>> +    send_gdb "$expr"
>> +    send_gdb "\x1b"
>> +    send_gdb "?"
>> +    gdb_test_multiple "" "$test" {
>> +	-re "$expected_output" {
>> +	    pass "$test"
>> +	}
>> +    }
>> +}
>> +
> [snip]
>> +
>> +proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {
> 
> This procedure is only called from this file. If it is meant to be called by
> test writers, it deserves a comment, even if trivial.
> 
> If it is only meant for use here, then it should be named differently to
> differentiate it, or stick it into an appropriately namespace so that it is
> more obvious to potential users of this code that it is not meant to be
> "exported." There's a lot of (sometimes mindless) cut-n-paste going on in the
> test suite.

I gave it a comment now.  Actually, test_gdb_complete_unique can just
be a wrapper around test_gdb_complete_unique_re; not sure why I didn't
do that the first time.  Probably just mindless copy/paste.

I like the idea of a namespace, so I created one ("completion") and
moved other helper procedures and all the related globals there.
I considered moving the public procedures into the namespace as well,
but it requires touching many many lines of the huge tests throughout, and
causes hard to resolve conflicts and it doesn't seem worth it to fight
against that.  I'll see if it makes sense to do that on top of the
series instead.

> 
>> +    set append_char_re [string_to_regexp $append_char]
>> +    test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re
>> +
>> +    # Trim INPUT_LINE and COMPLETE LINE, for the case we're completing
>> +    # a command with leading whitespace.  Leading command whitespace
>> +    # is discarded by GDB.
>> +    set input_line [string trimleft $input_line]
>> +    set expected_output_re [string trimleft $complete_line_re]
>> +    if {$append_char_re != " "} {
>> +	append expected_output_re $append_char_re
>> +    }
>> +    if {$max_completions} {
>> +	set max_completion_reached_msg \
>> +	    "*** List may be truncated, max-completions reached. ***"
>> +	set input_line_re \
>> +	    [string_to_regexp $input_line]
>> +	set max_completion_reached_msg_re \
>> +	    [string_to_regexp $max_completion_reached_msg]
>> +
>> +	append expected_output_re \
>> +	    "\r\n$input_line_re $max_completion_reached_msg_re"
>> +    }
>> +
>> +    test_gdb_complete_cmd_unique $input_line $expected_output_re
>> +}
> [snip]
>> +
>> +# Test completing all the substring prefixes of COMPLETION from
>> +# [0..START) to [0..END) complete to COMPLETION.  If END is ommitted,
>> +# default to the length of COMPLETION.
>> +
>> +proc test_complete_prefix_range {completion start {end -1}} {
>> +    if {$end == -1} {
>> +	set end [string length $completion]
>> +    }
>> +
>> +    for {set i $start} {$i < $end} {incr i} {
>> +	set line [string range $completion 0 $i]
>> +	test_gdb_complete_unique "$line" "$completion"
>> +    }
>> +}
>> +
>> +proc test_complete_prefix_range_input {input completion_re start {end -1}} {
> 
> This procedure also isn't used. Maybe also a later patch? Ditto the "if it
> is meant to be used, ..., or renamed/hidden."

Ah, yes, it's used in a later patch [1].  I've moved it to the right
patch now, and renamed 
test_complete_prefix_range_input->test_complete_prefix_range_re instead,
following the similar theme of other procedures.  I also reimplemented
test_complete_prefix_range on top of it.

[1] - [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests


> 
>> +    if {$end == -1} {
>> +	set end [string length $input]
>> +    }
>> +
>> +    for {set i $start} {$i < $end} {incr i} {
>> +	set line [string range $input 0 $i]
>> +	test_gdb_complete_unique_re "$line" $completion_re
>> +    }
>> +}
>> +
> [snip]
>> +# Return true if lists A and B have the same elements.  Order of
>> +# elements does not matter.
>> +
>> +proc gdb_leq {a b} {
>> +    return [expr {[lsort $a] eq [lsort $b]}]
>> +}
>> +
>> +# Check that creating the breakpoint at LINESPEC finds the same
>> +# breakpoint locations as completing LINESPEC.  COMPLETION_LIST is
>> +# expected completion match list.
> 
> You mention "LINESPEC" here, but this procedure actually takes a breakpoint
> command and a linepsec (or location?), no?

Right.  I think I first accepted a LINESPEC, then later made it work
with explicit locations too, and missed updating the comment.

> 
>> +
>> +proc check_bp_locations_match_list {break_command completion_list} {
>> +    global gdb_prompt
>> +    global hex
>> +
>> +    with_test_prefix "compare \"$break_command\" completion list with bp location list" {
>> +	set num_locations [create_bp $break_command]
>> +
>> +	set found_list ""
>> +
>> +	set any "\[^\r\n\]*"
>> +
>> +	gdb_test_multiple "info breakpoint \$bpnum" "info breakpoint" {
>> +	    -re "in \(\[^\r\n\]*\) at " {
>> +		# A function location.
>> +		set found_location "$expect_out(1,string)"
>> +		lappend found_list $found_location
>> +		exp_continue
>> +	    }
>> +	    -re "breakpoint${any}keep${any}y${any}$hex\[ \t]*\(${any}\)\r\n" {
>> +		# A label location.
>> +		set found_location "$expect_out(1,string)"
>> +		lappend found_list $found_location
>> +		exp_continue
>> +	    }
>> +	    -re "$gdb_prompt $" {
>> +	    }
>> +	}
>> +
>> +	gdb_assert {[gdb_leq $found_list $completion_list]} "matches"
>> +
>> +	delete_breakpoints
>> +    }
>> +}
>> +

Here's what I'm squashing in.

diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
index dd9bf70..5522b19 100644
--- a/gdb/testsuite/gdb.linespec/cpcompletion.exp
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -445,10 +445,8 @@ proc_with_prefix const-overload-quoted {} {
 # appends the end quote char automatically, both ' and ".
 
 proc_with_prefix append-end-quote-char-when-unambiguous {} {
-    global all_quotes_list
-
     foreach cmd_prefix {"b" "b -function"} {
-	foreach qc $all_quotes_list {
+	foreach qc $completion::all_quotes_list {
 	    set linespec "${qc}not_overloaded_fn()${qc}"
 	    foreach cmd [list "$cmd_prefix ${qc}not_overloaded_fn()" \
 			      "$cmd_prefix ${qc}not_overloaded_fn" \
@@ -480,13 +478,12 @@ proc_with_prefix in-source-file-unconstrained {} {
 # name.
 
 proc_with_prefix in-source-file-unambiguous {} {
-    global maybe_quoted_list
-
-    foreach sqc $maybe_quoted_list {
-	foreach fqc $maybe_quoted_list {
+    foreach sqc $completion::maybe_quoted_list {
+	foreach fqc $completion::maybe_quoted_list {
 	    # Linespec.
 	    foreach sep {":" ": "} {
-		set linespec "${sqc}cpls2.cc${sqc}${sep}${fqc}file_constrained_test_cpls2_function(int)${fqc}"
+		set linespec \
+		    "${sqc}cpls2.cc${sqc}${sep}${fqc}file_constrained_test_cpls2_function(int)${fqc}"
 		set complete_line "b $linespec"
 		set start [index_after "constrained_test" $complete_line]
 		set input_line [string range $complete_line 0 $start]
@@ -515,10 +512,8 @@ proc_with_prefix in-source-file-unambiguous {} {
 # function name.
 
 proc_with_prefix in-source-file-ambiguous {} {
-    global maybe_quoted_list
-
-    foreach sqc $maybe_quoted_list {
-	foreach fqc $maybe_quoted_list {
+    foreach sqc $completion::maybe_quoted_list {
+	foreach fqc $completion::maybe_quoted_list {
 	    # Linespec.
 	    foreach sep {":" ": "} {
 		set cmd_prefix "b ${sqc}cpls2.cc${sqc}${sep}"
@@ -542,13 +537,9 @@ proc_with_prefix in-source-file-ambiguous {} {
 # instead of a whitespace character.
 
 proc_with_prefix source-complete-appends-colon {} {
-    global maybe_quoted_list
-    global all_quotes_list
-    global keyword_list
-
     # Test with quotes to make sure the end quote char is put at the
     # right place.
-    foreach qc $maybe_quoted_list {
+    foreach qc $completion::maybe_quoted_list {
 	test_gdb_complete_unique \
 	    "b ${qc}cpls2." \
 	    "b ${qc}cpls2.cc${qc}" ":"
@@ -574,8 +565,9 @@ proc_with_prefix source-complete-appends-colon {} {
     # Cursor at the end of the string.
     test_gdb_complete_none "b nonexistingfilename.cc"
     # Cursor past the end of the string.
-    test_gdb_complete_multiple "b nonexistingfilename.cc " "" "" $keyword_list
-    foreach qc $all_quotes_list {
+    test_gdb_complete_multiple "b nonexistingfilename.cc " "" "" \
+	$completion::keyword_list
+    foreach qc $completion::all_quotes_list {
 	# Unterminated quote.
 	test_gdb_complete_none "b ${qc}nonexistingfilename.cc"
 	test_gdb_complete_none "b ${qc}nonexistingfilename.cc "
@@ -585,7 +577,8 @@ proc_with_prefix source-complete-appends-colon {} {
 	    "b ${qc}nonexistingfilename.cc${qc}"
 	# Terminated quote, cursor past the quote.
 	test_gdb_complete_multiple \
-	    "b ${qc}nonexistingfilename.cc${qc} " "" "" $keyword_list
+	    "b ${qc}nonexistingfilename.cc${qc} " "" "" \
+	    $completion::keyword_list
     }
 }
 
@@ -617,18 +610,16 @@ proc_with_prefix incomplete-scope-colon {} {
     # finds the same breakpoint location as completion does.
     #
     proc incomplete_scope_colon_helper {prototype range_ss {skip_check_bp 0}} {
-	global maybe_quoted_list
-
 	foreach source {"" "cpls.cc"} {
 	    # Test with and without source quoting.
-	    foreach sqc $maybe_quoted_list {
+	    foreach sqc $completion::maybe_quoted_list {
 		if {$source == "" && $sqc != ""} {
 		    # Invalid combination.
 		    continue
 		}
 
 		# Test with and without function quoting.
-		foreach fqc $maybe_quoted_list {
+		foreach fqc $completion::maybe_quoted_list {
 		    if {$source == ""} {
 			set linespec_source ""
 			set explicit_source ""
@@ -676,10 +667,8 @@ proc_with_prefix incomplete-scope-colon {} {
 # Test completing functions/methods in anonymous namespaces.
 
 proc_with_prefix anon-ns {} {
-    global maybe_quoted_list
-
     foreach cmd_prefix {"b" "b -function"} {
-	foreach qc $maybe_quoted_list {
+	foreach qc $completion::maybe_quoted_list {
 	    test_gdb_complete_unique \
 		"$cmd_prefix ${qc}anon_ns_function" \
 		"$cmd_prefix ${qc}anon_ns_function()${qc}"
@@ -833,9 +822,8 @@ proc_with_prefix function-labels {} {
 # location options in explicit locations mode.
 
 proc_with_prefix keywords-after-function {} {
-    global explicit_opts_list keyword_list
-
-    set explicit_list [concat $explicit_opts_list $keyword_list]
+    set explicit_list \
+	[concat $completion::explicit_opts_list $completion::keyword_list]
 
     # Test without a source file, with a known source file, and with
     # and unknown source file.
@@ -845,20 +833,21 @@ proc_with_prefix keywords-after-function {} {
 	{ "function_with_labels(int)" "unknown_function(int)" } \
 	{
 	    # Linespec version.
-	    test_gdb_complete_multiple "b ${location} " "" "" $keyword_list
+	    test_gdb_complete_multiple "b ${location} " "" "" \
+		$completion::keyword_list
 	} \
 	{
 	    # Explicit locations version.
-	    test_gdb_complete_multiple "b ${location} " "" "" $explicit_list
+	    test_gdb_complete_multiple "b ${location} " "" "" \
+		$explicit_list
 	}
 }
 
 # Same, but after a label.
 
 proc_with_prefix keywords-after-label {} {
-    global explicit_opts_list keyword_list
-
-    set explicit_list [concat $explicit_opts_list $keyword_list]
+    set explicit_list \
+	[concat $completion::explicit_opts_list $completion::keyword_list]
 
     foreach_location_labels \
 	{ "" "cpls.cc" } \
@@ -866,24 +855,23 @@ proc_with_prefix keywords-after-label {} {
 	{ "label1" "non_existing_label" } \
 	{
 	    # Linespec version.
-	    test_gdb_complete_multiple "b ${location} " "" "" $keyword_list
+	    test_gdb_complete_multiple "b ${location} " "" "" \
+		$completion::keyword_list
 	} \
 	{
 	    # Explicit locations version.
-	    test_gdb_complete_multiple "b ${location} " "" "" $explicit_list
+	    test_gdb_complete_multiple "b ${location} " "" "" \
+		$explicit_list
 	}
 }
 
 # Similar, but after an unknown file, and in linespec mode only.
 
 proc_with_prefix keywords-after-unknown-file {} {
-    global maybe_quoted_list
-    global keyword_list
-
     # Test with and without quoting.
-    foreach qc $maybe_quoted_list {
+    foreach qc $completion::maybe_quoted_list {
 	set line "b ${qc}unknown_file.cc${qc}: "
-	test_gdb_complete_multiple $line "" "" $keyword_list
+	test_gdb_complete_multiple $line "" "" $completion::keyword_list
     }
 }
 
diff --git a/gdb/testsuite/gdb.linespec/cpls-hyphen.cc b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
index fdc063f..4bd2d9f 100644
--- a/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
+++ b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc
@@ -1,3 +1,22 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 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/>.  */
+
+/* A function in a file whose name has an hyphen.  */
+
 int
 ns_hyphen_function (int i)
 {
diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp
index 6936971..1e7f0dc 100644
--- a/gdb/testsuite/gdb.linespec/explicit.exp
+++ b/gdb/testsuite/gdb.linespec/explicit.exp
@@ -225,7 +225,7 @@ namespace eval $testfile {
 	}
 
 	with_test_prefix "complete unique file name" {
-	    foreach qc $maybe_quoted_list {
+	    foreach qc $completion::maybe_quoted_list {
 		set cmd "break -source ${qc}3explicit.c${qc}"
 		test_gdb_complete_unique \
 		    "break -source ${qc}3ex" \
@@ -332,7 +332,7 @@ namespace eval $testfile {
 	global maybe_quoted_list
 
 	with_test_prefix "complete unique label name" {
-	    foreach qc $maybe_quoted_list {
+	    foreach qc $completion::maybe_quoted_list {
 		test_gdb_complete_unique \
 		    "break -function myfunction -label ${qc}to" \
 		    "break -function myfunction -label ${qc}top${qc}"
@@ -521,7 +521,7 @@ namespace eval $testfile {
 	# such as "-func main -sour 3ex\t" (main is defined in explicit.c).
 	# The completer cannot handle these yet.
 
-	# Follows completion tests that require having no symbols
+	# The following completion tests require having no symbols
 	# loaded.
 	gdb_exit
 	gdb_start
diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
index ef78269..3d03943 100644
--- a/gdb/testsuite/lib/completion-support.exp
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -15,18 +15,23 @@
 
 # This file is part of the gdb testsuite.
 
-set timeout 5
+# Any variable or procedure in the namespace whose name starts with
+# "_" is private to the module.  Do not use these.
 
-set bell_re "\\\x07"
+namespace eval completion {
+    variable bell_re "\\\x07"
 
-# List of all quote chars.
-set all_quotes_list {"'" "\""}
+    # List of all quote chars.
+    variable all_quotes_list {"'" "\""}
 
-# List of all quote chars, including no-quote at all.
-set maybe_quoted_list {"" "'" "\""}
+    # List of all quote chars, including no-quote at all.
+    variable maybe_quoted_list {"" "'" "\""}
 
-set keyword_list {"if" "task" "thread"}
-set explicit_opts_list {"-function" "-label" "-line" "-qualified" "-source"}
+    variable keyword_list {"if" "task" "thread"}
+
+    variable explicit_opts_list \
+	{"-function" "-label" "-line" "-qualified" "-source"}
+}
 
 # Make a regular expression that matches a TAB completion list.
 
@@ -81,14 +86,12 @@ proc clear_input_line { test } {
 # Test that completing LINE with TAB completes to nothing.
 
 proc test_gdb_complete_tab_none { line } {
-    global bell_re
-
     set line_re [string_to_regexp $line]
 
     set test "tab complete \"$line\""
     send_gdb "$line\t"
     gdb_test_multiple "" "$test" {
-	-re "^$line_re$bell_re$" {
+	-re "^$line_re$completion::bell_re$" {
 	    pass "$test"
 	}
     }
@@ -115,13 +118,12 @@ proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re }
 }
 
 # Test that completing INPUT_LINE with TAB completes to "INPUT_LINE +
-# ADD_COMPLETED_LINE" and that is displays the completion matches in
+# ADD_COMPLETED_LINE" and that it displays the completion matches in
 # COMPLETION_LIST.
 
 proc test_gdb_complete_tab_multiple { input_line add_completed_line \
 					  completion_list } {
     global gdb_prompt
-    global bell_re
 
     set input_line_re [string_to_regexp $input_line]
     set add_completed_line_re [string_to_regexp $add_completed_line]
@@ -131,7 +133,7 @@ proc test_gdb_complete_tab_multiple { input_line add_completed_line \
     set test "tab complete \"$input_line\""
     send_gdb "$input_line\t"
     gdb_test_multiple "" "$test (first tab)" {
-	-re "^${input_line_re}$bell_re$add_completed_line_re$" {
+	-re "^${input_line_re}${completion::bell_re}$add_completed_line_re$" {
 	    send_gdb "\t"
 	    # If we auto-completed to an ambiguous prefix, we need an
 	    # extra tab to show the matches list.
@@ -189,21 +191,6 @@ proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list
     }
 }
 
-proc test_gdb_complete_menu { line expected_output } {
-
-    set test "menu complete $line"
-#    send_gdb "$expr\033?"
-#    send_gdb "$expr^\[?"
-    send_gdb "$expr"
-    send_gdb "\x1b"
-    send_gdb "?"
-    gdb_test_multiple "" "$test" {
-	-re "$expected_output" {
-	    pass "$test"
-	}
-    }
-}
-
 # Test that completing LINE completes to nothing.
 
 proc test_gdb_complete_none { input_line } {
@@ -211,7 +198,7 @@ proc test_gdb_complete_none { input_line } {
     test_gdb_complete_cmd_none $input_line
 }
 
-# Test that completing INPUT_LINE completes to COMPLETE_LINE.
+# Test that completing INPUT_LINE completes to COMPLETE_LINE_RE.
 #
 # APPEND_CHAR is the character expected to be appended after
 # EXPECTED_OUTPUT when TAB completing.  Normally that's a whitespace,
@@ -222,9 +209,12 @@ proc test_gdb_complete_none { input_line } {
 # match, this will only be visible in the "complete" command output.
 # Tab completion will just auto-complete the only match and won't
 # display a match list.
+#
+# Note: usually it's more convenient to pass a literal string instead
+# of a regular expression (as COMPLETE_LINE_RE).  See
+# test_gdb_complete_unique below.
 
-proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_completions 0}} {
-    set complete_line_re [string_to_regexp $complete_line]
+proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {
     set append_char_re [string_to_regexp $append_char]
     test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re
 
@@ -251,31 +241,12 @@ proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_
     test_gdb_complete_cmd_unique $input_line $expected_output_re
 }
 
-proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {
-    set append_char_re [string_to_regexp $append_char]
-    test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re
+# Like TEST_GDB_COMPLETE_UNIQUE_RE, but COMPLETE_LINE is a string, not
+# a regular expression.
 
-    # Trim INPUT_LINE and COMPLETE LINE, for the case we're completing
-    # a command with leading whitespace.  Leading command whitespace
-    # is discarded by GDB.
-    set input_line [string trimleft $input_line]
-    set expected_output_re [string trimleft $complete_line_re]
-    if {$append_char_re != " "} {
-	append expected_output_re $append_char_re
-    }
-    if {$max_completions} {
-	set max_completion_reached_msg \
-	    "*** List may be truncated, max-completions reached. ***"
-	set input_line_re \
-	    [string_to_regexp $input_line]
-	set max_completion_reached_msg_re \
-	    [string_to_regexp $max_completion_reached_msg]
-
-	append expected_output_re \
-	    "\r\n$input_line_re $max_completion_reached_msg_re"
-    }
-
-    test_gdb_complete_cmd_unique $input_line $expected_output_re
+proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_completions 0}} {
+    set complete_line_re [string_to_regexp $complete_line]
+    test_gdb_complete_unique_re $input_line $complete_line_re $append_char $max_completions
 }
 
 # Test that completing "CMD_PREFIX + COMPLETION_WORD" adds
@@ -288,8 +259,8 @@ proc test_gdb_complete_multiple { cmd_prefix completion_word add_completed_line
     test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char
 }
 
-# Test completing all the substring prefixes of COMPLETION from
-# [0..START) to [0..END) complete to COMPLETION.  If END is ommitted,
+# Test that all the substring prefixes of COMPLETION from
+# [0..START) to [0..END) complete to COMPLETION.  If END is ommitted,
 # default to the length of COMPLETION.
 
 proc test_complete_prefix_range {completion start {end -1}} {
@@ -303,17 +274,6 @@ proc test_complete_prefix_range {completion start {end -1}} {
     }
 }
 
-proc test_complete_prefix_range_input {input completion_re start {end -1}} {
-    if {$end == -1} {
-	set end [string length $input]
-    }
-
-    for {set i $start} {$i < $end} {incr i} {
-	set line [string range $input 0 $i]
-	test_gdb_complete_unique_re "$line" $completion_re
-    }
-}
-
 # Find NEEDLE in HAYSTACK and return the index _after_ NEEDLE.  E.g.,
 # searching for "(" in "foo(int)" returns 4, which would be useful if
 # you want to find the "(" to try completing "foo(".
@@ -326,10 +286,10 @@ proc index_after {needle haystack} {
     return [expr $start + [string length $needle]]
 }
 
-# Create a breakpoint using BREAK_COMMAND, and return the number of
-# locations found.
+# Create a breakpoint using BREAK_COMMAND, and return the number
+# of locations found.
 
-proc create_bp {break_command} {
+proc completion::_create_bp {break_command} {
     global gdb_prompt
     global decimal hex
 
@@ -361,11 +321,18 @@ proc create_bp {break_command} {
     return $found_locations
 }
 
-# Check that trying to create a breakpoint from linespec fails.
+# Return true if lists A and B have the same elements.  Order of
+# elements does not matter.
+
+proc completion::_leq {a b} {
+    return [expr {[lsort $a] eq [lsort $b]}]
+}
+
+# Check that trying to create a breakpoint using BREAK_COMMAND fails.
 
 proc check_setting_bp_fails {break_command} {
     with_test_prefix "\"$break_command\" creates no bp locations" {
-	set found_locations [create_bp $break_command]
+	set found_locations [completion::_create_bp $break_command]
 	gdb_assert {$found_locations == 0} "matches"
 	if {$found_locations != 0} {
 	    delete_breakpoints
@@ -373,23 +340,16 @@ proc check_setting_bp_fails {break_command} {
     }
 }
 
-# Return true if lists A and B have the same elements.  Order of
-# elements does not matter.
-
-proc gdb_leq {a b} {
-    return [expr {[lsort $a] eq [lsort $b]}]
-}
-
-# Check that creating the breakpoint at LINESPEC finds the same
-# breakpoint locations as completing LINESPEC.  COMPLETION_LIST is
-# expected completion match list.
+# Check that creating the breakpoint using BREAK_COMMAND finds the
+# same breakpoint locations as completing BREAK_COMMAND.
+# COMPLETION_LIST is the expected completion match list.
 
 proc check_bp_locations_match_list {break_command completion_list} {
     global gdb_prompt
     global hex
 
     with_test_prefix "compare \"$break_command\" completion list with bp location list" {
-	set num_locations [create_bp $break_command]
+	set num_locations [completion::_create_bp $break_command]
 
 	set found_list ""
 
@@ -412,7 +372,7 @@ proc check_bp_locations_match_list {break_command completion_list} {
 	    }
 	}
 
-	gdb_assert {[gdb_leq $found_list $completion_list]} "matches"
+	gdb_assert {[completion::_leq $found_list $completion_list]} "matches"
 
 	delete_breakpoints
     }
@@ -426,7 +386,6 @@ proc check_bp_locations_match_list {break_command completion_list} {
 # currently iterated location.
 
 proc foreach_location_functions { sources functions body_linespec body_explicit } {
-    global maybe_quoted_list
     upvar source source
     upvar function function
     upvar source_sep source_sep
@@ -434,14 +393,14 @@ proc foreach_location_functions { sources functions body_linespec body_explicit
 
     foreach source $sources {
 	# Test with and without source quoting.
-	foreach sqc $maybe_quoted_list {
+	foreach sqc $completion::maybe_quoted_list {
 	    if {$source == "" && $sqc != ""} {
 		# Invalid combination.
 		continue
 	    }
 
 	    # Test with and without function quoting.
-	    foreach fqc $maybe_quoted_list {
+	    foreach fqc $completion::maybe_quoted_list {
 		# Test known and unknown functions.
 		foreach function $functions {
 		    # Linespec version.  Test with and without spacing
@@ -477,7 +436,6 @@ proc foreach_location_functions { sources functions body_linespec body_explicit
 # Same as foreach_locations_functions, but also iterate over
 # combinations of labels.
 proc foreach_location_labels { sources functions labels body_linespec body_explicit } {
-    global maybe_quoted_list
     upvar source source
     upvar function function
     upvar label label

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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-11-22 16:48     ` Pedro Alves
@ 2017-11-24 16:48       ` Pedro Alves
  2017-11-24 16:57         ` Pedro Alves
  2017-11-28  0:39         ` Keith Seitz
  2017-11-28  0:02       ` Keith Seitz
  1 sibling, 2 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-24 16:48 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

Hey Keith,

So I took your suggestion and ran with it.  And in the end
I like it!  See more below.

On 11/22/2017 04:48 PM, Pedro Alves wrote:

> 
> On 08/09/2017 12:48 AM, Keith Seitz wrote:
>> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>

>>>  /* This help string is used for the break, hbreak, tbreak and thbreak
>>>     commands.  It is defined as a macro to prevent duplication.
>>> diff --git a/gdb/completer.c b/gdb/completer.c
>>> index eabbce7..99e40a3 100644
>>> --- a/gdb/completer.c
>>> +++ b/gdb/completer.c
>>> @@ -609,6 +612,7 @@ static const char *const explicit_options[] =
>>>    {
>>>      "-source",
>>>      "-function",
>>> +    "-qualified",
>>>      "-line",
>>>      "-label",
>>>      NULL
>>
>> The "-qualified" option can be used with linespecs, too, right?
> 
> Not really, no.
> 
>>
>> (gdb) b -qualified A::b   (a linespec location)
>> (gdb) b -qualified -function A::b  (an explicit location)
>>
>> Actually, I see that it does not work (yet?). Consider:
>>
>>      1	struct A
>>      2	{
>>      3	  int doit ()
>>      4	  {
>>      5	    int i;
>>      6	
>>      7	    for (i = 0; i < 10; ++i)
>>      8	      {
>>      9	        switch (i)
>>     10		  {
>>     11		  top: 
>>     12	          case 5:
>>     13	            ++i;
>>     14	            goto top;
>>     15	          default:  break;
>>     16		  }
>>     17	      }
>>     18	    return i;
>>     19	  }
>>     20	};
>>
>> (gdb) b A::doit:top
>> Breakpoint 1 at 0x400633: file simple-label.cc, line 11.
>> (gdb) b -function A::doit -label top
>> Note: breakpoint 1 also set at pc 0x400633.
>> Breakpoint 2 at 0x400633: file simple-label.cc, line 11.
>> (gdb) b -qualified A::doit:top
>> Function "A::doit:top" not defined.
>> Make breakpoint pending on future shared library load? (y or [n]) n
>> (gdb) b -qualified A::doit -label top
>> Note: breakpoints 1 and 2 also set at pc 0x400633.
>> Breakpoint 3 at 0x400633: file simple-label.cc, line 11.
>>
>> Is there a reason to exclude linespecs? Perhaps naively, I would have thought to
>> make -qualified a parsing option instead of a replacement for -function.
> 
> I went this route because it seemed to me that a separate
> option would end up being less convenient, because it forces you
> to type/combine two options:
> 
>  (gdb) b -q -f A::doit:top
> 

Sorry, here I was really trying to talk about:

  (gdb) b -q -f A::doit -label top

vs:

  (gdb) b -q A::doit -label top

But ...

> and I thought that since you're requiring an option, and
> explicit linespecs are just superior to regular linespecs, that
> it'd be OK to move users to explicit linespecs if they need
> a qualified name.
> 

> Dunno.


... well, I'll be d*mn+d...  I gave your suggestion a try, and
I actually like it!  If we make "-qualified" a flag instead of an
option with an argument, then usual case of:

  (gdb) b -q A::doit

works the exact same.  It's only when you specify source files
and labels that it makes a difference.  So with your suggestion,
we get a lot of benefit (works with linespecs, which is the common
case, I guess) with only a mild downside (a little more typing in
the explicit location case).

Here's what the delta patch looks like.  I left "-qualified"
as an explicit location option, rather than splitting the parsing
of "qualified" and the real explicit options, because this way
support for "-qualified" in the middle of other options
"-source filename.cc -qualified -function func" falls out
naturally.

Surprisingly, I didn't have to change much in the tests.  But I did
extend them to cover these new possibilities.  I'll show the tests
delta as a follow up.

From b84d26c68ced2a6544660e1f3cf5584c9e1dd4ef Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 24 Nov 2017 15:28:43 +0000
Subject: [PATCH] alternative

---
 gdb/ax-gdb.c               |  3 +-
 gdb/breakpoint.c           | 23 +++++++------
 gdb/completer.c            | 39 +++++++++++++++++----
 gdb/guile/scm-breakpoint.c |  6 ++--
 gdb/linespec.c             | 31 +++++++++++------
 gdb/linespec.h             |  3 +-
 gdb/location.c             | 86 ++++++++++++++++++++++++++++++----------------
 gdb/location.h             | 35 ++++++++++++++-----
 gdb/mi/mi-cmd-break.c      |  3 +-
 gdb/python/py-breakpoint.c |  3 +-
 gdb/python/python.c        |  3 +-
 11 files changed, 163 insertions(+), 72 deletions(-)

diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c
index 52ca081..5027f6a 100644
--- a/gdb/ax-gdb.c
+++ b/gdb/ax-gdb.c
@@ -2638,7 +2638,8 @@ agent_command_1 (const char *exp, int eval)
 
       exp = skip_spaces (exp);
 
-      event_location_up location = new_linespec_location (&exp);
+      event_location_up location
+	= new_linespec_location (&exp, symbol_name_match_type::WILD);
       decode_line_full (location.get (), DECODE_LINE_FUNFIRSTLINE, NULL,
 			(struct symtab *) NULL, 0, &canonical,
 			NULL, NULL);
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 88cb505..c7bb77d 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -9100,9 +9100,9 @@ parse_breakpoint_sals (const struct event_location *location,
 
   if (event_location_type (location) == LINESPEC_LOCATION)
     {
-      const char *address = get_linespec_location (location);
+      const char *spec = get_linespec_location (location)->spec_string;
 
-      if (address == NULL)
+      if (spec == NULL)
 	{
 	  /* The last displayed codepoint, if it's valid, is our default
 	     breakpoint address.  */
@@ -9148,15 +9148,15 @@ parse_breakpoint_sals (const struct event_location *location,
   cursal = get_current_source_symtab_and_line ();
   if (last_displayed_sal_is_valid ())
     {
-      const char *address = NULL;
+      const char *spec = NULL;
 
       if (event_location_type (location) == LINESPEC_LOCATION)
-	address = get_linespec_location (location);
+	spec = get_linespec_location (location)->spec_string;
 
       if (!cursal.symtab
-	  || (address != NULL
-	      && strchr ("+-", address[0]) != NULL
-	      && address[1] != '['))
+	  || (spec != NULL
+	      && strchr ("+-", spec[0]) != NULL
+	      && spec[1] != '['))
 	{
 	  decode_line_full (location, DECODE_LINE_FUNFIRSTLINE, NULL,
 			    get_last_displayed_symtab (),
@@ -13147,12 +13147,13 @@ strace_marker_create_sals_from_location (const struct event_location *location,
   struct linespec_sals lsal;
   const char *arg_start, *arg;
 
-  arg = arg_start = get_linespec_location (location);
+  arg = arg_start = get_linespec_location (location)->spec_string;
   lsal.sals = decode_static_tracepoint_spec (&arg);
 
   std::string str (arg_start, arg - arg_start);
   const char *ptr = str.c_str ();
-  canonical->location = new_linespec_location (&ptr);
+  canonical->location
+    = new_linespec_location (&ptr, symbol_name_match_type::FULL);
 
   lsal.canonical
     = xstrdup (event_location_to_string (canonical->location.get ()));
@@ -13213,7 +13214,7 @@ strace_marker_decode_location (struct breakpoint *b,
 			       struct program_space *search_pspace)
 {
   struct tracepoint *tp = (struct tracepoint *) b;
-  const char *s = get_linespec_location (location);
+  const char *s = get_linespec_location (location)->spec_string;
 
   std::vector<symtab_and_line> sals = decode_static_tracepoint_spec (&s);
   if (sals.size () > tp->static_trace_marker_id_idx)
@@ -14759,7 +14760,7 @@ strace_command (const char *arg, int from_tty)
   if (arg && startswith (arg, "-m") && isspace (arg[2]))
     {
       ops = &strace_marker_breakpoint_ops;
-      location = new_linespec_location (&arg);
+      location = new_linespec_location (&arg, symbol_name_match_type::FULL);
     }
   else
     {
diff --git a/gdb/completer.c b/gdb/completer.c
index aa0f105..d0224aa 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -582,7 +582,8 @@ complete_source_filenames (const char *text)
 
 static void
 complete_address_and_linespec_locations (completion_tracker &tracker,
-					 const char *text)
+					 const char *text,
+					 symbol_name_match_type match_type)
 {
   if (*text == '*')
     {
@@ -594,7 +595,7 @@ complete_address_and_linespec_locations (completion_tracker &tracker,
     }
   else
     {
-      linespec_complete (tracker, text);
+      linespec_complete (tracker, text, match_type);
     }
 }
 
@@ -642,6 +643,9 @@ collect_explicit_location_matches (completion_tracker &tracker,
   const struct explicit_location *explicit_loc
     = get_explicit_location (location);
 
+  /* True if the option expects an argument.  */
+  bool needs_arg = true;
+
   /* Note, in the various MATCH_* below, we complete on
      explicit_loc->foo instead of WORD, because only the former will
      have already skipped past any quote char.  */
@@ -657,7 +661,6 @@ collect_explicit_location_matches (completion_tracker &tracker,
       break;
 
     case MATCH_FUNCTION:
-    case MATCH_QUALIFIED:
       {
 	const char *function = string_or_empty (explicit_loc->function_name);
 	linespec_complete_function (tracker, function,
@@ -666,6 +669,9 @@ collect_explicit_location_matches (completion_tracker &tracker,
       }
       break;
 
+    case MATCH_QUALIFIED:
+      needs_arg = false;
+      break;
     case MATCH_LINE:
       /* Nothing to offer.  */
       break;
@@ -685,7 +691,7 @@ collect_explicit_location_matches (completion_tracker &tracker,
       gdb_assert_not_reached ("unhandled explicit_location_match_type");
     }
 
-  if (tracker.completes_to_completion_word (word))
+  if (!needs_arg || tracker.completes_to_completion_word (word))
     {
       tracker.discard_completions ();
       tracker.advance_custom_word_point_by (strlen (word));
@@ -874,7 +880,7 @@ location_completer (struct cmd_list_element *ignore,
       tracker.advance_custom_word_point_by (1);
     }
 
-  if (location != NULL)
+  if (completion_info.saw_explicit_location_option)
     {
       if (*copy != '\0')
 	{
@@ -914,10 +920,29 @@ location_completer (struct cmd_list_element *ignore,
 
 	}
     }
+  /* This is an address or linespec location.  */
+  else if (location != NULL)
+    {
+      /* Handle non-explicit location options.  */
+
+      int keyword = skip_keyword (tracker, explicit_options, &text);
+      if (keyword == -1)
+	complete_on_enum (tracker, explicit_options, text, text);
+      else
+	{
+	  tracker.advance_custom_word_point_by (copy - text);
+	  text = copy;
+
+	  symbol_name_match_type match_type
+	    = get_explicit_location (location.get ())->func_name_match_type;
+	  complete_address_and_linespec_locations (tracker, text, match_type);
+	}
+    }
   else
     {
-      /* This is an address or linespec location.  */
-      complete_address_and_linespec_locations (tracker, text);
+      /* No options.  */
+      complete_address_and_linespec_locations (tracker, text,
+					       symbol_name_match_type::WILD);
     }
 
   /* Add matches for option names, if either:
diff --git a/gdb/guile/scm-breakpoint.c b/gdb/guile/scm-breakpoint.c
index ec75be5..f84815e 100644
--- a/gdb/guile/scm-breakpoint.c
+++ b/gdb/guile/scm-breakpoint.c
@@ -424,8 +424,10 @@ gdbscm_register_breakpoint_x (SCM self)
   pending_breakpoint_scm = self;
   location = bp_smob->spec.location;
   copy = skip_spaces (location);
-  event_location_up eloc = string_to_event_location_basic (&copy,
-							   current_language);
+  event_location_up eloc
+    = string_to_event_location_basic (&copy,
+				      current_language,
+				      symbol_name_match_type::WILD);
 
   TRY
     {
diff --git a/gdb/linespec.c b/gdb/linespec.c
index c11ca1d..fca3efd 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -302,6 +302,9 @@ struct ls_parser
   struct linespec result;
 #define PARSER_RESULT(PPTR) (&(PPTR)->result)
 
+  /* Whether to do full matching or wild matching.  */
+  symbol_name_match_type match_type;
+
   /* What the parser believes the current word point should complete
      to.  */
   linespec_complete_what complete_what;
@@ -1869,7 +1872,7 @@ linespec_parse_basic (linespec_parser *parser)
 
 	  linespec_complete_function (tmp_tracker,
 				      parser->completion_word,
-				      symbol_name_match_type::WILD,
+				      parser->match_type,
 				      source_filename);
 
 	  if (tmp_tracker.have_completions ())
@@ -1894,7 +1897,7 @@ linespec_parse_basic (linespec_parser *parser)
   /* Try looking it up as a function/method.  */
   find_linespec_symbols (PARSER_STATE (parser),
 			 PARSER_RESULT (parser)->file_symtabs, name,
-			 symbol_name_match_type::WILD,
+			 parser->match_type,
 			 &symbols, &minimal_symbols);
 
   if (symbols != NULL || minimal_symbols != NULL)
@@ -2511,10 +2514,12 @@ convert_explicit_location_to_sals (struct linespec_state *self,
    if no file is validly specified.  Callers must check that.
    Also, the line number returned may be invalid.  */
 
-/* Parse the linespec in ARG.  */
+/* Parse the linespec in ARG.  MATCH_TYPE indicates how function names
+   should be matched.  */
 
 static std::vector<symtab_and_line>
-parse_linespec (linespec_parser *parser, const char *arg)
+parse_linespec (linespec_parser *parser, const char *arg,
+		symbol_name_match_type match_type)
 {
   linespec_token token;
   struct gdb_exception file_exception = exception_none;
@@ -2544,6 +2549,7 @@ parse_linespec (linespec_parser *parser, const char *arg)
   parser->lexer.stream = arg;
   parser->completion_word = arg;
   parser->complete_what = linespec_complete_what::FUNCTION;
+  parser->match_type = match_type;
 
   /* Initialize the default symtab and line offset.  */
   initialize_defaults (&PARSER_STATE (parser)->default_symtab,
@@ -2875,7 +2881,7 @@ complete_linespec_component (linespec_parser *parser,
     {
       completion_list fn_list;
 
-      linespec_complete_function (tracker, text, symbol_name_match_type::WILD,
+      linespec_complete_function (tracker, text, parser->match_type,
 				  source_filename);
       if (source_filename == NULL)
 	{
@@ -2981,7 +2987,8 @@ linespec_complete_label (completion_tracker &tracker,
 /* See description in linespec.h.  */
 
 void
-linespec_complete (completion_tracker &tracker, const char *text)
+linespec_complete (completion_tracker &tracker, const char *text,
+		   symbol_name_match_type match_type)
 {
   linespec_parser parser;
   struct cleanup *cleanup;
@@ -2990,6 +2997,7 @@ linespec_complete (completion_tracker &tracker, const char *text)
   linespec_parser_new (&parser, 0, current_language, NULL, NULL, 0, NULL);
   cleanup = make_cleanup (linespec_parser_delete, &parser);
   parser.lexer.saved_arg = text;
+  parser.match_type = match_type;
   PARSER_STREAM (&parser) = text;
 
   parser.completion_tracker = &tracker;
@@ -2999,7 +3007,7 @@ linespec_complete (completion_tracker &tracker, const char *text)
      furthest completion point we managed to parse to.  */
   TRY
     {
-      parse_linespec (&parser, text);
+      parse_linespec (&parser, text, match_type);
     }
   CATCH (except, RETURN_MASK_ERROR)
     {
@@ -3047,7 +3055,7 @@ linespec_complete (completion_tracker &tracker, const char *text)
       VEC (bound_minimal_symbol_d) *minimal_symbols;
       find_linespec_symbols (PARSER_STATE (&parser),
 			     PARSER_RESULT (&parser)->file_symtabs,
-			     func_name, symbol_name_match_type::WILD,
+			     func_name, parser.match_type,
 			     &function_symbols, &minimal_symbols);
 
       PARSER_RESULT (&parser)->function_symbols = function_symbols;
@@ -3189,7 +3197,9 @@ event_location_to_sals (linespec_parser *parser,
 	PARSER_STATE (parser)->is_linespec = 1;
 	TRY
 	  {
-	    result = parse_linespec (parser, get_linespec_location (location));
+	    const linespec_location *ls = get_linespec_location (location);
+	    result = parse_linespec (parser,
+				     ls->spec_string, ls->match_type);
 	  }
 	CATCH (except, RETURN_MASK_ERROR)
 	  {
@@ -3500,7 +3510,8 @@ decode_objc (struct linespec_state *self, linespec_p ls, const char *arg)
 	  else
 	    str = saved_arg;
 
-	  self->canonical->location = new_linespec_location (&str);
+	  self->canonical->location
+	    = new_linespec_location (&str, symbol_name_match_type::FULL);
 	}
     }
 
diff --git a/gdb/linespec.h b/gdb/linespec.h
index 7add83e..85beb62 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -180,7 +180,8 @@ extern const char * const linespec_keywords[];
 /* Complete a linespec.  */
 
 extern void linespec_complete (completion_tracker &tracker,
-			       const char *text);
+			       const char *text,
+			       symbol_name_match_type match_type);
 
 /* Complete a function symbol, in linespec mode, according to
    FUNC_MATCH_TYPE.  If SOURCE_FILENAME is non-NULL, limits completion
diff --git a/gdb/location.c b/gdb/location.c
index cbd37ba..c767890 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -41,13 +41,14 @@ struct event_location
 
   union
   {
-    /* A generic "this is a string specification" for a location.
-       This representation is used by both "normal" linespecs and
-       probes.  */
+    /* A probe.  */
     char *addr_string;
-#define EL_LINESPEC(P) ((P)->u.addr_string)
 #define EL_PROBE(P) ((P)->u.addr_string)
 
+    /* A "normal" linespec.  */
+    struct linespec_location linespec_location;
+#define EL_LINESPEC(P) (&(P)->u.linespec_location)
+
     /* An address in the inferior.  */
     CORE_ADDR address;
 #define EL_ADDRESS(P) (P)->u.address
@@ -78,17 +79,20 @@ initialize_explicit_location (struct explicit_location *explicit_loc)
 {
   memset (explicit_loc, 0, sizeof (struct explicit_location));
   explicit_loc->line_offset.sign = LINE_OFFSET_UNKNOWN;
+  explicit_loc->func_name_match_type = symbol_name_match_type::WILD;
 }
 
 /* See description in location.h.  */
 
 event_location_up
-new_linespec_location (const char **linespec)
+new_linespec_location (const char **linespec,
+		       symbol_name_match_type match_type)
 {
   struct event_location *location;
 
   location = XCNEW (struct event_location);
   EL_TYPE (location) = LINESPEC_LOCATION;
+  EL_LINESPEC (location)->match_type = match_type;
   if (*linespec != NULL)
     {
       const char *p;
@@ -97,14 +101,14 @@ new_linespec_location (const char **linespec)
       linespec_lex_to_end (linespec);
       p = remove_trailing_whitespace (orig, *linespec);
       if ((p - orig) > 0)
-	EL_LINESPEC (location) = savestring (orig, p - orig);
+	EL_LINESPEC (location)->spec_string = savestring (orig, p - orig);
     }
   return event_location_up (location);
 }
 
 /* See description in location.h.  */
 
-const char *
+const linespec_location *
 get_linespec_location (const struct event_location *location)
 {
   gdb_assert (EL_TYPE (location) == LINESPEC_LOCATION);
@@ -249,8 +253,7 @@ explicit_to_string_internal (int as_linespec,
 	  if (explicit_loc->func_name_match_type
 	      == symbol_name_match_type::FULL)
 	    buf.puts ("-qualified ");
-	  else
-	    buf.puts ("-function ");
+	  buf.puts ("-function ");
 	}
       buf.puts (explicit_loc->function_name);
       need_space = 1;
@@ -313,8 +316,10 @@ copy_event_location (const struct event_location *src)
   switch (EL_TYPE (src))
     {
     case LINESPEC_LOCATION:
-      if (EL_LINESPEC (src) != NULL)
-	EL_LINESPEC (dst) = xstrdup (EL_LINESPEC (src));
+      EL_LINESPEC (dst)->match_type = EL_LINESPEC (src)->match_type;
+      if (EL_LINESPEC (src)->spec_string != NULL)
+	EL_LINESPEC (dst)->spec_string
+	  = xstrdup (EL_LINESPEC (src)->spec_string);
       break;
 
     case ADDRESS_LOCATION:
@@ -359,7 +364,7 @@ event_location_deleter::operator() (event_location *location) const
       switch (EL_TYPE (location))
 	{
 	case LINESPEC_LOCATION:
-	  xfree (EL_LINESPEC (location));
+	  xfree (EL_LINESPEC (location)->spec_string);
 	  break;
 
 	case ADDRESS_LOCATION:
@@ -394,8 +399,17 @@ event_location_to_string (struct event_location *location)
       switch (EL_TYPE (location))
 	{
 	case LINESPEC_LOCATION:
-	  if (EL_LINESPEC (location) != NULL)
-	    EL_STRING (location) = xstrdup (EL_LINESPEC (location));
+	  if (EL_LINESPEC (location)->spec_string != NULL)
+	    {
+	      linespec_location *ls = EL_LINESPEC (location);
+	      if (ls->match_type == symbol_name_match_type::FULL)
+		{
+		  EL_STRING (location)
+		    = concat ("-qualified ", ls->spec_string, (char *) NULL);
+		}
+	      else
+		EL_STRING (location) = xstrdup (ls->spec_string);
+	    }
 	  break;
 
 	case ADDRESS_LOCATION:
@@ -762,12 +776,23 @@ string_to_explicit_location (const char **argp,
 	 argument.  */
       bool have_oarg = false;
 
+      /* True if the option needs an argument.  */
+      bool need_oarg = false;
+
       /* Convenience to consistently set both OARG/HAVE_OARG from
 	 ARG.  */
       auto set_oarg = [&] (gdb::unique_xmalloc_ptr<char> arg)
 	{
+	  if (completion_info != NULL)
+	    {
+	      /* We do this here because the set of options that take
+		 arguments matches the set of explicit location
+		 options.  */
+	      completion_info->saw_explicit_location_option = true;
+	    }
 	  oarg = std::move (arg);
 	  have_oarg = oarg != NULL;
+	  need_oarg = true;
 	};
 
       if (strncmp (opt.get (), "-source", len) == 0)
@@ -781,14 +806,9 @@ string_to_explicit_location (const char **argp,
 	  set_oarg (explicit_location_lex_one_function (argp, language,
 							completion_info));
 	  EL_EXPLICIT (location)->function_name = oarg.release ();
-	  EL_EXPLICIT (location)->func_name_match_type
-	    = symbol_name_match_type::WILD;
 	}
       else if (strncmp (opt.get (), "-qualified", len) == 0)
 	{
-	  set_oarg (explicit_location_lex_one_function (argp, language,
-							completion_info));
-	  EL_EXPLICIT (location)->function_name = oarg.release ();
 	  EL_EXPLICIT (location)->func_name_match_type
 	    = symbol_name_match_type::FULL;
 	}
@@ -830,7 +850,7 @@ string_to_explicit_location (const char **argp,
 	 case, it provides a much better user experience to issue
 	 the "invalid argument" error before any missing
 	 argument error.  */
-      if (!have_oarg && completion_info == NULL)
+      if (need_oarg && !have_oarg && completion_info == NULL)
 	error (_("missing argument for \"%s\""), opt.get ());
     }
 
@@ -853,7 +873,8 @@ string_to_explicit_location (const char **argp,
 
 event_location_up
 string_to_event_location_basic (const char **stringp,
-				const struct language_defn *language)
+				const struct language_defn *language,
+				symbol_name_match_type match_type)
 {
   event_location_up location;
   const char *cs;
@@ -881,7 +902,7 @@ string_to_event_location_basic (const char **stringp,
       else
 	{
 	  /* Everything else is a linespec.  */
-	  location = new_linespec_location (stringp);
+	  location = new_linespec_location (stringp, match_type);
 	}
     }
 
@@ -895,6 +916,7 @@ string_to_event_location (const char **stringp,
 			  const struct language_defn *language)
 {
   const char *arg, *orig;
+  symbol_name_match_type match_type = symbol_name_match_type::WILD;
 
   /* Try an explicit location.  */
   orig = arg = *stringp;
@@ -904,15 +926,21 @@ string_to_event_location (const char **stringp,
       /* It was a valid explicit location.  Advance STRINGP to
 	 the end of input.  */
       *stringp += arg - orig;
-    }
-  else
-    {
-      /* Everything else is a "basic" linespec, address, or probe
-	 location.  */
-      location = string_to_event_location_basic (stringp, language);
+
+      /* If the user really specified a location, then we're done.  */
+      if (!event_location_empty_p (location.get ()))
+	return location;
+
+      /* Otherwise, the user _only_ specified optional flags like
+	 "-qualified", otherwise string_to_explicit_location would
+	 have thrown an error.  Save the flags for "basic" linespec
+	 parsing below and discard the explicit location.  */
+      match_type = EL_EXPLICIT (location)->func_name_match_type;
     }
 
-  return location;
+  /* Everything else is a "basic" linespec, address, or probe
+     location.  */
+  return string_to_event_location_basic (stringp, language, match_type);
 }
 
 /* See description in location.h.  */
diff --git a/gdb/location.h b/gdb/location.h
index 74fd868..fcfa8fb 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -66,6 +66,17 @@ enum event_location_type
   PROBE_LOCATION
 };
 
+/* A traditional linespec.  */
+
+struct linespec_location
+{
+  /* Whether the function name is fully-qualified or not.  */
+  symbol_name_match_type match_type;
+
+  /* The linespec.  */
+  char *spec_string;
+};
+
 /* An explicit location.  This structure is used to bypass the
    parsing done on linespecs.  It still has the same requirements
    as linespecs, though.  For example, source_filename requires
@@ -110,7 +121,7 @@ extern char *
 
 /* Return a string representation of the LOCATION.
    This function may return NULL for unspecified linespecs,
-   e.g, LOCATION_LINESPEC and addr_string is NULL.
+   e.g, LINESPEC_LOCATION and spec_string is NULL.
 
    The result is cached in LOCATION.  */
 
@@ -130,12 +141,13 @@ typedef std::unique_ptr<event_location, event_location_deleter>
 
 /* Create a new linespec location.  */
 
-extern event_location_up new_linespec_location (const char **linespec);
+extern event_location_up new_linespec_location
+  (const char **linespec, symbol_name_match_type match_type);
 
-/* Return the linespec location (a string) of the given event_location
-   (which must be of type LINESPEC_LOCATION).  */
+/* Return the linespec location of the given event_location (which
+   must be of type LINESPEC_LOCATION).  */
 
-extern const char *
+extern const linespec_location *
   get_linespec_location (const struct event_location *location);
 
 /* Create a new address location.
@@ -214,12 +226,14 @@ extern event_location_up
   string_to_event_location (const char **argp,
 			    const struct language_defn *langauge);
 
-/* Like string_to_event_location, but does not attempt to parse explicit
-   locations.  */
+/* Like string_to_event_location, but does not attempt to parse
+   explicit locations.  MATCH_TYPE indicates how function names should
+   be matched.  */
 
 extern event_location_up
   string_to_event_location_basic (const char **argp,
-				  const struct language_defn *language);
+				  const struct language_defn *language,
+				  symbol_name_match_type match_type);
 
 /* Structure filled in by string_to_explicit_location to aid the
    completer.  */
@@ -236,6 +250,11 @@ struct explicit_completion_info
      If the last option is not quoted, then both are set to NULL. */
   const char *quoted_arg_start = NULL;
   const char *quoted_arg_end = NULL;
+
+  /* True if we saw an explicit location option, as opposed to only
+     flags that affect both explicit locations and linespecs, like
+     "-qualified".  */
+  bool saw_explicit_location_option = false;
 };
 
 /* Attempt to convert the input string in *ARGP into an explicit location.
diff --git a/gdb/mi/mi-cmd-break.c b/gdb/mi/mi-cmd-break.c
index 833bdc0..6cb1d71 100644
--- a/gdb/mi/mi-cmd-break.c
+++ b/gdb/mi/mi-cmd-break.c
@@ -337,7 +337,8 @@ mi_cmd_break_insert_1 (int dprintf, const char *command, char **argv, int argc)
     }
   else
     {
-      location = string_to_event_location_basic (&address, current_language);
+      location = string_to_event_location_basic (&address, current_language,
+						 symbol_name_match_type::WILD);
       if (*address)
 	error (_("Garbage '%s' at end of location"), address);
     }
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 86719d1..5bc073e 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -681,7 +681,8 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 	case bp_breakpoint:
 	  {
 	    event_location_up location
-	      = string_to_event_location_basic (&copy, current_language);
+	      = string_to_event_location_basic (&copy, current_language,
+						symbol_name_match_type::WILD);
 	    create_breakpoint (python_gdbarch,
 			       location.get (), NULL, -1, NULL,
 			       0,
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 5f15261..8ca2eed 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -838,7 +838,8 @@ gdbpy_decode_line (PyObject *self, PyObject *args)
     return NULL;
 
   if (arg != NULL)
-    location = string_to_event_location_basic (&arg, python_language);
+    location = string_to_event_location_basic (&arg, python_language,
+					       symbol_name_match_type::WILD);
 
   std::vector<symtab_and_line> decoded_sals;
   symtab_and_line def_sal;
-- 
2.5.5


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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-11-24 16:48       ` Pedro Alves
@ 2017-11-24 16:57         ` Pedro Alves
  2017-11-28  0:39         ` Keith Seitz
  1 sibling, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-24 16:57 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 11/24/2017 04:48 PM, Pedro Alves wrote:
> Hey Keith,
> 
> So I took your suggestion and ran with it.  And in the end
> I like it!  See more below.
> 

...

> Surprisingly, I didn't have to change much in the tests.  But I did
> extend them to cover these new possibilities.  I'll show the tests
> delta as a follow up.

Here it is.  This covers -qualified with linespecs that have more
components than just then function name, and also explicit locations
with -qualified in different positions.

From c06f24e28a4ae9afbb40b7c4f61db57b3e9c2b7b Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 24 Nov 2017 16:32:04 +0000
Subject: [PATCH] Fix/extend tests

---
 gdb/testsuite/gdb.linespec/cpcompletion.exp | 64 ++++++++++++++----------
 gdb/testsuite/gdb.linespec/explicit.exp     | 77 ++++++++++++++++++-----------
 2 files changed, 85 insertions(+), 56 deletions(-)

diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
index 5522b19..25f7f81 100644
--- a/gdb/testsuite/gdb.linespec/cpcompletion.exp
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -190,37 +190,49 @@ proc_with_prefix overload-2 {} {
 # Test linespecs / locations using fully-qualified names.
 
 proc_with_prefix fqn {} {
-    set cmd_prefix "b -qualified"
 
-    test_gdb_complete_unique \
-	"$cmd_prefix overload2_func" \
-	"$cmd_prefix overload2_function(overload2_arg1)"
+    # "-qualified" works with both explicit locations and linespecs.
+    # Also test that combining a source file with a function name
+    # still results in a full match, with both linespecs and explicit
+    # locations.
+    foreach cmd_prefix {
+	"b -qualified "
+	"b -qualified -function "
+	"b -qualified cpls.cc:"
+	"b -qualified -source cpls.cc -function "
+	"b -source cpls.cc -qualified -function "
+    } {
+	test_gdb_complete_unique \
+	    "${cmd_prefix}overload2_func" \
+	    "${cmd_prefix}overload2_function(overload2_arg1)"
 
-    # Drill down until we find a unique completion.
-    test_gdb_complete_multiple "$cmd_prefix " "ns_overload2_test::" "" {
-	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
-	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
-	"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
-	"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
-	"ns_overload2_test::overload2_function(overload2_arg5)"
-	"ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"
-    }
+	# Drill down until we find a unique completion.
+	test_gdb_complete_multiple "${cmd_prefix}" "ns_overload2_test::" "" {
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+	    "ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	    "ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
+	    "ns_overload2_test::overload2_function(overload2_arg5)"
+	    "ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"
+	}
 
-    test_gdb_complete_multiple "$cmd_prefix " "ns_overload2_test::(anonymous namespace)::" "" {
-	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
-	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
-	"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
-	"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
-    }
+	test_gdb_complete_multiple "${cmd_prefix}" "ns_overload2_test::(anonymous namespace)::" "" {
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+	    "ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
+	    "ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
+	}
 
-    test_gdb_complete_multiple "$cmd_prefix " "ns_overload2_test::(anonymous namespace)::ns_overload2_test::" "" {
-	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
-	"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
-    }
+	test_gdb_complete_multiple "${cmd_prefix}" "ns_overload2_test::(anonymous namespace)::ns_overload2_test::" "" {
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	    "ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
+	}
 
-    test_gdb_complete_unique \
-	"$cmd_prefix ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_func" \
-	"$cmd_prefix ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+	test_gdb_complete_unique \
+	    "${cmd_prefix}ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_func" \
+	    "${cmd_prefix}ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
+
+    }
 }
 
 # Check that a fully-qualified looked name don't match symbols in
diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp
index 8fcdf62..1e7f0dc 100644
--- a/gdb/testsuite/gdb.linespec/explicit.exp
+++ b/gdb/testsuite/gdb.linespec/explicit.exp
@@ -409,40 +409,57 @@ namespace eval $testfile {
 	    "task"
 	    "thread"
 	}
-	foreach what { "-function" "-label" "-line" "-qualified" "-source" } {
-	    with_test_prefix "complete after $what" {
-		if {$what != "-line"} {
-		    test_gdb_complete_multiple \
-			"b $what argument " "" "" $completions_list
-		    test_gdb_complete_unique \
-			"b $what argument thr" \
-			"b $what argument thread"
-		    test_gdb_complete_unique \
-			"b $what argument -fun" \
-			"b $what argument -function"
-		} else {
-		    # After -line, we expect a number / offset.
-		    foreach line {"10" "+10" "-10"} {
-			test_gdb_complete_multiple \
-			    "b -line $line " "" "" $completions_list
-			test_gdb_complete_unique \
-			    "b -line $line thr" \
-			    "b -line $line thread"
-			test_gdb_complete_unique \
-			    "b -line $line -fun" \
-			    "b -line $line -function"
+	foreach what { "-function" "-label" "-line" "-source" } {
+	    # Also test with "-qualified" appearing before the
+	    # explicit location.
+	    foreach prefix {"" "-qualified "} {
+
+		# ... and with "-qualified" appearing after the
+		# explicit location.
+		foreach suffix {"" " -qualified"} {
+		    with_test_prefix "complete after $prefix$what$suffix" {
+			if {$what != "-line"} {
+			    set w "$prefix$what argument$suffix "
+			    test_gdb_complete_multiple \
+				"b $w" "" "" $completions_list
+			    test_gdb_complete_unique \
+				"b $w thr" \
+				"b $w thread"
+			    test_gdb_complete_unique \
+				"b $w -fun" \
+				"b $w -function"
+			} else {
+			    # After -line, we expect a number / offset.
+			    foreach line {"10" "+10" "-10"} {
+				set w "$prefix-line $line$suffix"
+				test_gdb_complete_multiple \
+				    "b $w " "" "" $completions_list
+				test_gdb_complete_unique \
+				    "b $w thr" \
+				    "b $w thread"
+				test_gdb_complete_unique \
+				    "b $w -fun" \
+				    "b $w -function"
+			    }
+
+			    # With an invalid -line argument, we don't get any
+			    # completions.
+			    test_gdb_complete_none "b $prefix-line argument$suffix "
+			}
+
 		    }
 
-		    # With an invalid -line argument, we don't get any
-		    # completions.
-		    test_gdb_complete_none "b -line argument "
 		}
 
-		# Don't complete a linespec keyword ("thread") or
-		# another option name when expecting an option
-		# argument.
-		test_gdb_complete_none "b $what thr"
-		test_gdb_complete_none "b $what -fun"
+		# These tests don't make sense with "-qualified" after
+		# the location.
+		with_test_prefix "complete after $prefix$what" {
+		    # Don't complete a linespec keyword ("thread") or
+		    # another option name when expecting an option
+		    # argument.
+		    test_gdb_complete_none "b $prefix$what thr"
+		    test_gdb_complete_none "b $prefix$what -fun"
+		}
 	    }
 	}
 
-- 
2.5.5


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

* [pushed] Re: [PATCH 34/40] Make strcmp_iw NOT ignore whitespace in the middle of tokens
  2017-08-09 15:48   ` Keith Seitz
@ 2017-11-24 23:38     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-24 23:38 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/09/2017 04:48 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> currently "b func tion" manages to set a breakpoint at "function" !
>>
>> All this years I had never noticed this, but now that the linespec
>> completer actually works, this easily happens by accident, with:
> 
> That makes two of us!
> 
>> The operator_stoken changes are necessary due to a latent bug --
>> currently "operator char" becomes "operatorchar", and later look ups
>> only find it because strcmp_iw ignores the whitespace...
> 
> I have a similar fix on the compile branch. :-)
> 
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* c-exp.y (oper): Add space to operator names.
>> 	* cp-support.c (cp_symbol_name_matches_1)
>> 	(cp_fq_symbol_name_matches): Pass language to
>> 	strncmp_iw_with_mode.
>> 	(test_cp_symbol_name_cmp): Add unit tests.
>> 	* language.c (default_symbol_name_matcher): Pass language to
>> 	strncmp_iw_with_mode.
>> 	* utils.c: Include "cp-support.h" and <algorithm>.
>> 	(valid_identifier_name_char, cp_skip_operator_token, skip_ws)
>> 	(cp_is_operator): New functions.
>> 	(strncmp_iw_with_mode): Use them.  Add language parameter.  Don't
>> 	skip whitespace in the symbol name when the lookup name doesn't
>> 	have spaces, and vice versa.
>> 	(strncmp_iw, strcmp_iw): Pass language to strncmp_iw_with_mode.
>                                       ^^^^^^^^
> 
> Not just language, but language_minimal.
> 
>> 	* utils.h (strncmp_iw_with_mode): Add language parameter.
> 
>> diff --git a/gdb/c-exp.y b/gdb/c-exp.y
>> index 24a2fbd..0a182cc 100644
>> --- a/gdb/c-exp.y
>> +++ b/gdb/c-exp.y
>> @@ -1487,7 +1487,7 @@ oper:	OPERATOR NEW
>>  	|	OPERATOR '>'
>>  			{ $$ = operator_stoken (">"); }
>>  	|	OPERATOR ASSIGN_MODIFY
>> -			{ const char *op = "unknown";
>> +			{ const char *op = " unknown";
> 
> Good catch. I missed that.
> 
>>  			  switch ($2)
>>  			    {
>>  			    case BINOP_RSH:
>> @@ -1563,7 +1563,8 @@ oper:	OPERATOR NEW
>>  
>>  			  c_print_type ($2, NULL, &buf, -1, 0,
>>  					&type_print_raw_options);
>> -			  $$ = operator_stoken (buf.c_str ());
>> +			  std::string name = " " + buf.string ();
>> +			  $$ = operator_stoken (name.c_str ());
>>  			}
>>  	;
>>  
> 
> The only additional change that I have in my compile branch is
> that since this type's name could come from a user, it needs to be canonicalized.
> But I can hit that when(ever?!) I start submitting some of the precursor
> patches that I have. [I have a test that demonstrates the need for
> canonicalization in the c++compile branch.]
> 
>> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
>> index 84d8a6b..4c353c5 100644
>> --- a/gdb/cp-support.c
>> +++ b/gdb/cp-support.c
>> @@ -1857,6 +1857,67 @@ test_cp_symbol_name_cmp ()
>>    CHECK_MATCH_C ("function(int)", "function(int)");
> 
> [snip a WHOLE LOTTA TESTS]
> 
> AWESOME!
> 
>> diff --git a/gdb/utils.c b/gdb/utils.c
>> index 9798edc..484c1ef 100644
>> --- a/gdb/utils.c
>> +++ b/gdb/utils.c
>> @@ -65,6 +65,8 @@
>>  #include "gdb_usleep.h"
>>  #include "interps.h"
>>  #include "gdb_regex.h"
>> +#include "cp-support.h"
>> +#include <algorithm>
>>  
>>  #if !HAVE_DECL_MALLOC
>>  extern PTR malloc ();		/* ARI: PTR */
>> @@ -2418,22 +2420,227 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
>>      }
>>  }
>>  
>> +/* True if CH is a character that can be part of a symbol name.  I.e.,
>> +   either a number, a letter, or a '_'.  */
>> +
>> +static bool
>> +valid_identifier_name_char (int ch)
>> +{
>> +  return (isalnum (ch) || ch == '_');
>> +}
> 
> Couldn't this be language-dependent? [Yikes!]
> Also note that there are a handful of places where this could be used
> [follow-up patch?] in linespec.c, location.c, symtab.c. Maybe more.
> 

Yeah, and in the language parsers too.  I have a follow up patch
(in another branch where I'm playing with UTF-8) that normalizes this.
It involves including safe-ctype.h, which then forces isalnum -> ISALNUM
etc., so I'd rather leave such normalization for that branch.

> We have logic for c++ operators all over the place. Note to self: this needs
> to be cleaned up/consolidated.

Definitely agreed.  I'd like to move some if this code out of the gdb/utils.c
kitchensink too, but I was leaving that for follow up patches, because moving
this code around makes it harder to maintain/review.

>> +
>> +/* Skip to end of token, or to END, whatever comes first.  */
>> +
> 
> I think a(n explicit) mention that the input is assumed to be an operator name. It's
> mentioned in the name, but please consider repeating that in the comment. It's important.

Fixed.


>> +
>> +static bool
>> +cp_is_operator (const char *string, const char *start)
> 
> Missing comment?

Yes, fixed.


> 
> Just a passing comment: I'm kinda torn on this. When new languages are added,
> this is going to be yet another place that language implementers are going to
> have to modify. While a language method would probably be better (for some
> definition of "better"), I don't want to see the language vector bloat beyond
> control either. So IMO there's no clear better path.

Inheritance is overrated.  :-P


>> +	  if (cp_is_operator (string1, string1_start))
>> +	    {
>> +	      /* An operator name in STRING1.  Check STRING2.  */
>> +	      size_t cmplen = std::min<size_t> (CP_OPERATOR_LEN, end_str2 - string2);
> 
> line length == 85

Indeed.  Fixed.


>>  strcmp_iw (const char *string1, const char *string2)
>>  {
>>    return strncmp_iw_with_mode (string1, string2, strlen (string2),
>> -			       strncmp_iw_mode::MATCH_PARAMS);
>> +			       strncmp_iw_mode::MATCH_PARAMS, language_minimal);
>>  }
> 
> I think the comments for both of these functions should be updated, since
> they pass language_minimal to strncmp_iw_with_mode. Therefore,
> 
>   strncmp_iw_with_mode (string1, string2, len, MATCH_PARAMS, a_language)
> 
> may not necessarily equal
> 
>   strncmp_iw (string1, string2)
> 
> That may not be obvious to the casual user. Some sort of caveat seems prudent.

Agreed, I've added comments.

>> --- a/gdb/utils.h
>> +++ b/gdb/utils.h
>> @@ -56,7 +56,8 @@ enum class strncmp_iw_mode
>>  extern int strncmp_iw_with_mode (const char *string1,
>>  				 const char *string2,
>>  				 size_t string2_len,
>> -				 strncmp_iw_mode mode);
>> +				 strncmp_iw_mode mode,
>> +				 enum language language);
> 
> While most of these parameters are rather obvious usage, it is not obvious to
> me why a strncmp-like function needs a language definition. [Of course,
> I understand why after reading the code, but a brief mention of how LANGUAGE
> affects the operation might be useful IMO. YMMV.]
> 

Fixed.

I realized I could rebase this on top of master (instead of on top of the
wildmatching), and pushed it in.  This will allow pushing in a good
part of the tests (in a follow up patch), exercising the earlier
get-rid-of-quoting-linespecs improvements, label completion, etc., before
the wild matching part is in.

Here's what I pushed in.

From 0662b6a7c1b3b04a4ca31a09af703c91c7aa9646 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 24 Nov 2017 23:30:04 +0000
Subject: [PATCH] Make strcmp_iw NOT ignore whitespace in the middle of tokens

currently "b func tion" manages to set a breakpoint at "function" !

All these years I had never noticed this, but now that the linespec
completer actually works, this easily happens by accident, with:

  "b func t<tab>"

expecting to get "thread", but getting instead:

  "b func tion"

...

Also, this:

  "b rettypefunc<int>"

manages to set a breakpoint on "rettype func<int>()".

These things happen due to strcmp_iw "magic".

Fix it by teaching strcmp_iw about when can it skip whitespace.  This
required handling user-defined operators, and scope operators,
complicating the code a bit, unfortunately.  I added unit tests for
all the corner cases I stumbled on, as I was developing this, and then
in the end wrote a testsuite testcase covering many of the same things
and more (to be added later).

gdb/ChangeLog:
2017-11-24  Pedro Alves  <palves@redhat.com>

	* cp-support.c (cp_symbol_name_matches_1): New, factored out from
	cp_fq_symbol_name_matches.  Pass language_cplus to
	strncmp_with_mode.
	(cp_fq_symbol_name_matches): Call cp_symbol_name_matches_1.
	(selftests::test_cp_symbol_name_cmp): New.
	(_initialize_cp_support): Register "cp_symbol_name_matches"
	selftests.
	* language.c (default_symbol_name_matcher): Pass language_minimal
	to strncmp_iw_with_mode.
	* utils.c: Include "cp-support.h" and <algorithm>.
	(valid_identifier_name_char, cp_skip_operator_token, skip_ws)
	(cp_is_operator): New functions.
	(strncmp_iw_with_mode): Use them.  Add language parameter.  Don't
	skip whitespace in the symbol name when the lookup name doesn't
	have spaces, and vice versa.
	(strncmp_iw, strcmp_iw): Pass language to strncmp_iw_with_mode.
	* utils.h (strncmp_iw_with_mode): Add language parameter.
---
 gdb/ChangeLog    |  20 +++++
 gdb/cp-support.c | 178 +++++++++++++++++++++++++++++++++++++++---
 gdb/language.c   |   2 +-
 gdb/utils.c      | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 gdb/utils.h      |  18 ++++-
 5 files changed, 429 insertions(+), 22 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 26d5cd3..befce60 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,23 @@
+2017-11-24  Pedro Alves  <palves@redhat.com>
+
+	* cp-support.c (cp_symbol_name_matches_1): New, factored out from
+	cp_fq_symbol_name_matches.  Pass language_cplus to
+	strncmp_with_mode.
+	(cp_fq_symbol_name_matches): Call cp_symbol_name_matches_1.
+	(selftests::test_cp_symbol_name_cmp): New.
+	(_initialize_cp_support): Register "cp_symbol_name_matches"
+	selftests.
+	* language.c (default_symbol_name_matcher): Pass language_minimal
+	to strncmp_iw_with_mode.
+	* utils.c: Include "cp-support.h" and <algorithm>.
+	(valid_identifier_name_char, cp_skip_operator_token, skip_ws)
+	(cp_is_operator): New functions.
+	(strncmp_iw_with_mode): Use them.  Add language parameter.  Don't
+	skip whitespace in the symbol name when the lookup name doesn't
+	have spaces, and vice versa.
+	(strncmp_iw, strcmp_iw): Pass language to strncmp_iw_with_mode.
+	* utils.h (strncmp_iw_with_mode): Add language parameter.
+
 2017-11-24  Joel Brobecker  <brobecker@adacore.com>
 
 	* ada-lang.c (ada_exception_message_1, ada_exception_message):
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 1cab69b..368112a 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1617,6 +1617,39 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
 
 /* C++ symbol_name_matcher_ftype implementation.  */
 
+/* Helper for cp_fq_symbol_name_matches (i.e.,
+   symbol_name_matcher_ftype implementation).  Split to a separate
+   function for unit-testing convenience.
+
+   See symbol_name_matcher_ftype for description of SYMBOL_SEARCH_NAME
+   and COMP_MATCH_RES.
+
+   LOOKUP_NAME/LOOKUP_NAME_LEN is the name we're looking up.
+
+   See strncmp_iw_with_mode for description of MODE.
+*/
+
+static bool
+cp_symbol_name_matches_1 (const char *symbol_search_name,
+			  const char *lookup_name,
+			  size_t lookup_name_len,
+			  strncmp_iw_mode mode,
+			  completion_match *match)
+{
+  if (strncmp_iw_with_mode (symbol_search_name,
+			    lookup_name, lookup_name_len,
+			    mode, language_cplus) == 0)
+    {
+      if (match != NULL)
+	match->set_match (symbol_search_name);
+      return true;
+    }
+
+  return false;
+}
+
+/* C++ symbol_name_matcher_ftype implementation.  */
+
 static bool
 cp_fq_symbol_name_matches (const char *symbol_search_name,
 			   const lookup_name_info &lookup_name,
@@ -1629,16 +1662,9 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
 			  ? strncmp_iw_mode::NORMAL
 			  : strncmp_iw_mode::MATCH_PARAMS);
 
-  if (strncmp_iw_with_mode (symbol_search_name,
-			    name.c_str (), name.size (),
-			    mode) == 0)
-    {
-      if (match != NULL)
-	match->set_match (symbol_search_name);
-      return true;
-    }
-
-  return false;
+  return cp_symbol_name_matches_1 (symbol_search_name,
+				   name.c_str (), name.size (),
+				   mode, match);
 }
 
 /* See cp-support.h.  */
@@ -1653,6 +1679,136 @@ cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
 
 namespace selftests {
 
+void
+test_cp_symbol_name_matches ()
+{
+#define CHECK_MATCH(SYMBOL, INPUT)					\
+  SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL,				\
+					INPUT, sizeof (INPUT) - 1,	\
+					strncmp_iw_mode::MATCH_PARAMS,	\
+					NULL))
+
+#define CHECK_NOT_MATCH(SYMBOL, INPUT)					\
+  SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL,			\
+					 INPUT, sizeof (INPUT) - 1,	\
+					 strncmp_iw_mode::MATCH_PARAMS,	\
+					 NULL))
+
+  /* Like CHECK_MATCH, and also check that INPUT (and all substrings
+     that start at index 0) completes to SYMBOL.  */
+#define CHECK_MATCH_C(SYMBOL, INPUT)					\
+  do									\
+    {									\
+      CHECK_MATCH (SYMBOL, INPUT);					\
+      for (size_t i = 0; i < sizeof (INPUT) - 1; i++)			\
+	SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL, INPUT, i,		\
+					      strncmp_iw_mode::NORMAL,	\
+					      NULL));			\
+    } while (0)
+
+  /* Like CHECK_NOT_MATCH, and also check that INPUT does NOT complete
+     to SYMBOL.  */
+#define CHECK_NOT_MATCH_C(SYMBOL, INPUT)				\
+  do									\
+    { 									\
+      CHECK_NOT_MATCH (SYMBOL, INPUT);					\
+      SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL, INPUT,		\
+					     sizeof (INPUT) - 1,	\
+					     strncmp_iw_mode::NORMAL,	\
+					     NULL));			\
+    } while (0)
+
+  /* Lookup name without parens matches all overloads.  */
+  CHECK_MATCH_C ("function()", "function");
+  CHECK_MATCH_C ("function(int)", "function");
+
+  /* Check whitespace around parameters is ignored.  */
+  CHECK_MATCH_C ("function()", "function ()");
+  CHECK_MATCH_C ("function ( )", "function()");
+  CHECK_MATCH_C ("function ()", "function( )");
+  CHECK_MATCH_C ("func(int)", "func( int )");
+  CHECK_MATCH_C ("func(int)", "func ( int ) ");
+  CHECK_MATCH_C ("func ( int )", "func( int )");
+  CHECK_MATCH_C ("func ( int )", "func ( int ) ");
+
+  /* Check symbol name prefixes aren't incorrectly matched.  */
+  CHECK_NOT_MATCH ("func", "function");
+  CHECK_NOT_MATCH ("function", "func");
+  CHECK_NOT_MATCH ("function()", "func");
+
+  /* Check that if the lookup name includes parameters, only the right
+     overload matches.  */
+  CHECK_MATCH_C ("function(int)", "function(int)");
+  CHECK_NOT_MATCH_C ("function(int)", "function()");
+
+  /* Check that whitespace within symbol names is not ignored.  */
+  CHECK_NOT_MATCH_C ("function", "func tion");
+  CHECK_NOT_MATCH_C ("func__tion", "func_ _tion");
+  CHECK_NOT_MATCH_C ("func11tion", "func1 1tion");
+
+  /* Check the converse, which can happen with template function,
+     where the return type is part of the demangled name.  */
+  CHECK_NOT_MATCH_C ("func tion", "function");
+  CHECK_NOT_MATCH_C ("func1 1tion", "func11tion");
+  CHECK_NOT_MATCH_C ("func_ _tion", "func__tion");
+
+  /* Within parameters too.  */
+  CHECK_NOT_MATCH_C ("func(param)", "func(par am)");
+
+  /* Check handling of whitespace around C++ operators.  */
+  CHECK_NOT_MATCH_C ("operator<<", "opera tor<<");
+  CHECK_NOT_MATCH_C ("operator<<", "operator< <");
+  CHECK_NOT_MATCH_C ("operator<<", "operator < <");
+  CHECK_NOT_MATCH_C ("operator==", "operator= =");
+  CHECK_NOT_MATCH_C ("operator==", "operator = =");
+  CHECK_MATCH_C ("operator<<", "operator <<");
+  CHECK_MATCH_C ("operator<<()", "operator <<");
+  CHECK_NOT_MATCH_C ("operator<<()", "operator<<(int)");
+  CHECK_NOT_MATCH_C ("operator<<(int)", "operator<<()");
+  CHECK_MATCH_C ("operator==", "operator ==");
+  CHECK_MATCH_C ("operator==()", "operator ==");
+  CHECK_MATCH_C ("operator <<", "operator<<");
+  CHECK_MATCH_C ("operator ==", "operator==");
+  CHECK_MATCH_C ("operator bool", "operator  bool");
+  CHECK_MATCH_C ("operator bool ()", "operator  bool");
+  CHECK_MATCH_C ("operatorX<<", "operatorX < <");
+  CHECK_MATCH_C ("Xoperator<<", "Xoperator < <");
+
+  CHECK_MATCH_C ("operator()(int)", "operator()(int)");
+  CHECK_MATCH_C ("operator()(int)", "operator ( ) ( int )");
+  CHECK_MATCH_C ("operator()<long>(int)", "operator ( ) < long > ( int )");
+  /* The first "()" is not the parameter list.  */
+  CHECK_NOT_MATCH ("operator()(int)", "operator");
+
+  /* Misc user-defined operator tests.  */
+
+  CHECK_NOT_MATCH_C ("operator/=()", "operator ^=");
+  /* Same length at end of input.  */
+  CHECK_NOT_MATCH_C ("operator>>", "operator[]");
+  /* Same length but not at end of input.  */
+  CHECK_NOT_MATCH_C ("operator>>()", "operator[]()");
+
+  CHECK_MATCH_C ("base::operator char*()", "base::operator char*()");
+  CHECK_MATCH_C ("base::operator char*()", "base::operator char * ()");
+  CHECK_MATCH_C ("base::operator char**()", "base::operator char * * ()");
+  CHECK_MATCH ("base::operator char**()", "base::operator char * *");
+  CHECK_MATCH_C ("base::operator*()", "base::operator*()");
+  CHECK_NOT_MATCH_C ("base::operator char*()", "base::operatorc");
+  CHECK_NOT_MATCH ("base::operator char*()", "base::operator char");
+  CHECK_NOT_MATCH ("base::operator char*()", "base::operat");
+
+  /* Check handling of whitespace around C++ scope operators.  */
+  CHECK_NOT_MATCH_C ("foo::bar", "foo: :bar");
+  CHECK_MATCH_C ("foo::bar", "foo :: bar");
+  CHECK_MATCH_C ("foo :: bar", "foo::bar");
+
+  CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi()");
+  CHECK_MATCH_C ("abc::def::ghi ( )", "abc::def::ghi()");
+  CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi ( )");
+  CHECK_MATCH_C ("function()", "function()");
+  CHECK_MATCH_C ("bar::function()", "bar::function()");
+}
+
 /* If non-NULL, return STR wrapped in quotes.  Otherwise, return a
    "<null>" string (with no quotes).  */
 
@@ -1856,6 +2012,8 @@ display the offending symbol."),
 #endif
 
 #if GDB_SELF_TEST
+  selftests::register_test ("cp_symbol_name_matches",
+			    selftests::test_cp_symbol_name_matches);
   selftests::register_test ("cp_remove_params",
 			    selftests::test_cp_remove_params);
 #endif
diff --git a/gdb/language.c b/gdb/language.c
index 76047c7..2a1419c 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -713,7 +713,7 @@ default_symbol_name_matcher (const char *symbol_search_name,
 			  : strncmp_iw_mode::MATCH_PARAMS);
 
   if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
-			    mode) == 0)
+			    mode, language_minimal) == 0)
     {
       if (match != NULL)
 	match->set_match (symbol_search_name);
diff --git a/gdb/utils.c b/gdb/utils.c
index b5c011b..3e817ed 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -68,6 +68,8 @@
 #include "job-control.h"
 #include "common/selftest.h"
 #include "common/gdb_optional.h"
+#include "cp-support.h"
+#include <algorithm>
 
 #if !HAVE_DECL_MALLOC
 extern PTR malloc ();		/* ARI: PTR */
@@ -2156,22 +2158,233 @@ fprintf_symbol_filtered (struct ui_file *stream, const char *name,
     }
 }
 
+/* True if CH is a character that can be part of a symbol name.  I.e.,
+   either a number, a letter, or a '_'.  */
+
+static bool
+valid_identifier_name_char (int ch)
+{
+  return (isalnum (ch) || ch == '_');
+}
+
+/* Skip to end of token, or to END, whatever comes first.  Input is
+   assumed to be a C++ operator name.  */
+
+static const char *
+cp_skip_operator_token (const char *token, const char *end)
+{
+  const char *p = token;
+  while (p != end && !isspace (*p) && *p != '(')
+    {
+      if (valid_identifier_name_char (*p))
+	{
+	  while (p != end && valid_identifier_name_char (*p))
+	    p++;
+	  return p;
+	}
+      else
+	{
+	  /* Note, ordered such that among ops that share a prefix,
+	     longer comes first.  This is so that the loop below can
+	     bail on first match.  */
+	  static const char *ops[] =
+	    {
+	      "[",
+	      "]",
+	      "~",
+	      ",",
+	      "-=", "--", "->", "-",
+	      "+=", "++", "+",
+	      "*=", "*",
+	      "/=", "/",
+	      "%=", "%",
+	      "|=", "||", "|",
+	      "&=", "&&", "&",
+	      "^=", "^",
+	      "!=", "!",
+	      "<<=", "<=", "<<", "<",
+	      ">>=", ">=", ">>", ">",
+	      "==", "=",
+	    };
+
+	  for (const char *op : ops)
+	    {
+	      size_t oplen = strlen (op);
+	      size_t lencmp = std::min<size_t> (oplen, end - p);
+
+	      if (strncmp (p, op, lencmp) == 0)
+		return p + lencmp;
+	    }
+	  /* Some unidentified character.  Return it.  */
+	  return p + 1;
+	}
+    }
+
+  return p;
+}
+
+/* Advance STRING1/STRING2 past whitespace.  */
+
+static void
+skip_ws (const char *&string1, const char *&string2, const char *end_str2)
+{
+  while (isspace (*string1))
+    string1++;
+  while (string2 < end_str2 && isspace (*string2))
+    string2++;
+}
+
+/* True if STRING points at the start of a C++ operator name.  START
+   is the start of the string that STRING points to, hence when
+   reading backwards, we must not read any character before START.  */
+
+static bool
+cp_is_operator (const char *string, const char *start)
+{
+  return ((string == start
+	   || !valid_identifier_name_char (string[-1]))
+	  && strncmp (string, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+	  && !valid_identifier_name_char (string[CP_OPERATOR_LEN]));
+}
+
 /* See utils.h.  */
 
 int
 strncmp_iw_with_mode (const char *string1, const char *string2,
-		      size_t string2_len, strncmp_iw_mode mode)
+		      size_t string2_len, strncmp_iw_mode mode,
+		      enum language language)
 {
+  const char *string1_start = string1;
   const char *end_str2 = string2 + string2_len;
+  bool skip_spaces = true;
+  bool have_colon_op = (language == language_cplus
+			|| language == language_rust
+			|| language == language_fortran);
 
   while (1)
     {
-      while (isspace (*string1))
-	string1++;
-      while (string2 < end_str2 && isspace (*string2))
-	string2++;
+      if (skip_spaces
+	  || ((isspace (*string1) && !valid_identifier_name_char (*string2))
+	      || (isspace (*string2) && !valid_identifier_name_char (*string1))))
+	{
+	  skip_ws (string1, string2, end_str2);
+	  skip_spaces = false;
+	}
+
       if (*string1 == '\0' || string2 == end_str2)
 	break;
+
+      /* Handle the :: operator.  */
+      if (have_colon_op && string1[0] == ':' && string1[1] == ':')
+	{
+	  if (*string2 != ':')
+	    return 1;
+
+	  string1++;
+	  string2++;
+
+	  if (string2 == end_str2)
+	    break;
+
+	  if (*string2 != ':')
+	    return 1;
+
+	  string1++;
+	  string2++;
+
+	  while (isspace (*string1))
+	    string1++;
+	  while (string2 < end_str2 && isspace (*string2))
+	    string2++;
+	  continue;
+	}
+
+      /* Handle C++ user-defined operators.  */
+      else if (language == language_cplus
+	       && *string1 == 'o')
+	{
+	  if (cp_is_operator (string1, string1_start))
+	    {
+	      /* An operator name in STRING1.  Check STRING2.  */
+	      size_t cmplen
+		= std::min<size_t> (CP_OPERATOR_LEN, end_str2 - string2);
+	      if (strncmp (string1, string2, cmplen) != 0)
+		return 1;
+
+	      string1 += cmplen;
+	      string2 += cmplen;
+
+	      if (string2 != end_str2)
+		{
+		  /* Check for "operatorX" in STRING2.  */
+		  if (valid_identifier_name_char (*string2))
+		    return 1;
+
+		  skip_ws (string1, string2, end_str2);
+		}
+
+	      /* Handle operator().  */
+	      if (*string1 == '(')
+		{
+		  if (string2 == end_str2)
+		    {
+		      if (mode == strncmp_iw_mode::NORMAL)
+			return 0;
+		      else
+			{
+			  /* Don't break for the regular return at the
+			     bottom, because "operator" should not
+			     match "operator()", since this open
+			     parentheses is not the parameter list
+			     start.  */
+			  return *string1 != '\0';
+			}
+		    }
+
+		  if (*string1 != *string2)
+		    return 1;
+
+		  string1++;
+		  string2++;
+		}
+
+	      while (1)
+		{
+		  skip_ws (string1, string2, end_str2);
+
+		  /* Skip to end of token, or to END, whatever comes
+		     first.  */
+		  const char *end_str1 = string1 + strlen (string1);
+		  const char *p1 = cp_skip_operator_token (string1, end_str1);
+		  const char *p2 = cp_skip_operator_token (string2, end_str2);
+
+		  cmplen = std::min (p1 - string1, p2 - string2);
+		  if (p2 == end_str2)
+		    {
+		      if (strncmp (string1, string2, cmplen) != 0)
+			return 1;
+		    }
+		  else
+		    {
+		      if (p1 - string1 != p2 - string2)
+			return 1;
+		      if (strncmp (string1, string2, cmplen) != 0)
+			return 1;
+		    }
+
+		  string1 += cmplen;
+		  string2 += cmplen;
+
+		  if (*string1 == '\0' || string2 == end_str2)
+		    break;
+		  if (*string1 == '(' || *string2 == '(')
+		    break;
+		}
+
+	      continue;
+	    }
+	}
+
       if (case_sensitivity == case_sensitive_on && *string1 != *string2)
 	break;
       if (case_sensitivity == case_sensitive_off
@@ -2179,6 +2392,12 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
 	      != tolower ((unsigned char) *string2)))
 	break;
 
+      /* If we see any non-whitespace, non-identifier-name character
+	 (any of "()<>*&" etc.), then skip spaces the next time
+	 around.  */
+      if (!isspace (*string1) && !valid_identifier_name_char (*string1))
+	skip_spaces = true;
+
       string1++;
       string2++;
     }
@@ -2200,7 +2419,7 @@ int
 strncmp_iw (const char *string1, const char *string2, size_t string2_len)
 {
   return strncmp_iw_with_mode (string1, string2, string2_len,
-			       strncmp_iw_mode::NORMAL);
+			       strncmp_iw_mode::NORMAL, language_minimal);
 }
 
 /* See utils.h.  */
@@ -2209,7 +2428,7 @@ int
 strcmp_iw (const char *string1, const char *string2)
 {
   return strncmp_iw_with_mode (string1, string2, strlen (string2),
-			       strncmp_iw_mode::MATCH_PARAMS);
+			       strncmp_iw_mode::MATCH_PARAMS, language_minimal);
 }
 
 /* This is like strcmp except that it ignores whitespace and treats
diff --git a/gdb/utils.h b/gdb/utils.h
index e2fa430..dff4b17 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -48,17 +48,24 @@ enum class strncmp_iw_mode
 
 /* Helper for strcmp_iw and strncmp_iw.  Exported so that languages
    can implement both NORMAL and MATCH_PARAMS variants in a single
-   function and defer part of the work to strncmp_iw_with_mode.  */
+   function and defer part of the work to strncmp_iw_with_mode.
+   LANGUAGE is used to implement some context-sensitive
+   language-specific comparisons.  For example, for C++,
+   "string1=operator()" should not match "string2=operator" even in
+   MATCH_PARAMS mode.  */
 extern int strncmp_iw_with_mode (const char *string1,
 				 const char *string2,
 				 size_t string2_len,
-				 strncmp_iw_mode mode);
+				 strncmp_iw_mode mode,
+				 enum language language);
 
 /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
    differences in whitespace.  STRING2_LEN is STRING2's length.
    Returns 0 if STRING1 matches STRING2_LEN characters of STRING2,
    non-zero otherwise (slightly different than strncmp()'s range of
-   return values).  */
+   return values).  Note: passes language_minimal to
+   strncmp_iw_with_mode, and should therefore be avoided if a more
+   suitable language is available.  */
 extern int strncmp_iw (const char *string1, const char *string2,
 		       size_t string2_len);
 
@@ -70,7 +77,10 @@ extern int strncmp_iw (const char *string1, const char *string2,
    As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
    This "feature" is useful when searching for matching C++ function
    names (such as if the user types 'break FOO', where FOO is a
-   mangled C++ function).  */
+   mangled C++ function).
+
+   Note: passes language_minimal to strncmp_iw_with_mode, and should
+   therefore be avoided if a more suitable language is available.  */
 extern int strcmp_iw (const char *string1, const char *string2);
 
 extern int strcmp_iw_ordered (const char *, const char *);
-- 
2.5.5

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

* [pushed] Re: [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests
  2017-08-09 17:59   ` Keith Seitz
@ 2017-11-25  0:18     ` Pedro Alves
  2017-11-30 15:43       ` Yao Qi
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-25  0:18 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/09/2017 06:59 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>> Grows the gdb.linespec/ tests like this:
>>
>>  -# of expected passes           4458
>>  +# of expected passes           8817
> 
> Again, this is awesome.
> 
> Just one little nit.
> 
>> diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.cc b/gdb/testsuite/gdb.linespec/cpls-ops.cc
>> new file mode 100644
>> index 0000000..c1156f1
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.linespec/cpls-ops.cc
>> @@ -0,0 +1,253 @@
> [snip]
>> +
>> +test_op_conversion::operator const volatile test_op_conversion_res **() const volatile
> 
> line length == 86

Indeed, fixed.

> 
>> +{
>> +  return NULL;
>> +}
>> +
> [snip]
> 
> 
> Otherwise, LGTM.
> 

Great, and a much belated thanks.

 and that this one works with current master already, so I've
pushed it in, as below.


From 6a3c6ee41898743234d8fd9f9cab15f2ecdaba49 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Sat, 25 Nov 2017 00:09:25 +0000
Subject: [PATCH] Add comprehensive C++ operator linespec/location/completion
 tests

This exercises the special handling C++ operators require in several
places in the linespec parser, both the linespec and explicit location
completers, symbol lookup, etc.  Particularly, makes sure all that
works without quoting.

Note that despite the apparent smallish size, this adds thousands of
tests to the testsuite, due to combination explosion (linespecs,
explicit locations, tab completion, complete command, completion at
different points in each function, etc.)

Grows the gdb.linespec/ tests like this:

 -# of expected passes           3464
 +# of expected passes           7823

gdb/testsuite/ChangeLog:
2017-11-25  Pedro Alves  <palves@redhat.com>

	* gdb.linespec/cpls-ops.cc: New file.
	* gdb.linespec/cpls-ops.exp: New file.
	* lib/completion-support.exp (test_complete_prefix_range_re): New,
	factored out from ...
	(test_complete_prefix_range): ... this.
---
 gdb/testsuite/ChangeLog                  |   8 +
 gdb/testsuite/gdb.linespec/cpls-ops.cc   | 254 ++++++++++++++
 gdb/testsuite/gdb.linespec/cpls-ops.exp  | 565 +++++++++++++++++++++++++++++++
 gdb/testsuite/lib/completion-support.exp |  23 +-
 4 files changed, 843 insertions(+), 7 deletions(-)
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-ops.cc
 create mode 100644 gdb/testsuite/gdb.linespec/cpls-ops.exp

diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 469dde1..b45ff7b 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2017-11-25  Pedro Alves  <palves@redhat.com>
+
+	* gdb.linespec/cpls-ops.cc: New file.
+	* gdb.linespec/cpls-ops.exp: New file.
+	* lib/completion-support.exp (test_complete_prefix_range_re): New,
+	factored out from ...
+	(test_complete_prefix_range): ... this.
+
 2017-11-24  Pedro Alves  <palves@redhat.com>
 
 	* gdb.linespec/cpcompletion.exp: New file.
diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.cc b/gdb/testsuite/gdb.linespec/cpls-ops.cc
new file mode 100644
index 0000000..c9b034d
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls-ops.cc
@@ -0,0 +1,254 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <cstddef>
+
+/* Code for operator() tests.  */
+
+struct test_unique_op_call
+{
+  void operator() (int);
+};
+
+void
+test_unique_op_call::operator() (int)
+{}
+
+struct test_op_call
+{
+  void operator() ();
+  void operator() (int);
+  void operator() (long);
+
+  template<typename T>
+  void operator() (T *);
+};
+
+void
+test_op_call::operator() (int)
+{}
+
+void
+test_op_call::operator() ()
+{}
+
+void
+test_op_call::operator() (long)
+{}
+
+template<typename T>
+void
+test_op_call::operator() (T *t)
+{
+}
+
+/* Code for operator[] tests.  */
+
+struct test_unique_op_array
+{
+  void operator[] (int);
+};
+
+void
+test_unique_op_array::operator[] (int)
+{}
+
+struct test_op_array
+{
+  void operator[] (int);
+  void operator[] (long);
+
+  template<typename T>
+  void operator[] (T *);
+};
+
+void
+test_op_array::operator[] (int)
+{}
+
+void
+test_op_array::operator[] (long)
+{}
+
+template<typename T>
+void
+test_op_array::operator[] (T *t)
+{}
+
+/* Code for operator new tests.  */
+
+struct test_op_new
+{
+  void *operator new (size_t);
+};
+
+void *
+test_op_new::operator new (size_t)
+{
+  return NULL;
+}
+
+/* Code for operator delete tests.  */
+
+struct test_op_delete
+{
+  void operator delete (void *);
+};
+
+void
+test_op_delete::operator delete (void *)
+{
+}
+
+/* Code for operator new[] tests.  */
+
+struct test_op_new_array
+{
+  void *operator new[] (size_t);
+};
+
+void *
+test_op_new_array::operator new[] (size_t)
+{
+  return NULL;
+}
+
+/* Code for operator delete[] tests.  */
+
+struct test_op_delete_array
+{
+  void operator delete[] (void *);
+};
+
+void
+test_op_delete_array::operator delete[] (void *)
+{
+}
+
+/* Code for user-defined conversion tests.  */
+
+struct test_op_conversion_res;
+
+struct test_op_conversion
+{
+  operator const volatile test_op_conversion_res **() const volatile;
+};
+
+test_op_conversion::operator const volatile test_op_conversion_res **()
+  const volatile
+{
+  return NULL;
+}
+
+/* Code for the assignment operator tests.  */
+
+struct test_op_assign
+{
+  test_op_assign operator= (const test_op_assign &);
+};
+
+test_op_assign
+test_op_assign::operator= (const test_op_assign &)
+{
+  return test_op_assign ();
+}
+
+/* Code for the arrow operator tests.  */
+
+struct test_op_arrow
+{
+  test_op_arrow operator-> ();
+};
+
+test_op_arrow
+test_op_arrow::operator-> ()
+{
+  return test_op_arrow ();
+}
+
+/* Code for the logical/arithmetic operators tests.  */
+
+struct E
+{
+};
+
+#define GEN_OP(NS, ...)				\
+  namespace test_ops {				\
+    void operator __VA_ARGS__ {}		\
+  }						\
+  namespace test_op_ ## NS {			\
+    void operator __VA_ARGS__ {}		\
+  }
+
+GEN_OP (PLUS_A,    +=  (E, E)  )
+GEN_OP (PLUS,      +   (E, E)  )
+GEN_OP (MINUS_A,   -=  (E, E)  )
+GEN_OP (MINUS,     -   (E, E)  )
+GEN_OP (MOD_A,     %=  (E, E)  )
+GEN_OP (MOD,       %   (E, E)  )
+GEN_OP (EQ,        ==  (E, E)  )
+GEN_OP (NEQ,       !=  (E, E)  )
+GEN_OP (LAND,      &&  (E, E)  )
+GEN_OP (LOR,       ||  (E, E)  )
+GEN_OP (SL_A,      <<= (E, E)  )
+GEN_OP (SR_A,      >>= (E, E)  )
+GEN_OP (SL,        <<  (E, E)  )
+GEN_OP (SR,        >>  (E, E)  )
+GEN_OP (OE,        |=  (E, E)  )
+GEN_OP (BIT_O,     |   (E, E)  )
+GEN_OP (XOR_A,     ^=  (E, E)  )
+GEN_OP (XOR,       ^   (E, E)  )
+GEN_OP (BIT_AND_A, &=  (E, E)  )
+GEN_OP (BIT_AND,   &   (E, E)  )
+GEN_OP (LT,        <   (E, E)  )
+GEN_OP (LTE,       <=  (E, E)  )
+GEN_OP (GTE,       >=  (E, E)  )
+GEN_OP (GT,        >   (E, E)  )
+GEN_OP (MUL_A,     *=  (E, E)  )
+GEN_OP (MUL,       *   (E, E)  )
+GEN_OP (DIV_A,     /=  (E, E)  )
+GEN_OP (DIV,       /   (E, E)  )
+GEN_OP (NEG,       ~   (E)      )
+GEN_OP (NOT,       !   (E)      )
+GEN_OP (PRE_INC,   ++  (E)      )
+GEN_OP (POST_INC,  ++  (E, int) )
+GEN_OP (PRE_DEC,   --  (E)      )
+GEN_OP (POST_DEC,  --  (E, int) )
+GEN_OP (COMMA,     ,   (E, E)  )
+
+int
+main ()
+{
+  test_op_call opcall;
+  opcall ();
+  opcall (1);
+  opcall (1l);
+  opcall ((int *) 0);
+
+  test_unique_op_call opcall2;
+  opcall2 (1);
+
+  test_op_array op_array;
+  op_array[1];
+  op_array[1l];
+  op_array[(int *) 0];
+
+  test_unique_op_array unique_op_array;
+  unique_op_array[1];
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.exp b/gdb/testsuite/gdb.linespec/cpls-ops.exp
new file mode 100644
index 0000000..c1b6c33
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpls-ops.exp
@@ -0,0 +1,565 @@
+# Copyright 2017 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/>.
+
+# This file is part of the gdb testsuite.
+
+load_lib completion-support.exp
+
+standard_testfile cpls-ops.cc
+
+if {[prepare_for_testing "failed to prepare" $testfile \
+	 [list $srcfile] {debug}]} {
+    return -1
+}
+
+gdb_test_no_output "set max-completions unlimited"
+
+# Check that the explicit location completer manages to find the next
+# option name after a "-function function" option.  A useful test when
+# the -function options's argument is a C++ operator, which can
+# include characters like '-'.
+
+proc check_explicit_skips_function_argument {function} {
+    test_gdb_complete_unique \
+	"b -function $function -sour" \
+	"b -function $function -source"
+}
+
+# Helper function for the operator new/new[] tests.  CLASS_NAME is the
+# name of the class that contains the operator we're testing.
+# BRACKETS is set to [] if testing operator new[], and to empty if
+# testing operator new.
+
+proc test_operator_new {class_name brackets} {
+    # The type size_t is typedef-ed to.
+    set size_t "unsigned long"
+
+    # Complete all prefixes between "operato" and the full prototype.
+    foreach cmd_prefix {"b" "b -function"} {
+	set location "${class_name}::operator new${brackets}($size_t)"
+	set line "$cmd_prefix $location"
+	set start [index_after "operato" $line]
+	test_complete_prefix_range $line $start
+	check_bp_locations_match_list "$cmd_prefix $location" [list $location]
+
+	# Same, but with extra spaces.  Note that the original spaces in
+	# the input line are preserved after completion.
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator new " \
+	    "$cmd_prefix ${class_name}::operator new ${brackets}($size_t)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} (" \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} ($size_t)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} ( $size_t " \
+	    "$cmd_prefix ${class_name}::operator new ${brackets} ( $size_t )"
+
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator new${brackets}" \
+		 "${class_name}::operator new${brackets} ($size_t)" \
+		 "${class_name}::operator new ${brackets} ( $size_t )"]
+	foreach linespec $location_list {
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" [list $location]
+	}
+    }
+
+    # Check that the explicit location completer manages to find the
+    # option name after -function, when the -function's argument is a
+    # C++ operator new / new[].
+    check_explicit_skips_function_argument \
+	"${class_name}::operator new ${brackets} ( $size_t )"
+}
+
+proc_with_prefix operator-new {} {
+    test_operator_new test_op_new ""
+}
+
+proc_with_prefix operator-new\[\] {} {
+    test_operator_new test_op_new_array "\[\]"
+}
+
+# Helper function for the operator delete/delete[] tests.  CLASS_NAME
+# is the name of the class that contains the operator we're testing.
+# BRACKETS is set to "[]" if testing operator delete[], and to empty
+# if testing operator delete.
+
+proc test_operator_delete {class_name brackets} {
+    # Complete all prefixes between "operato" and the full prototype.
+    foreach cmd_prefix {"b" "b -function"} {
+	set location "${class_name}::operator delete${brackets}(void*)"
+	set line "$cmd_prefix $location"
+	set start [index_after "operato" $line]
+	test_complete_prefix_range $line $start
+	check_bp_locations_match_list "$cmd_prefix $location" [list $location]
+
+	# Same, but with extra spaces.  Note that the original spaces in
+	# the input line are preserved after completion.
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete " \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets}(void*)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} (" \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} (void*)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void* " \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void* )"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void * " \
+	    "$cmd_prefix ${class_name}::operator delete ${brackets} ( void * )"
+
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator delete${brackets}" \
+		 "${class_name}::operator delete${brackets}(void *)" \
+		 "${class_name}::operator delete ${brackets} ( void * )"]
+	foreach linespec $location_list {
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" [list $location]
+	}
+    }
+
+    # Check that the explicit location completer manages to find the
+    # option name after -function, when the -function's argument is a
+    # C++ operator delete / delete[].
+    check_explicit_skips_function_argument \
+	"${class_name}::operator delete ${brackets} ( void * )"
+}
+
+proc_with_prefix operator-delete {} {
+    test_operator_delete test_op_delete ""
+}
+
+proc_with_prefix operator-delete\[\] {} {
+    test_operator_delete test_op_delete_array "\[\]"
+}
+
+# Helper for testing both operator() and operator[].  Tests completion
+# when the operator match is unique.  CLASS_NAME is the class that
+# holds the operator to test.  OPN and CLS are the open and close
+# characters ("()" or "[]").
+
+proc test_operator_unique {class_name opn cls} {
+    # Complete all prefixes between "oper" and the full prototype.
+    foreach cmd_prefix {"b" "b -function"} {
+	set location "${class_name}::operator${opn}${cls}(int)"
+	set line "$cmd_prefix $location"
+	set start [index_after "${class_name}" $line]
+	test_complete_prefix_range $line $start
+	check_bp_locations_match_list "$cmd_prefix $location" [list $location]
+
+	# Same, but with extra spaces.  Note that the original spaces in
+	# the input line are preserved after completion.
+
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int " \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int )"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls}" \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls}(int)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn}${cls}" \
+	    "$cmd_prefix ${class_name}::operator ${opn}${cls}(int)"
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn}" \
+	    "$cmd_prefix ${class_name}::operator ${opn}${cls}(int)"
+
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator${opn}${cls}" \
+		 "${class_name}::operator ${opn}${cls}" \
+		 "${class_name}::operator ${opn}${cls}(int)" \
+		 "${class_name}::operator ${opn} ${cls} ( int )"]
+	foreach linespec $location_list {
+	    check_bp_locations_match_list \
+		"$cmd_prefix $linespec" [list $location]
+	}
+    }
+
+    # Check that the explicit location completer manages to find the
+    # option name after -function, when the -function's argument is a
+    # C++ operator().
+    check_explicit_skips_function_argument \
+	"${class_name}::operator ${opn} ${cls} ( int )"
+}
+
+# Helper for testing both operator() and operator[].  Tests completion
+# when the operator match is ambiguous.  CLASS_NAME is the class that
+# holds the operator to test.  OPN and CLS are the open and close
+# characters ("()" or "[]").
+
+proc test_operator_ambiguous {class_name opn cls} {
+    foreach cmd_prefix {"b" "b -function"} {
+	check_setting_bp_fails "$cmd_prefix ${class_name}::operator"
+
+	set linespec_noparams "${class_name}::operator${opn}${cls}"
+
+	set location_list \
+	    [list \
+		 "${class_name}::operator${opn}${cls}(int)" \
+		 "${class_name}::operator${opn}${cls}(long)" \
+		 "${class_name}::operator${opn}${cls}<int>(int*)"]
+	# The operator[] test can't have a "()" overload, since that
+	# wouldn't compile.
+	if {$opn == "("} {
+	    set location_list \
+		[concat \
+		     [list "${class_name}::operator${opn}${cls}()"] \
+		     $location_list]
+	}
+	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>" \
+	    [list "${class_name}::operator${opn}${cls}<int>(int*)"]
+
+	# 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*)"]
+
+	# Add extra spaces.
+	test_gdb_complete_unique \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( in" \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int)"
+	check_bp_locations_match_list \
+	    "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int )" \
+	    [list "${class_name}::operator${opn}${cls}(int)"]
+    }
+}
+
+proc_with_prefix operator()-unique {} {
+    test_operator_unique test_unique_op_call "(" ")"
+}
+
+proc_with_prefix operator\[\]-unique {} {
+    test_operator_unique test_unique_op_array "\[" "\]"
+}
+
+proc_with_prefix operator()-ambiguous {} {
+    test_operator_ambiguous test_op_call "(" ")"
+}
+
+proc_with_prefix operator\[\]-ambiguous {} {
+    test_operator_ambiguous test_op_array "\[" "\]"
+}
+
+# Test arithmetic/logical operators.  Test completing all C++
+# arithmetic/logical operators, when all the operators are in the same
+# class.
+
+proc_with_prefix ops-valid-ambiguous {} {
+    set locations {
+	"test_ops::operator!(E)"
+	"test_ops::operator!=(E, E)"
+	"test_ops::operator%(E, E)"
+	"test_ops::operator%=(E, E)"
+	"test_ops::operator&&(E, E)"
+	"test_ops::operator&(E, E)"
+	"test_ops::operator&=(E, E)"
+	"test_ops::operator*(E, E)"
+	"test_ops::operator*=(E, E)"
+	"test_ops::operator+(E, E)"
+	"test_ops::operator++(E)"
+	"test_ops::operator++(E, int)"
+	"test_ops::operator+=(E, E)"
+	"test_ops::operator,(E, E)"
+	"test_ops::operator-(E, E)"
+	"test_ops::operator--(E)"
+	"test_ops::operator--(E, int)"
+	"test_ops::operator-=(E, E)"
+	"test_ops::operator/(E, E)"
+	"test_ops::operator/=(E, E)"
+	"test_ops::operator<(E, E)"
+	"test_ops::operator<<(E, E)"
+	"test_ops::operator<<=(E, E)"
+	"test_ops::operator<=(E, E)"
+	"test_ops::operator==(E, E)"
+	"test_ops::operator>(E, E)"
+	"test_ops::operator>=(E, E)"
+	"test_ops::operator>>(E, E)"
+	"test_ops::operator>>=(E, E)"
+	"test_ops::operator^(E, E)"
+	"test_ops::operator^=(E, E)"
+	"test_ops::operator|(E, E)"
+	"test_ops::operator|=(E, E)"
+	"test_ops::operator||(E, E)"
+	"test_ops::operator~(E)"
+    }
+    foreach linespec $locations {
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_unique \
+		"$cmd_prefix $linespec" \
+		"$cmd_prefix $linespec"
+
+	}
+
+	check_explicit_skips_function_argument "$linespec"
+    }
+
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_multiple \
+	    "$cmd_prefix " "test_ops::operator" "" $locations
+    }
+}
+
+# Test completing all C++ operators, with and without spaces.  The
+# test without spaces makes sure the completion matches exactly the
+# expected prototype.  The version with whitespace is a bit more lax
+# for simplicity.  In that case, we only make sure we get back the
+# terminating ')'.  Each operator is defined in a separate class so
+# that we can exercise unique completion matches.
+
+proc_with_prefix ops-valid-unique {} {
+    set locations {
+	"test_op_BIT_AND::operator&(E, E)"
+	"test_op_BIT_AND_A::operator&=(E, E)"
+	"test_op_BIT_O::operator|(E, E)"
+	"test_op_COMMA::operator,(E, E)"
+	"test_op_DIV::operator/(E, E)"
+	"test_op_DIV_A::operator/=(E, E)"
+	"test_op_EQ::operator==(E, E)"
+	"test_op_GT::operator>(E, E)"
+	"test_op_GTE::operator>=(E, E)"
+	"test_op_LAND::operator&&(E, E)"
+	"test_op_LOR::operator||(E, E)"
+	"test_op_LT::operator<(E, E)"
+	"test_op_LTE::operator<=(E, E)"
+	"test_op_MINUS::operator-(E, E)"
+	"test_op_MINUS_A::operator-=(E, E)"
+	"test_op_MOD::operator%(E, E)"
+	"test_op_MOD_A::operator%=(E, E)"
+	"test_op_MUL::operator*(E, E)"
+	"test_op_MUL_A::operator*=(E, E)"
+	"test_op_NEG::operator~(E)"
+	"test_op_NEQ::operator!=(E, E)"
+	"test_op_NOT::operator!(E)"
+	"test_op_OE::operator|=(E, E)"
+	"test_op_PLUS::operator+(E, E)"
+	"test_op_PLUS_A::operator+=(E, E)"
+	"test_op_POST_DEC::operator--(E, int)"
+	"test_op_POST_INC::operator++(E, int)"
+	"test_op_PRE_DEC::operator--(E)"
+	"test_op_PRE_INC::operator++(E)"
+	"test_op_SL::operator<<(E, E)"
+	"test_op_SL_A::operator<<=(E, E)"
+	"test_op_SR::operator>>(E, E)"
+	"test_op_SR_A::operator>>=(E, E)"
+	"test_op_XOR::operator^(E, E)"
+	"test_op_XOR_A::operator^=(E, E)"
+    }
+    set linespecs_ws {
+	"test_op_BIT_AND::operator & ( E , E )"
+	"test_op_BIT_AND_A::operator &= ( E , E )"
+	"test_op_BIT_O::operator | (E , E )"
+	"test_op_COMMA::operator , ( E , E )"
+	"test_op_DIV::operator / (E , E )"
+	"test_op_DIV_A::operator /= ( E , E )"
+	"test_op_EQ::operator == ( E , E )"
+	"test_op_GT::operator > ( E , E )"
+	"test_op_GTE::operator >= ( E , E )"
+	"test_op_LAND::operator && ( E , E )"
+	"test_op_LOR::operator || ( E , E )"
+	"test_op_LT::operator < ( E , E )"
+	"test_op_LTE::operator <= ( E , E )"
+	"test_op_MINUS::operator - ( E , E )"
+	"test_op_MINUS_A::operator -= ( E , E )"
+	"test_op_MOD::operator % ( E , E )"
+	"test_op_MOD_A::operator %= ( E , E )"
+	"test_op_MUL::operator * ( E , E )"
+	"test_op_MUL_A::operator *= ( E , E )"
+	"test_op_NEG::operator ~ ( E )"
+	"test_op_NEQ::operator != ( E , E )"
+	"test_op_NOT::operator ! ( E )"
+	"test_op_OE::operator |= ( E , E )"
+	"test_op_PLUS::operator + ( E , E )"
+	"test_op_PLUS_A::operator += ( E , E )"
+	"test_op_POST_DEC::operator -- ( E , int )"
+	"test_op_POST_INC::operator ++ ( E , int )"
+	"test_op_PRE_DEC::operator -- ( E )"
+	"test_op_PRE_INC::operator ++ ( E )"
+	"test_op_SL::operator << ( E , E )"
+	"test_op_SL_A::operator <<= ( E , E )"
+	"test_op_SR::operator >> ( E , E )"
+	"test_op_SR_A::operator >>= ( E , E )"
+	"test_op_XOR::operator ^ ( E , E )"
+	"test_op_XOR_A::operator ^= ( E , E )"
+    }
+    foreach linespec $locations linespec_ws $linespecs_ws {
+	foreach cmd_prefix {"b" "b -function"} {
+	    with_test_prefix "no-whitespace" {
+		set line "$cmd_prefix $linespec"
+		set start [index_after "::operato" $line]
+		test_complete_prefix_range $line $start
+	    }
+
+	    with_test_prefix "whitespace" {
+		set line_ws "$cmd_prefix $linespec_ws"
+		set start_ws [index_after "::operator " $line_ws]
+		test_complete_prefix_range_re \
+		    $line_ws "$cmd_prefix test_op_.*::operator .*\\\)" $start_ws
+	    }
+	}
+
+	check_explicit_skips_function_argument "$linespec"
+	check_explicit_skips_function_argument "$linespec_ws"
+    }
+}
+
+# Test completing an invalid (whitespace at the wrong place) operator
+# name.
+
+proc_with_prefix ops-invalid {} {
+    foreach linespec {
+	"test_op_BIT_AND_A::operator& =(E, E)"
+	"test_op_DIV_A::operator/ =(E, E)"
+	"test_op_EQ::operator= =(E, E)"
+	"test_op_GTE::operator> =(E, E)"
+	"test_op_LAND::operator& &(E, E)"
+	"test_op_LOR::operator| |(E, E)"
+	"test_op_LTE::operator< =(E, E)"
+	"test_op_MINUS_A::operator- =(E, E)"
+	"test_op_MOD_A::operator% =(E, E)"
+	"test_op_MUL_A::operator* =(E, E)"
+	"test_op_NEQ::operator! =(E, E)"
+	"test_op_OE::operator| =(E, E)"
+	"test_op_PLUS_A::operator+ =(E, E)"
+	"test_op_POST_DEC::operator- -(E, int)"
+	"test_op_POST_INC::operator+ +(E, int)"
+	"test_op_PRE_DEC::operator- -(E)"
+	"test_op_PRE_INC::operator+ +(E)"
+	"test_op_SL::operator< <(E, E)"
+	"test_op_SL_A::operator< < =(E, E)"
+	"test_op_SR::operator> >(E, E)"
+	"test_op_SR_A::operator> > =(E, E)"
+	"test_op_XOR_A::operator^ =(E, E)"
+    } {
+	foreach cmd_prefix {"b" "b -function"} {
+	    test_gdb_complete_tab_none "$cmd_prefix $linespec"
+	    check_setting_bp_fails "$cmd_prefix $linespec"
+	}
+    }
+}
+
+# Test completing function/method FUNCTION.  Completion is tested at
+# every point starting after START_AFTER.  FUNCTION_WS is a version of
+# FUNCTION with extra (but valid) whitespace.  FUNCTION_INVALID is a
+# version of FUNCTION with invalid whitespace.  Tests that completion
+# of FUNCTION_WS completes to self, and that a completion of
+# FUNCTION_INVALID fails.
+
+proc test_function {function start_after function_ws {function_invalid ""}} {
+    foreach cmd_prefix {"b" "b -function"} {
+	set line "$cmd_prefix $function"
+	set start [index_after $start_after $line]
+	test_complete_prefix_range $line $start
+    }
+
+    check_explicit_skips_function_argument $function
+    check_explicit_skips_function_argument $function_ws
+
+    foreach cmd_prefix {"b" "b -function"} {
+	test_gdb_complete_unique \
+	    "$cmd_prefix $function_ws" \
+	    "$cmd_prefix $function_ws"
+	if {$function_invalid != ""} {
+	    test_gdb_complete_tab_none "$cmd_prefix $function_invalid"
+	    check_setting_bp_fails "$cmd_prefix $function_invalid"
+	}
+    }
+}
+
+# Test completing a user-defined conversion operator.
+
+proc_with_prefix conversion-operator {} {
+    test_function \
+	"test_op_conversion::operator test_op_conversion_res const volatile**() const volatile" \
+	"test_op_conversio" \
+	"test_op_conversion::operator test_op_conversion_res const volatile * * ( ) const volatile"}
+
+# Test completing an assignment operator.
+
+proc_with_prefix assignment-operator {} {
+    test_function \
+	"test_op_assign::operator=(test_op_assign const&)" \
+	"test_op_assig" \
+	"test_op_assign::operator = ( test_op_assign const & )" \
+}
+
+# Test completing an arrow operator.
+
+proc_with_prefix arrow-operator {} {
+    test_function \
+	"test_op_arrow::operator->()" \
+	"test_op_arro" \
+	"test_op_arrow::operator -> ( )" \
+	"test_op_arrow::operator - > ( )"
+}
+
+# The testcase driver.  Calls all test procedures.
+
+proc test_driver {} {
+    operator-delete
+    operator-delete\[\]
+    operator-new
+    operator-new\[\]
+    operator()-unique
+    operator()-ambiguous
+    operator\[\]-unique
+    operator\[\]-ambiguous
+    ops-valid-ambiguous
+    ops-valid-unique
+    ops-invalid
+    conversion-operator
+    assignment-operator
+    arrow-operator
+}
+
+test_driver
diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
index 70e6aec..25332cc 100644
--- a/gdb/testsuite/lib/completion-support.exp
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -259,21 +259,30 @@ proc test_gdb_complete_multiple { cmd_prefix completion_word add_completed_line
     test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char
 }
 
-# Test that all the substring prefixes of COMPLETION from [0..START)
-# to [0..END) complete to COMPLETION.  If END is ommitted, default to
-# the length of COMPLETION.
+# Test that all the substring prefixes of INPUT from [0..START) to
+# [0..END) complete to COMPLETION_RE (a regular expression).  If END
+# is ommitted, default to the length of INPUT.
 
-proc test_complete_prefix_range {completion start {end -1}} {
+proc test_complete_prefix_range_re {input completion_re start {end -1}} {
     if {$end == -1} {
-	set end [string length $completion]
+	set end [string length $input]
     }
 
     for {set i $start} {$i < $end} {incr i} {
-	set line [string range $completion 0 $i]
-	test_gdb_complete_unique "$line" "$completion"
+	set line [string range $input 0 $i]
+	test_gdb_complete_unique_re "$line" $completion_re
     }
 }
 
+# Test that all the substring prefixes of COMPLETION from [0..START)
+# to [0..END) complete to COMPLETION.  If END is ommitted, default to
+# the length of COMPLETION.
+
+proc test_complete_prefix_range {completion start {end -1}} {
+    set completion_re [string_to_regexp $completion]
+    test_complete_prefix_range_re $completion $completion_re $start $end
+}
+
 # Find NEEDLE in HAYSTACK and return the index _after_ NEEDLE.  E.g.,
 # searching for "(" in "foo(int)" returns 4, which would be useful if
 # you want to find the "(" to try completing "foo(".
-- 
2.5.5

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

* Re: [PATCH 37/40] Fix completing an empty string
  2017-08-09 18:01   ` Keith Seitz
@ 2017-11-25  0:28     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-25  0:28 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/09/2017 07:00 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>> gdb/testsuite/ChangeLog:
>> yyyy-mm-dd   Pedro Alves  <palves@redhat.com>
>>
>> 	* completer.c (complete_line_internal_1): Skip spaces until the
>> 	start of the command.
>> 	* gdb.base/complete-empty.exp: New file.
>> 	* gdb.base/completion.exp: Adjust.
> 
> Looks good!

This works against master too, so I pushed it in too!

Most of the series is in now, and we're down to the
wildmatching and the abitags parts.

Thanks,
Pedro Alves

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

* [pushed] Re: [PATCH 38/40] Use TOLOWER in SYMBOL_HASH_NEXT
  2017-08-09 19:25   ` Keith Seitz
@ 2017-11-25  0:35     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-25  0:35 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/09/2017 08:24 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>>      * dictionary.c: Include "safe-ctype.h".
>>      * minsyms.c: Include "safe-ctype.h".
>>      * minsyms.c (SYMBOL_HASH_NEXT): Use TOLOWER instead of tolower.
> 
> Looks good!

Since this improves performance a little bit, I guess I could say
it stands on is own, so I went ahead and pushed it in already,
as below.

From deeeba559bd0c18e06dba19f44571ee8a218fcdb Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Sat, 25 Nov 2017 00:33:05 +0000
Subject: [PATCH] Use TOLOWER in SYMBOL_HASH_NEXT
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The support for setting breakpoint in functions with ABI tags patch
will add a use of SYMBOL_HASH_NEXT in cp-support.c, which fails to
compile with:

  src/gdb/cp-support.c:38:0:
  src/gdb/cp-support.c: In function ‘unsigned int cp_search_name_hash(const char*)’:
  src/gdb/../include/safe-ctype.h:148:20: error: ‘do_not_use_tolower_with_safe_ctype’ was not declared in this scope
   #define tolower(c) do_not_use_tolower_with_safe_ctype
		      ^
  src/gdb/minsyms.h:174:18: note: in expansion of macro ‘tolower’
     ((hash) * 67 + tolower ((unsigned char) (c)) - 113)
		    ^
  src/gdb/cp-support.c:1677:14: note: in expansion of macro ‘SYMBOL_HASH_NEXT’
	 hash = SYMBOL_HASH_NEXT (hash, *string);
		^

This fixes the problem before it happens.

I was somewhat worried about whether this might have an impact with
languages that are case insensitive, but I convinced myself that it
doesn't.  As bonus, this improves GDB's minsym interning performance a
bit (3%-10%).  See
<https://sourceware.org/ml/gdb/2017-11/msg00021.html>.

gdb/ChangeLog:
2017-11-25  Pedro Alves  <palves@redhat.com>

	* dictionary.c: Include "safe-ctype.h".
	* minsyms.c: Include "safe-ctype.h".
	* minsyms.c (SYMBOL_HASH_NEXT): Use TOLOWER instead of tolower.
---
 gdb/ChangeLog    | 6 ++++++
 gdb/dictionary.c | 1 +
 gdb/minsyms.c    | 1 +
 gdb/minsyms.h    | 2 +-
 4 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 4e0b45e..61db1b1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,9 @@
+2017-11-25  Pedro Alves  <palves@redhat.com>
+
+	* dictionary.c: Include "safe-ctype.h".
+	* minsyms.c: Include "safe-ctype.h".
+	* minsyms.c (SYMBOL_HASH_NEXT): Use TOLOWER instead of tolower.
+
 2017-11-25   Pedro Alves  <palves@redhat.com>
 
 	* completer.c (complete_line_internal_1): Skip spaces until the
diff --git a/gdb/dictionary.c b/gdb/dictionary.c
index fb307538..7d26efb 100644
--- a/gdb/dictionary.c
+++ b/gdb/dictionary.c
@@ -26,6 +26,7 @@
 #include "symtab.h"
 #include "buildsym.h"
 #include "dictionary.h"
+#include "safe-ctype.h"
 
 /* This file implements dictionaries, which are tables that associate
    symbols to names.  They are represented by an opaque type 'struct
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index a0d3bd5..4898da1 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -52,6 +52,7 @@
 #include "cli/cli-utils.h"
 #include "symbol.h"
 #include <algorithm>
+#include "safe-ctype.h"
 
 /* See minsyms.h.  */
 
diff --git a/gdb/minsyms.h b/gdb/minsyms.h
index f326be9..5c0dde4 100644
--- a/gdb/minsyms.h
+++ b/gdb/minsyms.h
@@ -177,7 +177,7 @@ unsigned int msymbol_hash_iw (const char *);
    requirements.  */
 
 #define SYMBOL_HASH_NEXT(hash, c)			\
-  ((hash) * 67 + tolower ((unsigned char) (c)) - 113)
+  ((hash) * 67 + TOLOWER ((unsigned char) (c)) - 113)
 
 \f
 
-- 
2.5.5

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

* Re: [PATCH 31/40] Handle custom completion match prefix / LCD
  2017-08-08 21:28   ` Keith Seitz
@ 2017-11-27 17:11     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-27 17:11 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/08/2017 10:27 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
> 
>> diff --git a/gdb/completer.h b/gdb/completer.h
>> index df90822..db781ab 100644
>> --- a/gdb/completer.h
>> +++ b/gdb/completer.h
>> @@ -116,12 +116,44 @@ private:
>>    std::string m_storage;
>>  };
>>  
>> +/* The result of a successful completion match, but for least common
>> +   denominator (LCD) computation.  Some completers provide matches
>> +   that don't start with the completion "word".  E.g., completing on
>> +   "b push_ba" on a C++ program usually completes to
>> +   std::vector<...>::push_back, std::string::push_back etc.  In such
>> +   case, the symbol comparison routine will set the LCD match to point
>> +   into the "push_back" substring within the symbol's name string.  */
> 
> [missing newline?]

Fixed.

> 
>> +class completion_match_for_lcd
>> +{
>> +public:
>> +  /* Set the match for LCD.  See m_match's description.  */
>> +  void set_match (const char *match)
>> +  { m_match = match; }
>> +
>> +  /* Get the resulting LCD, after a successful match.  */
>> +  const char *finish ()
>> +  { return m_match; }
>> +
>> +  /* Prepare for another completion matching sequence.  */
>> +  void clear ()
>> +  { m_match = NULL; }
>> +
>> +private:
>> +  /* The completion match result for LCD.  This is usually either a
>> +     pointer into to a substring within a symbol's name, or to the
>> +     storage of the pairing completion_match object.  */
>> +  const char *m_match;
>> +};
>> +
>>  /* Convenience aggregate holding info returned by the symbol name
>>     matching routines (see symbol_name_matcher_ftype).  */
>>  struct completion_match_result
>>  {
>>    /* The completion match candidate.  */
>>    completion_match match;
>> +
>> +  /* The completion match, for LCD computation purposes.  */
>> +  completion_match_for_lcd match_for_lcd;
>>  };
>>  
>>  /* The final result of a completion that is handed over to either
>> diff --git a/gdb/cp-support.c b/gdb/cp-support.c
>> index 064a45b..397c738 100644
>> --- a/gdb/cp-support.c
>> +++ b/gdb/cp-support.c
>> @@ -1660,8 +1660,11 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
>>  			    name.c_str (), name.size (),
>>  			    mode) == 0)
>>      {
>> -      if (match != NULL)
>> -	match->set_match (symbol_search_name);
>> +      if (comp_match_res != NULL)
>> +	{
>> +	  comp_match_res->match.set_match (symbol_search_name);
>> +	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
>> +	}
>>        return true;
>>      }
>>  
>> diff --git a/gdb/language.c b/gdb/language.c
>> index 6705a3b..511a86f 100644
>> --- a/gdb/language.c
>> +++ b/gdb/language.c
>> @@ -725,8 +725,11 @@ default_symbol_name_matcher (const char *symbol_search_name,
>>    if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (),
>>  			    mode) == 0)
>>      {
>> -      if (match != NULL)
>> -	match->set_match (symbol_search_name);
>> +      if (comp_match_res != NULL)
>> +	{
>> +	  comp_match_res->match.set_match (symbol_search_name);
>> +	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
>> +	}
>>        return true;
>>      }
>>    else
> 
> In the above two hunks, the code calls result->match.set_match (A)
> and result->match_for_lcd.set_match (A), i.e., both these calls take the
> same argument and are called at the same time.
> 
> Furthermore, on the final series of the patch,
> 
> $ grep -n set_match\ \( *.c
> ada-lang.c:6416:	  comp_match_res->match.set_match (match_str.c_str ());
> ada-lang.c:6417:	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
> ada-lang.c:6426:	  comp_match_res->match.set_match (match_str.c_str ());
> ada-lang.c:6427:	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
> cp-support.c:1734:	      comp_match_res->match.set_match (symbol_search_name);
> cp-support.c:1735:	      comp_match_res->match_for_lcd.set_match (sname);
> cp-support.c:1773:	  comp_match_res->match.set_match (symbol_search_name);
> cp-support.c:1774:	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
> language.c:725:	  comp_match_res->match.set_match (symbol_search_name);
> language.c:726:	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
> 
> It appears that these two are /always/ called together, suggesting that
> completion_match_result might "benefit" from a convenience method to set both
> of these parameters at the same time. [Unless there is a specific reason
> not to do this, of course.] WDYT?

OK, I'm folding the below into the series.  (I'm posting a v2 in a bit.)

From cc3d60557be486a0eb317e6920ccb323a76617c8 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Mon, 27 Nov 2017 12:26:31 +0000
Subject: [PATCH] set_match

---
 gdb/ada-lang.c   | 10 +++-------
 gdb/completer.h  | 11 +++++++++++
 gdb/cp-support.c | 29 +++++++++++++++++++++++------
 gdb/language.c   |  5 +----
 4 files changed, 38 insertions(+), 17 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index cad9d7c..38d1ce6 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6420,11 +6420,7 @@ ada_lookup_name_info::matches
       std::string &match_str = comp_match_res->match.storage ();
 
       if (!m_encoded_p)
-	{
-	  match_str = ada_decode (sym_name);
-	  comp_match_res->match.set_match (match_str.c_str ());
-	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
-	}
+	match_str = ada_decode (sym_name);
       else
 	{
 	  if (m_verbatim_p)
@@ -6432,9 +6428,9 @@ ada_lookup_name_info::matches
 	  else
 	    match_str = sym_name;
 
-	  comp_match_res->match.set_match (match_str.c_str ());
-	  comp_match_res->match_for_lcd.set_match (match_str.c_str ());
 	}
+
+      comp_match_res->set_match (match_str.c_str ());
     }
 
   return true;
diff --git a/gdb/completer.h b/gdb/completer.h
index 97611e4..f756412 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -155,6 +155,17 @@ struct completion_match_result
 
   /* The completion match, for LCD computation purposes.  */
   completion_match_for_lcd match_for_lcd;
+
+  /* Convenience that sets both MATCH and MATCH_FOR_LCD.  M_FOR_LCD is
+     optional.  If not specified, defaults to M.  */
+  void set_match (const char *m, const char *m_for_lcd = NULL)
+  {
+    match.set_match (m);
+    if (m_for_lcd == NULL)
+      match_for_lcd.set_match (m);
+    else
+      match_for_lcd.set_match (m_for_lcd);
+  }
 };
 
 /* The final result of a completion that is handed over to either
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 4c78af8..172d821 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1682,8 +1682,28 @@ cp_symbol_name_matches_1 (const char *symbol_search_name,
 	{
 	  if (comp_match_res != NULL)
 	    {
-	      comp_match_res->match.set_match (symbol_search_name);
-	      comp_match_res->match_for_lcd.set_match (sname);
+	      /* Note here we set different MATCH and MATCH_FOR_LCD
+		 strings.  This is because with
+
+		  (gdb) b push_bac[TAB]
+
+		 we want the completion matches to list
+
+		  std::vector<int>::push_back(...)
+		  std::vector<char>::push_back(...)
+
+		 etc., which are SYMBOL_SEARCH_NAMEs, while we want
+		 the input line to auto-complete to
+
+		  (gdb) push_back(...)
+
+		 which is SNAME, not to
+
+		  (gdb) std::vector<
+
+		 which would be the regular common prefix between all
+		 the matches otherwise.  */
+	      comp_match_res->set_match (symbol_search_name, sname);
 	    }
 	  return true;
 	}
@@ -1718,10 +1738,7 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
 			    mode, language_cplus) == 0)
     {
       if (comp_match_res != NULL)
-	{
-	  comp_match_res->match.set_match (symbol_search_name);
-	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
-	}
+	comp_match_res->set_match (symbol_search_name);
       return true;
     }
 
diff --git a/gdb/language.c b/gdb/language.c
index 64ef7e0..c05b703 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -716,10 +716,7 @@ default_symbol_name_matcher (const char *symbol_search_name,
 			    mode, language_minimal) == 0)
     {
       if (comp_match_res != NULL)
-	{
-	  comp_match_res->match.set_match (symbol_search_name);
-	  comp_match_res->match_for_lcd.set_match (symbol_search_name);
-	}
+	comp_match_res->set_match (symbol_search_name);
       return true;
     }
   else
-- 
2.5.5


> 
>> diff --git a/gdb/symtab.h b/gdb/symtab.h
>> index d68eed8..736fea0 100644
>> --- a/gdb/symtab.h
>> +++ b/gdb/symtab.h
>> @@ -292,15 +292,21 @@ private:
>>  
>>     SYMBOL_SEARCH_NAME should be a symbol's "search" name.
>>  
>> -   On success and if non-NULL, MATCH is set to point to the symbol
>> -   name as should be presented to the user as a completion match list
>> -   element.  In most languages, this is the same as the symbol's
>> -   search name, but in some, like Ada, the display name is dynamically
>> -   computed within the comparison routine.  */
>> +   On success and if non-NULL, COMP_MATCH_RES->match is set to point
>> +   to the symbol name as should be presented to the user as a
>> +   completion match list element.  In most languages, this is the same
>> +   as the symbol's search name, but in some, like Ada, the display
>> +   name is dynamically computed within the comparison routine.
>> +
>> +   Also, on success and if non-NULL, COMP_MATCH_RES->match_for_lcd
>> +   points the part of SYMBOL_SEARCH_NAME that was considered to match
>             ^
> missing "to"

Fixed, thanks.

-- 
Pedro Alves

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

* Re: [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436)
  2017-08-09 19:34   ` Keith Seitz
@ 2017-11-27 17:14     ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-27 17:14 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 08/09/2017 08:34 PM, Keith Seitz wrote:
> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>> gdb/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* completer.h (completion_match_for_lcd) <match,
>> 	mark_ignored_range>: New methods.
>> 	<finish>: Consider ignored ranges.
>> 	<clear>: Clear ignored ranges.
>> 	<m_ignored_ranges, m_finished_storage>: New fields.
>> 	* cp-support.c (cp_search_name_hash): Ignore ABI tags.
>> 	(cp_symbol_name_matches_1, cp_fq_symbol_name_matches): Pass the
>> 	completion_match_for_lcd pointer to strncmp_iw_with_mode.
>> 	(test_cp_symbol_name_cmp): Add [abi:...] tags unit tests.
>> 	* language.c (default_symbol_name_matcher): Pass the
>> 	completion_match_for_lcd pointer to strncmp_iw_with_mode.
>> 	* linespec.c (linespec_lexer_lex_string): Don't tokenize ABI tags.
>> 	* utils.c (skip_abi_tag): New function.
>> 	(strncmp_iw_with_mode): Add completion_match_for_lcd parameter.
>> 	Handle ABI tags.
>> 	* utils.h (strncmp_iw_with_mode): Add completion_match_for_lcd
>> 	parameter.
>>
>> gdb/testsuite/ChangeLog:
>> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
>>
>> 	* gdb.linespec/cpls-abi-tag.cc: New file.
>> 	* gdb.linespec/cpls-abi-tag.exp: New file.
> 
> Just one little thing: the PR# isn't mentioned in the ChangeLogs.

Thanks Keith.  I've fixed that in v2.

> [This is also c++/18601, but I've marked that as a dup of this bug which is
> discussed more.]

Thanks,
Pedro Alves

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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-11-22 16:48     ` Pedro Alves
  2017-11-24 16:48       ` Pedro Alves
@ 2017-11-28  0:02       ` Keith Seitz
  2017-11-28  0:21         ` Pedro Alves
  1 sibling, 1 reply; 183+ messages in thread
From: Keith Seitz @ 2017-11-28  0:02 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 11/22/2017 08:48 AM, Pedro Alves wrote:
> On 08/09/2017 12:48 AM, Keith Seitz wrote:
>> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>
>> I think this would read better if it read: "This behavior may be overridden
>> by using the \"-qualified\" flag and specifying a fully qualified name."
>> [I am not a fan of using informal writing in documentation.]
> 
> How about the even simpler:
> 
> @@ -15295,7 +15295,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
>  syntax to specify location parameters.\n\
>  Example: To specify the start of the label named \"the_top\" in the\n\
>  function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
> --function fact -label the_top\".\n"
> +-function fact -label the_top\".\n\
> +For C++, \"-function\" matches functions and methods by name, ignoring\n\
> +missing leading specifiers (namespaces and classes).\n\
> +\"-qualified\" matches functions and methods by fully qualified name.\n"
>  

Simple is good!

>>>  /* This help string is used for the break, hbreak, tbreak and thbreak
>>>     commands.  It is defined as a macro to prevent duplication.
>>> diff --git a/gdb/completer.c b/gdb/completer.c
>>> index eabbce7..99e40a3 100644
>>> --- a/gdb/completer.c
>>> +++ b/gdb/completer.c
>>> @@ -609,6 +612,7 @@ static const char *const explicit_options[] =
>>>    {
>>>      "-source",
>>>      "-function",
>>> +    "-qualified",
>>>      "-line",
>>>      "-label",
>>>      NULL
>>
>> The "-qualified" option can be used with linespecs, too, right?
> 
> Not really, no.
> 

If I've read my catch-up mail correctly, there's been a change of plan here.
So I'll just respond to the relevant parts not addressed in follow-ups.
If I've missed something, don't hesitate to point them out to me. [You know
where to find me.]

> Do you see "-qualified" being an alternative to "-function"
> instead of a flag as a blocker?
> 
> Please let me know.

I don't think this is relevant anymore, but just in case: Do *not* delay the
next release for this. 8.1 absolutely *needs* this patch set.

> Here's the current/updated patch.

That all looks okay to me. [TBH, I've just diffed this with the previous.] I will look to the follow-on immediately.

Keith

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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-11-28  0:02       ` Keith Seitz
@ 2017-11-28  0:21         ` Pedro Alves
  2017-11-28  0:42           ` Keith Seitz
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-28  0:21 UTC (permalink / raw)
  To: Keith Seitz, gdb-patches

On 11/28/2017 12:01 AM, Keith Seitz wrote:
> On 11/22/2017 08:48 AM, Pedro Alves wrote:
>> On 08/09/2017 12:48 AM, Keith Seitz wrote:
>>> On 06/02/2017 05:22 AM, Pedro Alves wrote:
>>>
>>> I think this would read better if it read: "This behavior may be overridden
>>> by using the \"-qualified\" flag and specifying a fully qualified name."
>>> [I am not a fan of using informal writing in documentation.]
>>
>> How about the even simpler:
>>
>> @@ -15295,7 +15295,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
>>  syntax to specify location parameters.\n\
>>  Example: To specify the start of the label named \"the_top\" in the\n\
>>  function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
>> --function fact -label the_top\".\n"
>> +-function fact -label the_top\".\n\
>> +For C++, \"-function\" matches functions and methods by name, ignoring\n\
>> +missing leading specifiers (namespaces and classes).\n\
>> +\"-qualified\" matches functions and methods by fully qualified name.\n"
>>  
> 
> Simple is good!

:-)  If you look at v2, you'll notice that that sentence was completely
rewritten though.  :-P

> If I've read my catch-up mail correctly, there's been a change of plan here.

Yup.  I've implemented your suggestion and posted it today:
  https://sourceware.org/ml/gdb-patches/2017-11/msg00689.html

Actually, I've just now posted a v2.1 to fix "save breakpoints":
  https://sourceware.org/ml/gdb-patches/2017-11/msg00709.html
so make sure to read that one instead.

So if you could skim that, and let me know if I've addressed
everything, I'd appreciate it.

> So I'll just respond to the relevant parts not addressed in follow-ups.
> If I've missed something, don't hesitate to point them out to me. [You know
> where to find me.]
> 
>> Do you see "-qualified" being an alternative to "-function"
>> instead of a flag as a blocker?
>>
>> Please let me know.
> 
> I don't think this is relevant anymore, but just in case: Do *not* delay the
> next release for this. 8.1 absolutely *needs* this patch set.
> 
>> Here's the current/updated patch.
> 
> That all looks okay to me. [TBH, I've just diffed this with the previous.] I will look to the follow-on immediately.

Thanks!

-- 
Pedro Alves

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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-11-24 16:48       ` Pedro Alves
  2017-11-24 16:57         ` Pedro Alves
@ 2017-11-28  0:39         ` Keith Seitz
  1 sibling, 0 replies; 183+ messages in thread
From: Keith Seitz @ 2017-11-28  0:39 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 11/24/2017 08:48 AM, Pedro Alves wrote:
>
> So I took your suggestion and ran with it.  And in the end
> I like it!  See more below.

Wow! Bored? ;-)

> On 11/22/2017 04:48 PM, Pedro Alves wrote:
> 
> ... well, I'll be d*mn+d...  I gave your suggestion a try, and
> I actually like it!  If we make "-qualified" a flag instead of an
> option with an argument, then usual case of:
> 
>   (gdb) b -q A::doit
> 
> works the exact same.  It's only when you specify source files
> and labels that it makes a difference.  So with your suggestion,
> we get a lot of benefit (works with linespecs, which is the common
> case, I guess) with only a mild downside (a little more typing in
> the explicit location case).

Yeah, linespecs are definitely still the primary interface to locations. I
agree, a little more (or even a little more careful) typing for explicit
locations is not much of a big deal. At least not to me.

> Here's what the delta patch looks like.  I left "-qualified"
> as an explicit location option, rather than splitting the parsing
> of "qualified" and the real explicit options, because this way
> support for "-qualified" in the middle of other options
> "-source filename.cc -qualified -function func" falls out
> naturally.

I hadn't thought of that. That's a reasonable approach.

>  gdb/ax-gdb.c               |  3 +-
>  gdb/breakpoint.c           | 23 +++++++------
>  gdb/completer.c            | 39 +++++++++++++++++----
>  gdb/guile/scm-breakpoint.c |  6 ++--
>  gdb/linespec.c             | 31 +++++++++++------
>  gdb/linespec.h             |  3 +-
>  gdb/location.c             | 86 ++++++++++++++++++++++++++++++----------------
>  gdb/location.h             | 35 ++++++++++++++-----
>  gdb/mi/mi-cmd-break.c      |  3 +-
>  gdb/python/py-breakpoint.c |  3 +-
>  gdb/python/python.c        |  3 +-
>  11 files changed, 163 insertions(+), 72 deletions(-)

Looks good!

Thank you,
Keith

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

* Re: [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]
  2017-11-28  0:21         ` Pedro Alves
@ 2017-11-28  0:42           ` Keith Seitz
  0 siblings, 0 replies; 183+ messages in thread
From: Keith Seitz @ 2017-11-28  0:42 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 11/27/2017 04:21 PM, Pedro Alves wrote:
>>
>> Simple is good!
> 
> :-)  If you look at v2, you'll notice that that sentence was completely
> rewritten though.  :-P
> 

Ha ha ha... That's what I get for linear email processing!

>> If I've read my catch-up mail correctly, there's been a change of plan here.
> 
> Yup.  I've implemented your suggestion and posted it today:
>   https://sourceware.org/ml/gdb-patches/2017-11/msg00689.html
> 
> Actually, I've just now posted a v2.1 to fix "save breakpoints":
>   https://sourceware.org/ml/gdb-patches/2017-11/msg00709.html
> so make sure to read that one instead.
> 
> So if you could skim that, and let me know if I've addressed
> everything, I'd appreciate it.

Absolutely. Please ignore any irrelevant replies from me. I'll eventually get to the right patch. [Besides, I could use the review practice!]

Keith

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

* Re: [pushed] Re: [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests
  2017-11-25  0:18     ` [pushed] " Pedro Alves
@ 2017-11-30 15:43       ` Yao Qi
  2017-11-30 16:06         ` Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Yao Qi @ 2017-11-30 15:43 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Keith Seitz, GDB Patches

On Sat, Nov 25, 2017 at 12:18 AM, Pedro Alves <palves@redhat.com> wrote:
>
> Great, and a much belated thanks.
>
>  and that this one works with current master already, so I've
> pushed it in, as below.
>

Hi Pedro,
some of these tests fail on some buildslsaves,

https://gdb-build.sergiodj.net/builders/Fedora-x86_64-m32/builds/8122/steps/test%20gdb/logs/stdio
https://gdb-build.sergiodj.net/builders/Ubuntu-AArch32-m32/builds/2219/steps/test%20gdb/logs/stdio
https://gdb-build.sergiodj.net/builders/Fedora-i686/builds/8145/steps/test%20gdb/logs/stdio

-- 
Yao (齐尧)

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

* Re: [pushed] Re: [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests
  2017-11-30 15:43       ` Yao Qi
@ 2017-11-30 16:06         ` Pedro Alves
  2017-11-30 16:35           ` [pushed] Fix gdb.linespec/cpls-ops.exp on 32-bit (Re: [pushed] Re: [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests) Pedro Alves
  0 siblings, 1 reply; 183+ messages in thread
From: Pedro Alves @ 2017-11-30 16:06 UTC (permalink / raw)
  To: Yao Qi; +Cc: Keith Seitz, GDB Patches

On 11/30/2017 03:43 PM, Yao Qi wrote:
> On Sat, Nov 25, 2017 at 12:18 AM, Pedro Alves <palves@redhat.com> wrote:
>>
>> Great, and a much belated thanks.
>>
>>  and that this one works with current master already, so I've
>> pushed it in, as below.
>>
> 
> Hi Pedro,
> some of these tests fail on some buildslsaves,
> 
> https://gdb-build.sergiodj.net/builders/Fedora-x86_64-m32/builds/8122/steps/test%20gdb/logs/stdio
> https://gdb-build.sergiodj.net/builders/Ubuntu-AArch32-m32/builds/2219/steps/test%20gdb/logs/stdio
> https://gdb-build.sergiodj.net/builders/Fedora-i686/builds/8145/steps/test%20gdb/logs/stdio
> 

Thanks.  Hmm, 32-bit builds.  I can reproduce this locally with -m32...

(gdb) complete b -function test_op_delete_array::operator delete [] ( void * ) -sour
b -function test_op_delete_array::operator delete [] ( void * ) -source
(gdb) PASS: gdb.linespec/cpls-ops.exp: operator-delete[]: cmd complete "b -function test_op_delete_array::operator delete [] ( void * ) -sour"
b test_op_new::operator new(unsigned int) FAIL: gdb.linespec/cpls-ops.exp: operator-new: tab complete "b test_op_new::operator" (timeout)
^CQuit
(gdb) complete b test_op_new::operator
b test_op_new::operator new(unsigned int)
(gdb) FAIL: gdb.linespec/cpls-ops.exp: operator-new: cmd complete "b test_op_new::operator"
b test_op_new::operator new(unsigned int) got a INT signal, interrupted by user 

Looks like I considered the possibility of different size_t typedefs
at some point but didn't really address it fully:

 proc test_operator_new {class_name brackets} {
     # The type size_t is typedef-ed to.
     set size_t "unsigned long"

I'll fix this when I have a chance.

Thanks,
Pedro Alves

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

* [pushed] Fix gdb.linespec/cpls-ops.exp on 32-bit (Re: [pushed] Re: [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests)
  2017-11-30 16:06         ` Pedro Alves
@ 2017-11-30 16:35           ` Pedro Alves
  0 siblings, 0 replies; 183+ messages in thread
From: Pedro Alves @ 2017-11-30 16:35 UTC (permalink / raw)
  To: Yao Qi; +Cc: Keith Seitz, GDB Patches

On 11/30/2017 04:05 PM, Pedro Alves wrote:

> Looks like I considered the possibility of different size_t typedefs
> at some point but didn't really address it fully:
> 
>  proc test_operator_new {class_name brackets} {
>      # The type size_t is typedef-ed to.
>      set size_t "unsigned long"
> 
> I'll fix this when I have a chance.

I went ahead and pushed this in.

From e3919f3e898aa5ab9a98dcaf9e242a9ebacc0757 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 30 Nov 2017 16:32:10 +0000
Subject: [PATCH] Fix gdb.linespec/cpls-ops.exp on 32-bit

gdb.linespec/cpls-ops.exp is currently failing on x86-64 -m32 and other
32-bit ports:

 b test_op_new::operator new(unsigned int) FAIL: gdb.linespec/cpls-ops.exp: operator-new: tab complete "b test_op_new::operator" (timeout)
 ^CQuit
 (gdb) complete b test_op_new::operator
 b test_op_new::operator new(unsigned int)
 (gdb) FAIL: gdb.linespec/cpls-ops.exp: operator-new: cmd complete "b test_op_new::operator"

The problem is simply that the testcase incorrectly assumes that
size_t is "unsigned long".

Fix this by extracting the right type with the "ptype" command.

gdb/testsuite/ChangeLog:
2017-11-30  Pedro Alves  <palves@redhat.com>

	* gdb.linespec/cpls-ops.exp
	(check_explicit_skips_function_argument): Extract the underlying
	type of size_t instead of hardcoding it.
---
 gdb/testsuite/ChangeLog                 |  6 ++++++
 gdb/testsuite/gdb.linespec/cpls-ops.exp | 13 +++++++++++--
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index d19eca2..5b8151d 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2017-11-30  Pedro Alves  <palves@redhat.com>
+
+	* gdb.linespec/cpls-ops.exp
+	(check_explicit_skips_function_argument): Extract the underlying
+	type of size_t instead of hardcoding it.
+
 2017-11-29  Pedro Alves  <palves@redhat.com>
 
 	PR c++/19436
diff --git a/gdb/testsuite/gdb.linespec/cpls-ops.exp b/gdb/testsuite/gdb.linespec/cpls-ops.exp
index c1b6c33..d58a6de 100644
--- a/gdb/testsuite/gdb.linespec/cpls-ops.exp
+++ b/gdb/testsuite/gdb.linespec/cpls-ops.exp
@@ -43,8 +43,17 @@ proc check_explicit_skips_function_argument {function} {
 # testing operator new.
 
 proc test_operator_new {class_name brackets} {
-    # The type size_t is typedef-ed to.
-    set size_t "unsigned long"
+    global gdb_prompt
+
+    # Extract the type size_t is typedef-ed to.
+    set size_t ""
+    set test "get size_t underlying type"
+    gdb_test_multiple "ptype size_t" $test {
+	-re " = (\[ a-z\]*)\r\n$gdb_prompt $" {
+	    set size_t $expect_out(1,string)
+	    pass "$test"
+	}
+    }
 
     # Complete all prefixes between "operato" and the full prototype.
     foreach cmd_prefix {"b" "b -function"} {
-- 
2.5.5


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

* Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction
  2017-06-02 12:23 ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Pedro Alves
  2017-07-14 17:23   ` Keith Seitz
@ 2018-03-05 21:43   ` Simon Marchi
  1 sibling, 0 replies; 183+ messages in thread
From: Simon Marchi @ 2018-03-05 21:43 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On 2017-06-02 08:22 AM, Pedro Alves wrote:
> This patch reworkds the whole completion machinery, and prepares it
> for later enhancements.
> 
> Adds a new "completion_tracker" class that is meant to hold everything
> about the state of the current completion operation.
> 
> This class now has the responsibility of tracking the list of
> completion matches, and checking whether the max completions limit has
> been reached.  You can look at this as this patch starting out by
> C++fying the existing "completion_tracker" in symtab.c (it's just an
> htab_t typedef currently), moving it to completer.h/c, and then making
> it a class/generalizing/enhancing it.
> 
> Unlike with the current tracking, completion_tracker now checks
> whether the limit has been reached on each completion match list
> insertion.  This both simplifies the max-completions handling code
> (maybe_add_completion_enum is gone, for example), and is a
> prerequisite for follow up patches.
> 
> The current completion_tracker is only used for symbol completions,
> and the symbol code gets at the current instance via globals.  This
> patch cleans that up by adding a completion_tracker reference to the
> signature of the completion functions, and passing the tracker around
> everywhere necessary.
> 
> Then, the patch changes how the completion match list is handed over
> to readline.  Currently, we're using the rl_completion_entry_function
> readline entry point, and the patch switches to
> rl_attempted_completion_function.  A following patch will want to let
> GDB itself decide the common completion prefix between all matches
> (what readline calls the "lowest common denominator"), instead of
> having readline compute it, and that's not possible with the
> rl_completion_entry_function entry point.  Also,
> rl_attempted_completion_function lets GDB hand over the match list to
> readline as an array in one go instead of passing down matches one by
> one, so from that angle it's a nicer entry point anyway.
> 
> Lastly, the patch catches exceptions around the readline entry points,
> because we can't let C++ exceptions cross readline.  We handle that in
> the readline input entry point, but the completion entry point isn't
> guarded, so GDB can abort if completion throws.  E.g., in current
> master:
> 
>  (gdb) b -function "fun<tab>
>  terminate called after throwing an instance of 'gdb_exception_RETURN_MASK_ERROR'
>  Aborted (core dumped)
> 
> This patch fixes that.  This will be exercised in the new tests added
> later on in the series.

Hi Pedro,

Sorry for bringing this thread from the dead...

We are trying to migrate to GDB 8.1, and noticed a behavior change in
the Python API starting with this patch.  I'd like to know if this change
was intended (probably not since I don't see it mentioned in the commit log),
if we should consider it a bug because it broke API, and if we should try
to revert to the previous behavior in GDB 8.1.1.

The complete method of a Python command is called twice, once to deduce the
break characters and the second time to do the actual completion.  I am using
the following script to do my tests:

---- 8< ----

class MyCommand(gdb.Command):
    def __init__(self):
        gdb.Command.__init__(self,'mycmd',gdb.COMMAND_USER)

    def invoke(self,argument,from_tty):
        raise gdb.GdbError('not implemented')

    def complete(self,text,word):
        print('\ntext={}, word={}\n'.format(text, word))
        return ['allo', 'bonjour']

MyCommand()

---- 8< ----

And I try the completion like this:

  (gdb) mycmd hello you<tab>

With 7.12 (probably 8.0 too), the two parameters (text and word) contain this:

  break chars phase: text=hello you, word=mycmd hello you
  completion phase:  text=hello you, word=you

And with 8.1:

  break chars phase: text=hello you, word=None
  completion phase:  text=hello you, word=you

The difference is that during the break chars phase, word is None instead of
containing the complete command line.  With 7.12, if you try this

  (gdb) mycmd hello<tab> you

word has an empty or random value, or GDB segfaults.  If you put a lot of
characters after (hello<tab> youuuuuuuuuuuuuuuuuuu) it has a greater chance
of segfaulting.  With 8.1 it does the right thing.

I think the new behavior makes more sense, because during the break chars
phase, we have not split the last word from the line yet (since we don't
know yet where to split, that's what we're trying to figure out).  The
documentation [1] says:

 word holds the last word of the command line; this is computed using a
 word-breaking heuristic.

So the previous behavior was probably buggy, but it still represents
an API break, that's why I'm asking.

At least, I think we should improve the Python doc to say that

 - word can be None (what should the user code return in that case?)
 - if word is not None, it is computed based on what the first invocation returned

What do you think?

Simon

[1] https://sourceware.org/gdb/onlinedocs/gdb/Commands-In-Python.html#index-completion-of-Python-commands

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

end of thread, other threads:[~2018-03-05 21:43 UTC | newest]

Thread overview: 183+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-02 12:22 [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves
2017-06-02 12:22 ` [PATCH 08/40] completion_list_add_name wrapper functions Pedro Alves
2017-06-27 12:56   ` Yao Qi
2017-06-27 15:35     ` Pedro Alves
2017-06-02 12:22 ` [PATCH 03/40] Fix gdb.base/completion.exp with --target_board=dwarf4-gdb-index Pedro Alves
2017-07-13 20:28   ` Keith Seitz
2017-07-14 16:02     ` Pedro Alves
2017-06-02 12:22 ` [PATCH 06/40] Expression completer should not match explicit location options Pedro Alves
2017-06-29  8:29   ` Yao Qi
2017-06-29 10:56     ` Pedro Alves
2017-06-29 11:08       ` Pedro Alves
2017-06-29 15:23         ` Pedro Alves
2017-06-29 11:24       ` Yao Qi
2017-06-29 15:25         ` Pedro Alves
2017-06-02 12:22 ` [PATCH 02/40] Eliminate make_cleanup_obstack_free, introduce auto_obstack Pedro Alves
2017-06-26 13:47   ` Yao Qi
2017-06-27 10:25     ` Pedro Alves
2017-06-28 10:36   ` Yao Qi
2017-06-28 14:39     ` Pedro Alves
2017-06-28 21:33       ` Yao Qi
2017-06-02 12:22 ` [PATCH 01/40] Make gdb.base/dmsym.exp independent of "set language ada" Pedro Alves
2017-07-18 19:42   ` Simon Marchi
2017-07-20 17:00     ` Pedro Alves
2017-06-02 12:22 ` [PATCH 14/40] Introduce CP_OPERATOR_STR/CP_OPERATOR_LEN and use throughout Pedro Alves
2017-07-14 18:04   ` Keith Seitz
2017-07-17 14:55     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 40/40] Document breakpoints / linespec & co improvements (manual + NEWS) Pedro Alves
2017-06-02 13:01   ` Eli Zaretskii
2017-06-02 13:33     ` Pedro Alves
2017-06-21 15:50       ` Pedro Alves
2017-06-21 19:14         ` Pedro Alves
2017-06-22 19:45           ` Eli Zaretskii
2017-06-22 19:42         ` Eli Zaretskii
2017-06-21 13:32     ` Pedro Alves
2017-06-21 18:26       ` Eli Zaretskii
2017-06-21 19:01         ` Pedro Alves
2017-06-22 19:43           ` Eli Zaretskii
2017-06-02 12:23 ` [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests Pedro Alves
2017-08-09 17:59   ` Keith Seitz
2017-11-25  0:18     ` [pushed] " Pedro Alves
2017-11-30 15:43       ` Yao Qi
2017-11-30 16:06         ` Pedro Alves
2017-11-30 16:35           ` [pushed] Fix gdb.linespec/cpls-ops.exp on 32-bit (Re: [pushed] Re: [PATCH 36/40] Add comprehensive C++ operator linespec/location/completion tests) Pedro Alves
2017-06-02 12:23 ` [PATCH 37/40] Fix completing an empty string Pedro Alves
2017-08-09 18:01   ` Keith Seitz
2017-11-25  0:28     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 09/40] Rename make_symbol_completion_list_fn -> symbol_completer Pedro Alves
2017-06-28 21:40   ` Yao Qi
2017-07-13 20:46   ` Keith Seitz
2017-07-17 11:00     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 34/40] Make strcmp_iw NOT ignore whitespace in the middle of tokens Pedro Alves
2017-08-09 15:48   ` Keith Seitz
2017-11-24 23:38     ` [pushed] " Pedro Alves
2017-06-02 12:23 ` [PATCH 10/40] Clean up "completer_handle_brkchars" callback handling Pedro Alves
2017-07-13 21:08   ` Keith Seitz
2017-07-17 11:14     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 13/40] Introduce strncmp_iw Pedro Alves
2017-06-29  8:42   ` Yao Qi
2017-07-17 19:16     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 35/40] Comprehensive C++ linespec/completer tests Pedro Alves
2017-08-09 17:30   ` Keith Seitz
2017-11-24 16:25     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 19/40] Fix cp_find_first_component_aux bug Pedro Alves
2017-07-17 19:17   ` Keith Seitz
2017-07-17 19:50     ` Pedro Alves
2017-07-17 21:38       ` Keith Seitz
2017-07-20 17:03         ` Pedro Alves
2017-06-02 12:23 ` [PATCH 18/40] A smarter linespec completer Pedro Alves
2017-07-15  0:07   ` Keith Seitz
2017-07-17 18:21     ` Pedro Alves
2017-07-17 19:02       ` Keith Seitz
2017-07-17 19:33         ` Pedro Alves
2017-06-02 12:23 ` [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436) Pedro Alves
2017-08-09 19:34   ` Keith Seitz
2017-11-27 17:14     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 27/40] Make cp_remove_params return a unique_ptr Pedro Alves
2017-08-08 20:35   ` Keith Seitz
2017-10-09 15:13     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Pedro Alves
2017-07-14 17:23   ` Keith Seitz
2017-07-17 13:56     ` Pedro Alves
2017-07-18  8:23       ` Christophe Lyon
2017-07-18  9:04         ` Pedro Alves
2017-07-18 10:42           ` [pushed] Fix GDB builds that include the simulator (Re: [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction) Pedro Alves
2018-03-05 21:43   ` [PATCH 11/40] Introduce class completion_tracker & rewrite completion<->readline interaction Simon Marchi
2017-06-02 12:23 ` [PATCH 28/40] lookup_name_info::make_ignore_params Pedro Alves
2017-08-08 20:55   ` Keith Seitz
2017-11-08 16:18     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 15/40] Rewrite/enhance explicit locations completer, parse left->right Pedro Alves
2017-07-14 20:55   ` Keith Seitz
2017-07-17 19:24     ` Pedro Alves
2017-06-02 12:23 ` [PATCH 38/40] Use TOLOWER in SYMBOL_HASH_NEXT Pedro Alves
2017-08-09 19:25   ` Keith Seitz
2017-11-25  0:35     ` [pushed] " Pedro Alves
2017-06-02 12:28 ` [PATCH 24/40] Per-language symbol name hashing algorithm Pedro Alves
2017-07-18 17:33   ` Keith Seitz
2017-07-20 18:53     ` Pedro Alves
2017-11-08 16:08       ` Pedro Alves
2017-06-02 12:29 ` [PATCH 05/40] command.h: Include scoped_restore_command.h Pedro Alves
2017-06-27 11:30   ` Yao Qi
2017-06-27 11:45     ` Pedro Alves
2017-06-27 11:52       ` Pedro Alves
2017-06-27 12:03         ` Pedro Alves
2017-06-27 15:46           ` [PATCH 05/40] command.h: Include common/scoped_restore.h Pedro Alves
2017-06-28  7:54             ` Yao Qi
2017-06-28 14:20               ` Pedro Alves
2017-06-02 12:29 ` [PATCH 17/40] Linespec lexing and C++ operators Pedro Alves
2017-07-14 21:45   ` Keith Seitz
2017-07-17 19:34     ` Pedro Alves
2017-06-02 12:29 ` [PATCH 12/40] "complete" command and completion word break characters Pedro Alves
2017-07-14 17:50   ` Keith Seitz
2017-07-17 14:36     ` Pedro Alves
2017-06-02 12:29 ` [PATCH 07/40] objfile_per_bfd_storage non-POD Pedro Alves
2017-06-27 12:00   ` Yao Qi
2017-06-27 15:30     ` Pedro Alves
2017-06-02 12:29 ` [PATCH 21/40] Use SYMBOL_MATCHES_SEARCH_NAME some more Pedro Alves
2017-07-17 21:39   ` Keith Seitz
2017-07-20 17:08     ` Pedro Alves
2017-06-02 12:29 ` [PATCH 16/40] Explicit locations -label completer Pedro Alves
2017-07-14 21:32   ` Keith Seitz
2017-06-02 12:29 ` [PATCH 33/40] Make the linespec/location completer ignore data symbols Pedro Alves
2017-08-09 15:42   ` Keith Seitz
2017-11-08 16:22     ` Pedro Alves
2017-06-02 12:30 ` [PATCH 30/40] Use search_domain::FUNCTIONS_DOMAIN when setting breakpoints Pedro Alves
2017-08-08 21:07   ` Keith Seitz
2017-11-08 16:20     ` Pedro Alves
2017-06-02 12:30 ` [PATCH 20/40] Eliminate block_iter_name_* Pedro Alves
2017-07-17 19:47   ` Keith Seitz
2017-07-20 17:05     ` Pedro Alves
2017-06-02 12:30 ` [PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching] Pedro Alves
2017-08-08 23:48   ` Keith Seitz
2017-11-22 16:48     ` Pedro Alves
2017-11-24 16:48       ` Pedro Alves
2017-11-24 16:57         ` Pedro Alves
2017-11-28  0:39         ` Keith Seitz
2017-11-28  0:02       ` Keith Seitz
2017-11-28  0:21         ` Pedro Alves
2017-11-28  0:42           ` Keith Seitz
2017-06-02 12:31 ` [PATCH 04/40] Fix TAB-completion + .gdb_index slowness (generalize filename_seen_cache) Pedro Alves
2017-07-13 20:41   ` Keith Seitz
2017-07-14 19:40     ` Pedro Alves
2017-07-17 10:51       ` Pedro Alves
2017-06-02 12:31 ` [PATCH 22/40] get_int_var_value Pedro Alves
2017-07-17 22:11   ` Keith Seitz
2017-07-20 17:15     ` Pedro Alves
2017-06-02 12:31 ` [PATCH 29/40] Simplify completion_list_add_name | remove sym_text / sym_text_len Pedro Alves
2017-08-08 20:59   ` Keith Seitz
2017-11-08 16:19     ` Pedro Alves
2017-06-02 12:33 ` [PATCH 31/40] Handle custom completion match prefix / LCD Pedro Alves
2017-08-08 21:28   ` Keith Seitz
2017-11-27 17:11     ` Pedro Alves
2017-06-02 12:39 ` [PATCH 25/40] Introduce lookup_name_info and generalize Ada's FULL/WILD name matching Pedro Alves
2017-07-18 20:14   ` Keith Seitz
2017-07-18 22:31     ` Pedro Alves
2017-07-20 19:00       ` Pedro Alves
2017-07-20 19:06         ` Pedro Alves
2017-08-08 20:29           ` Keith Seitz
2017-10-19 17:36             ` Pedro Alves
2017-11-01 15:38               ` Joel Brobecker
2017-11-08 16:10                 ` Pedro Alves
2017-11-08 22:15                   ` Joel Brobecker
2017-06-02 12:39 ` [PATCH 23/40] Make language_def O(1) Pedro Alves
2017-07-17 23:03   ` Keith Seitz
2017-07-20 17:40     ` Pedro Alves
2017-07-20 18:12       ` Get rid of "set language local"? (was: Re: [PATCH 23/40] Make language_def O(1)) Pedro Alves
2017-07-20 23:44         ` Matt Rice
2017-06-02 12:39 ` [PATCH 26/40] Optimize .gdb_index symbol name searching Pedro Alves
2017-08-08 20:32   ` Keith Seitz
2017-11-08 16:14     ` Pedro Alves
2017-11-08 16:16       ` [pushed] Reorder/reindent dw2_expand_symtabs_matching & friends (Re: [PATCH 26/40] Optimize .gdb_index symbol name searching) Pedro Alves
2017-11-18  5:23   ` [PATCH 26/40] Optimize .gdb_index symbol name searching Simon Marchi
2017-11-20  0:33     ` Pedro Alves
2017-11-20  0:42       ` [PATCH 2/3] Unit test name-component bounds searching directly Pedro Alves
2017-11-20  3:16         ` Simon Marchi
2017-11-20 14:17           ` Pedro Alves
2017-11-20  0:42       ` [PATCH 1/3] 0xff chars in name components table; cp-name-parser lex UTF-8 identifiers Pedro Alves
2017-11-20  1:38         ` Simon Marchi
2017-11-20 11:56           ` Pedro Alves
2017-11-20 16:50             ` Simon Marchi
2017-11-21  0:11               ` Pedro Alves
2017-11-20  0:42       ` [PATCH 3/3] Fix mapped_index::find_name_components_bounds upper bound computation Pedro Alves
2017-11-20  3:17         ` Simon Marchi
2017-06-02 15:26 ` [PATCH 00/40] C++ debugging improvements: breakpoints, TAB completion, more Pedro Alves

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).