Merge branch 'master' into merge-job
[deliverable/binutils-gdb.git] / gdb / linespec.c
index 0782c546fda75dc9736fd51eafe81717cc55ed30..d853e02d8f609283475a5e7faa5274f287523d49 100644 (file)
@@ -1,6 +1,6 @@
 /* Parser for linespec for the GNU debugger, GDB.
 
-   Copyright (C) 1986-2005, 2007-2012 Free Software Foundation, Inc.
+   Copyright (C) 1986-2020 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -33,7 +33,6 @@
 #include "block.h"
 #include "objc-lang.h"
 #include "linespec.h"
-#include "exceptions.h"
 #include "language.h"
 #include "interps.h"
 #include "mi/mi-cmds.h"
 #include "cli/cli-utils.h"
 #include "filenames.h"
 #include "ada-lang.h"
+#include "stack.h"
+#include "location.h"
+#include "gdbsupport/function-view.h"
+#include "gdbsupport/def-vector.h"
+#include <algorithm>
 
-typedef struct symtab *symtab_p;
-DEF_VEC_P (symtab_p);
+/* An enumeration of the various things a user might attempt to
+   complete for a linespec location.  */
 
-typedef struct symbol *symbolp;
-DEF_VEC_P (symbolp);
+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,
 
-typedef struct type *typep;
-DEF_VEC_P (typep);
+  /* 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,
+};
 
 /* An address entry is used to ensure that any given location is only
    added to the result a single time.  It holds an address and the
@@ -63,34 +87,74 @@ struct address_entry
   CORE_ADDR addr;
 };
 
+/* A linespec.  Elements of this structure are filled in by a parser
+   (either parse_linespec or some other function).  The structure is
+   then converted into SALs by convert_linespec_to_sals.  */
+
+struct linespec
+{
+  /* An explicit location describing the SaLs.  */
+  struct explicit_location explicit_loc;
+
+  /* The list of symtabs to search to which to limit the search.  May not
+     be NULL.  If explicit.SOURCE_FILENAME is NULL (no user-specified
+     filename), FILE_SYMTABS should contain one single NULL member.  This
+     will cause the code to use the default symtab.  */
+  std::vector<symtab *> *file_symtabs;
+
+  /* A list of matching function symbols and minimal symbols.  Both lists
+     may be NULL (or empty) if no matching symbols were found.  */
+  std::vector<block_symbol> *function_symbols;
+  std::vector<bound_minimal_symbol> *minimal_symbols;
+
+  /* A structure of matching label symbols and the corresponding
+     function symbol in which the label was found.  Both may be NULL
+     or both must be non-NULL.  */
+  struct
+  {
+    std::vector<block_symbol> *label_symbols;
+    std::vector<block_symbol> *function_symbols;
+  } labels;
+};
+typedef struct linespec *linespec_p;
+
+/* A canonical linespec represented as a symtab-related string.
+
+   Each entry represents the "SYMTAB:SUFFIX" linespec string.
+   SYMTAB can be converted for example by symtab_to_fullname or
+   symtab_to_filename_for_display as needed.  */
+
+struct linespec_canonical_name
+{
+  /* Remaining text part of the linespec string.  */
+  char *suffix;
+
+  /* If NULL then SUFFIX is the whole linespec string.  */
+  struct symtab *symtab;
+};
+
 /* An instance of this is used to keep all state while linespec
    operates.  This instance is passed around as a 'this' pointer to
    the various implementation methods.  */
 
 struct linespec_state
 {
+  /* The language in use during linespec processing.  */
+  const struct language_defn *language;
+
   /* The program space as seen when the module was entered.  */
   struct program_space *program_space;
 
+  /* If not NULL, the search is restricted to just this program
+     space.  */
+  struct program_space *search_pspace;
+
   /* The default symtab to use, if no other symtab is specified.  */
   struct symtab *default_symtab;
 
   /* The default line to use.  */
   int default_line;
 
-  /* If the linespec started with "FILE:", this holds all the matching
-     symtabs.  Otherwise, it will hold a single NULL entry, meaning
-     that the default symtab should be used.  */
-  VEC (symtab_p) *file_symtabs;
-
-  /* If the linespec started with "FILE:", this holds an xmalloc'd
-     copy of "FILE".  */
-  char *user_filename;
-
-  /* If the linespec is "FUNCTION:LABEL", this holds an xmalloc'd copy
-     of "FUNCTION".  */
-  char *user_function;
-
   /* The 'funfirstline' value that was passed in to decode_line_1 or
      decode_line_full.  */
   int funfirstline;
@@ -101,12 +165,15 @@ struct linespec_state
   /* The 'canonical' value passed to decode_line_full, or NULL.  */
   struct linespec_result *canonical;
 
-  /* Canonical strings that mirror the symtabs_and_lines result.  */
-  char **canonical_names;
+  /* Canonical strings that mirror the std::vector<symtab_and_line> result.  */
+  struct linespec_canonical_name *canonical_names;
 
   /* This is a set of address_entry objects which is used to prevent
      duplicate symbols from being entered into the result.  */
   htab_t addr_set;
+
+  /* Are we building a linespec?  */
+  int is_linespec;
 };
 
 /* This is a helper object that is used when collecting symbols into a
@@ -117,380 +184,1109 @@ struct collect_info
   /* The linespec object in use.  */
   struct linespec_state *state;
 
+  /* A list of symtabs to which to restrict matches.  */
+  std::vector<symtab *> *file_symtabs;
+
   /* The result being accumulated.  */
-  struct symtabs_and_lines result;
+  struct
+  {
+    std::vector<block_symbol> *symbols;
+    std::vector<bound_minimal_symbol> *minimal_symbols;
+  } result;
+
+  /* Possibly add a symbol to the results.  */
+  virtual bool add_symbol (block_symbol *bsym);
 };
 
-/* Prototypes for local functions.  */
+bool
+collect_info::add_symbol (block_symbol *bsym)
+{
+  /* In list mode, add all matching symbols, regardless of class.
+     This allows the user to type "list a_global_variable".  */
+  if (SYMBOL_CLASS (bsym->symbol) == LOC_BLOCK || this->state->list_mode)
+    this->result.symbols->push_back (*bsym);
 
-static void initialize_defaults (struct symtab **default_symtab,
-                                int *default_line);
+  /* Continue iterating.  */
+  return true;
+}
 
-static struct symtabs_and_lines decode_indirect (struct linespec_state *self,
-                                                char **argptr);
+/* Custom collect_info for symbol_searcher.  */
 
-static char *locate_first_half (char **argptr, int *is_quote_enclosed);
+struct symbol_searcher_collect_info
+  : collect_info
+{
+  bool add_symbol (block_symbol *bsym) override
+  {
+    /* Add everything.  */
+    this->result.symbols->push_back (*bsym);
+
+    /* Continue iterating.  */
+    return true;
+  }
+};
+
+/* Token types  */
+
+enum ls_token_type
+{
+  /* A keyword  */
+  LSTOKEN_KEYWORD = 0,
+
+  /* A colon "separator"  */
+  LSTOKEN_COLON,
+
+  /* A string  */
+  LSTOKEN_STRING,
+
+  /* A number  */
+  LSTOKEN_NUMBER,
+
+  /* A comma  */
+  LSTOKEN_COMMA,
+
+  /* EOI (end of input)  */
+  LSTOKEN_EOI,
+
+  /* Consumed token  */
+  LSTOKEN_CONSUMED
+};
+typedef enum ls_token_type linespec_token_type;
+
+/* 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
 
-static struct symtabs_and_lines decode_objc (struct linespec_state *self,
-                                            char **argptr);
+/* A token of the linespec lexer  */
 
-static struct symtabs_and_lines decode_compound (struct linespec_state *self,
-                                                char **argptr,
-                                                char *saved_arg,
-                                                char *p);
+struct ls_token
+{
+  /* The type of the token  */
+  linespec_token_type type;
+
+  /* Data for the token  */
+  union
+  {
+    /* A string, given as a stoken  */
+    struct stoken string;
+
+    /* A keyword  */
+    const char *keyword;
+  } data;
+};
+typedef struct ls_token linespec_token;
+
+#define LS_TOKEN_STOKEN(TOK) (TOK).data.string
+#define LS_TOKEN_KEYWORD(TOK) (TOK).data.keyword
+
+/* An instance of the linespec parser.  */
+
+struct linespec_parser
+{
+  linespec_parser (int flags, const struct language_defn *language,
+                  struct program_space *search_pspace,
+                  struct symtab *default_symtab,
+                  int default_line,
+                  struct linespec_result *canonical);
+
+  ~linespec_parser ();
 
-static VEC (symbolp) *lookup_prefix_sym (char **argptr, char *p,
-                                        VEC (symtab_p) *,
-                                        char **);
+  DISABLE_COPY_AND_ASSIGN (linespec_parser);
+
+  /* Lexer internal data  */
+  struct
+  {
+    /* Save head of input stream.  */
+    const char *saved_arg;
+
+    /* Head of the input stream.  */
+    const char *stream;
+#define PARSER_STREAM(P) ((P)->lexer.stream)
+
+    /* The current token.  */
+    linespec_token current;
+  } lexer {};
+
+  /* Is the entire linespec quote-enclosed?  */
+  int is_quote_enclosed = 0;
+
+  /* The state of the parse.  */
+  struct linespec_state state {};
+#define PARSER_STATE(PPTR) (&(PPTR)->state)
+
+  /* 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 = linespec_complete_what::NOTHING;
+
+  /* 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 = nullptr;
+
+  /* If the current token was a quoted string, then this is the
+     quoting character (either " or ').  */
+  int completion_quote_char = 0;
+
+  /* If the current token was a quoted string, then this points at the
+     end of the quoted string.  */
+  const char *completion_quote_end = nullptr;
+
+  /* If parsing for completion, then this points at the completion
+     tracker.  Otherwise, this is NULL.  */
+  struct completion_tracker *completion_tracker = nullptr;
+};
 
-static struct symtabs_and_lines find_method (struct linespec_state *self,
-                                            char *saved_arg,
-                                            char *copy,
-                                            const char *class_name,
-                                            VEC (symbolp) *sym_classes);
+/* A convenience macro for accessing the explicit location result of
+   the parser.  */
+#define PARSER_EXPLICIT(PPTR) (&PARSER_RESULT ((PPTR))->explicit_loc)
 
-static void cplusplus_error (const char *name, const char *fmt, ...)
-     ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3);
+/* Prototypes for local functions.  */
 
-static char *find_toplevel_char (char *s, char c);
+static void iterate_over_file_blocks
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain,
+   gdb::function_view<symbol_found_callback_ftype> callback);
 
-static int is_objc_method_format (const char *s);
+static void initialize_defaults (struct symtab **default_symtab,
+                                int *default_line);
 
-static VEC (symtab_p) *symtabs_from_filename (char **argptr,
-                                             char *p, int is_quote_enclosed,
-                                             char **user_filename);
+CORE_ADDR linespec_expression_to_pc (const char **exp_ptr);
 
-static VEC (symbolp) *find_function_symbols (char **argptr, char *p,
-                                            int is_quote_enclosed,
-                                            char **user_function);
+static std::vector<symtab_and_line> decode_objc (struct linespec_state *self,
+                                                linespec_p ls,
+                                                const char *arg);
 
-static struct symtabs_and_lines decode_all_digits (struct linespec_state *self,
-                                                  char **argptr,
-                                                  char *q);
+static std::vector<symtab *> symtabs_from_filename
+  (const char *, struct program_space *pspace);
 
-static struct symtabs_and_lines decode_dollar (struct linespec_state *self,
-                                              char *copy);
+static std::vector<block_symbol> *find_label_symbols
+  (struct linespec_state *self, std::vector<block_symbol> *function_symbols,
+   std::vector<block_symbol> *label_funcs_ret, const char *name,
+   bool completion_mode = false);
 
-static int decode_label (struct linespec_state *self,
-                        VEC (symbolp) *function_symbols,
-                        char *copy,
-                        struct symtabs_and_lines *result);
+static void find_linespec_symbols (struct linespec_state *self,
+                                  std::vector<symtab *> *file_symtabs,
+                                  const char *name,
+                                  symbol_name_match_type name_match_type,
+                                  std::vector<block_symbol> *symbols,
+                                  std::vector<bound_minimal_symbol> *minsyms);
 
-static struct symtabs_and_lines decode_variable (struct linespec_state *self,
-                                                char *copy);
+static struct line_offset
+     linespec_parse_variable (struct linespec_state *self,
+                             const char *variable);
 
 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,
+                                         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);
+static void add_all_symbol_names_from_pspace
+    (struct collect_info *info, struct program_space *pspace,
+     const std::vector<const char *> &names, enum search_domain search_domain);
 
-/* Helper functions.  */
+static std::vector<symtab *>
+  collect_symtabs_from_filename (const char *file,
+                                struct program_space *pspace);
 
-/* Add SAL to SALS.  */
+static std::vector<symtab_and_line> decode_digits_ordinary
+  (struct linespec_state *self,
+   linespec_p ls,
+   int line,
+   linetable_entry **best_entry);
 
-static void
-add_sal_to_sals_basic (struct symtabs_and_lines *sals,
-                      struct symtab_and_line *sal)
-{
-  ++sals->nelts;
-  sals->sals = xrealloc (sals->sals, sals->nelts * sizeof (sals->sals[0]));
-  sals->sals[sals->nelts - 1] = *sal;
-}
+static std::vector<symtab_and_line> decode_digits_list_mode
+  (struct linespec_state *self,
+   linespec_p ls,
+   struct symtab_and_line val);
 
-/* Add SAL to SALS, and also update SELF->CANONICAL_NAMES to reflect
-   the new sal, if needed.  If not NULL, SYMNAME is the name of the
-   symbol to use when constructing the new canonical name.  */
+static void minsym_found (struct linespec_state *self, struct objfile *objfile,
+                         struct minimal_symbol *msymbol,
+                         std::vector<symtab_and_line> *result);
 
-static void
-add_sal_to_sals (struct linespec_state *self,
-                struct symtabs_and_lines *sals,
-                struct symtab_and_line *sal,
-                const char *symname)
-{
-  add_sal_to_sals_basic (sals, sal);
+static bool compare_symbols (const block_symbol &a, const block_symbol &b);
 
-  if (self->canonical)
-    {
-      char *canonical_name = NULL;
+static bool compare_msymbols (const bound_minimal_symbol &a,
+                             const bound_minimal_symbol &b);
 
-      self->canonical_names = xrealloc (self->canonical_names,
-                                       sals->nelts * sizeof (char *));
-      if (sal->symtab && sal->symtab->filename)
-       {
-         char *filename = sal->symtab->filename;
+/* Permitted quote characters for the parser.  This is different from the
+   completer's quote characters to allow backward compatibility with the
+   previous parser.  */
+static const char *const linespec_quote_characters = "\"\'";
 
-         /* Note that the filter doesn't have to be a valid linespec
-            input.  We only apply the ":LINE" treatment to Ada for
-            the time being.  */
-         if (symname != NULL && sal->line != 0
-             && current_language->la_language == language_ada)
-           canonical_name = xstrprintf ("%s:%s:%d", filename, symname,
-                                        sal->line);
-         else if (symname != NULL)
-           canonical_name = xstrprintf ("%s:%s", filename, symname);
-         else
-           canonical_name = xstrprintf ("%s:%d", filename, sal->line);
-       }
+/* Lexer functions.  */
 
-      self->canonical_names[sals->nelts - 1] = canonical_name;
-    }
-}
+/* Lex a number from the input in PARSER.  This only supports
+   decimal numbers.
 
-/* A hash function for address_entry.  */
+   Return true if input is decimal numbers.  Return false if not.  */
 
-static hashval_t
-hash_address_entry (const void *p)
+static int
+linespec_lexer_lex_number (linespec_parser *parser, linespec_token *tokenp)
 {
-  const struct address_entry *aep = p;
-  hashval_t hash;
+  tokenp->type = LSTOKEN_NUMBER;
+  LS_TOKEN_STOKEN (*tokenp).length = 0;
+  LS_TOKEN_STOKEN (*tokenp).ptr = PARSER_STREAM (parser);
 
-  hash = iterative_hash_object (aep->pspace, 0);
-  return iterative_hash_object (aep->addr, hash);
-}
+  /* Keep any sign at the start of the stream.  */
+  if (*PARSER_STREAM (parser) == '+' || *PARSER_STREAM (parser) == '-')
+    {
+      ++LS_TOKEN_STOKEN (*tokenp).length;
+      ++(PARSER_STREAM (parser));
+    }
 
-/* An equality function for address_entry.  */
+  while (isdigit (*PARSER_STREAM (parser)))
+    {
+      ++LS_TOKEN_STOKEN (*tokenp).length;
+      ++(PARSER_STREAM (parser));
+    }
 
-static int
-eq_address_entry (const void *a, const void *b)
-{
-  const struct address_entry *aea = a;
-  const struct address_entry *aeb = b;
+  /* If the next character in the input buffer is not a space, comma,
+     quote, or colon, this input does not represent a number.  */
+  if (*PARSER_STREAM (parser) != '\0'
+      && !isspace (*PARSER_STREAM (parser)) && *PARSER_STREAM (parser) != ','
+      && *PARSER_STREAM (parser) != ':'
+      && !strchr (linespec_quote_characters, *PARSER_STREAM (parser)))
+    {
+      PARSER_STREAM (parser) = LS_TOKEN_STOKEN (*tokenp).ptr;
+      return 0;
+    }
 
-  return aea->pspace == aeb->pspace && aea->addr == aeb->addr;
+  return 1;
 }
 
-/* Check whether the address, represented by PSPACE and ADDR, is
-   already in the set.  If so, return 0.  Otherwise, add it and return
-   1.  */
+/* See linespec.h.  */
 
-static int
-maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
+const char *
+linespec_lexer_lex_keyword (const char *p)
 {
-  struct address_entry e, *p;
-  void **slot;
+  int i;
 
-  e.pspace = pspace;
-  e.addr = addr;
-  slot = htab_find_slot (set, &e, INSERT);
-  if (*slot)
-    return 0;
+  if (p != NULL)
+    {
+      for (i = 0; linespec_keywords[i] != NULL; ++i)
+       {
+         int len = strlen (linespec_keywords[i]);
+
+         /* If P begins with one of the keywords and the next
+            character is whitespace, we may have found a keyword.
+            It is only a keyword if it is not followed by another
+            keyword.  */
+         if (strncmp (p, linespec_keywords[i], len) == 0
+             && isspace (p[len]))
+           {
+             int j;
 
-  p = XNEW (struct address_entry);
-  memcpy (p, &e, sizeof (struct address_entry));
-  *slot = p;
+             /* Special case: "if" ALWAYS stops the lexer, since it
+                is not possible to predict what is going to appear in
+                the condition, which can only be parsed after SaLs have
+                been found.  */
+             if (i != IF_KEYWORD_INDEX)
+               {
+                 p += len;
+                 p = skip_spaces (p);
+                 for (j = 0; linespec_keywords[j] != NULL; ++j)
+                   {
+                     int nextlen = strlen (linespec_keywords[j]);
+
+                     if (strncmp (p, linespec_keywords[j], nextlen) == 0
+                         && isspace (p[nextlen]))
+                       return NULL;
+                   }
+               }
 
-  return 1;
+             return linespec_keywords[i];
+           }
+       }
+    }
+
+  return NULL;
 }
 
-/* Issue a helpful hint on using the command completion feature on
-   single quoted demangled C++ symbols as part of the completion
-   error.  */
+/*  See description in linespec.h.  */
 
-static void
-cplusplus_error (const char *name, const char *fmt, ...)
+int
+is_ada_operator (const char *string)
 {
-  struct ui_file *tmp_stream;
-  char *message;
+  const struct ada_opname_map *mapping;
 
-  tmp_stream = mem_fileopen ();
-  make_cleanup_ui_file_delete (tmp_stream);
+  for (mapping = ada_opname_table;
+       mapping->encoded != NULL
+        && !startswith (string, mapping->decoded); ++mapping)
+    ;
 
-  {
-    va_list args;
+  return mapping->decoded == NULL ? 0 : strlen (mapping->decoded);
+}
 
-    va_start (args, fmt);
-    vfprintf_unfiltered (tmp_stream, fmt, args);
-    va_end (args);
-  }
+/* Find QUOTE_CHAR in STRING, accounting for the ':' terminal.  Return
+   the location of QUOTE_CHAR, or NULL if not found.  */
+
+static const char *
+skip_quote_char (const char *string, char quote_char)
+{
+  const char *p, *last;
 
-  while (*name == '\'')
-    name++;
-  fprintf_unfiltered (tmp_stream,
-                     ("Hint: try '%s<TAB> or '%s<ESC-?>\n"
-                      "(Note leading single quote.)"),
-                     name, name);
+  p = last = find_toplevel_char (string, quote_char);
+  while (p && *p != '\0' && *p != ':')
+    {
+      p = find_toplevel_char (p, quote_char);
+      if (p != NULL)
+       last = p++;
+    }
 
-  message = ui_file_xstrdup (tmp_stream, NULL);
-  make_cleanup (xfree, message);
-  throw_error (NOT_FOUND_ERROR, "%s", message);
+  return last;
 }
 
-/* A helper for iterate_over_all_matching_symtabs that is passed as a
-   callback to the expand_symtabs_matching method.  */
+/* Make a writable copy of the string given in TOKEN, trimming
+   any trailing whitespace.  */
 
-static int
-iterate_name_matcher (const struct language_defn *language,
-                     const char *name, void *d)
+static gdb::unique_xmalloc_ptr<char>
+copy_token_string (linespec_token token)
 {
-  const char **dname = d;
+  const char *str, *s;
 
-  if (language->la_symbol_name_compare (name, *dname) == 0)
-    return 1;
-  return 0;
+  if (token.type == LSTOKEN_KEYWORD)
+    return make_unique_xstrdup (LS_TOKEN_KEYWORD (token));
+
+  str = LS_TOKEN_STOKEN (token).ptr;
+  s = remove_trailing_whitespace (str, str + LS_TOKEN_STOKEN (token).length);
+
+  return gdb::unique_xmalloc_ptr<char> (savestring (str, s - str));
 }
 
-/* A helper that walks over all matching symtabs in all objfiles and
-   calls CALLBACK for each symbol matching NAME.  If SEARCH_PSPACE is
-   not NULL, then the search is restricted to just that program
-   space.  */
+/* Does P represent the end of a quote-enclosed linespec?  */
 
-static void
-iterate_over_all_matching_symtabs (const char *name,
-                                  const domain_enum domain,
-                                  int (*callback) (struct symbol *, void *),
-                                  void *data,
-                                  struct program_space *search_pspace)
+static int
+is_closing_quote_enclosed (const char *p)
 {
-  struct objfile *objfile;
-  struct program_space *pspace;
+  if (strchr (linespec_quote_characters, *p))
+    ++p;
+  p = skip_spaces ((char *) p);
+  return (*p == '\0' || linespec_lexer_lex_keyword (p));
+}
 
-  ALL_PSPACES (pspace)
-  {
-    if (search_pspace != NULL && search_pspace != pspace)
-      continue;
-    if (pspace->executing_startup)
-      continue;
+/* Find the end of the parameter list that starts with *INPUT.
+   This helper function assists with lexing string segments
+   which might contain valid (non-terminating) commas.  */
 
-    set_current_program_space (pspace);
+static const char *
+find_parameter_list_end (const char *input)
+{
+  char end_char, start_char;
+  int depth;
+  const char *p;
+
+  start_char = *input;
+  if (start_char == '(')
+    end_char = ')';
+  else if (start_char == '<')
+    end_char = '>';
+  else
+    return NULL;
 
-    ALL_OBJFILES (objfile)
+  p = input;
+  depth = 0;
+  while (*p)
     {
-      struct symtab *symtab;
-
-      if (objfile->sf)
-       objfile->sf->qf->expand_symtabs_matching (objfile, NULL,
-                                                 iterate_name_matcher,
-                                                 ALL_DOMAIN,
-                                                 &name);
-
-      ALL_OBJFILE_SYMTABS (objfile, symtab)
+      if (*p == start_char)
+       ++depth;
+      else if (*p == end_char)
        {
-         if (symtab->primary)
+         if (--depth == 0)
            {
-             struct block *block;
-
-             block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
-             LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback, data);
+             ++p;
+             break;
            }
        }
+      ++p;
     }
-  }
+
+  return p;
 }
 
-/* Returns the block to be used for symbol searches for the given SYMTAB,
-   which may be NULL.  */
+/* If the [STRING, STRING_LEN) string ends with what looks like a
+   keyword, return the keyword start offset in STRING.  Return -1
+   otherwise.  */
 
-static struct block *
-get_search_block (struct symtab *symtab)
+static size_t
+string_find_incomplete_keyword_at_end (const char * const *keywords,
+                                      const char *string, size_t string_len)
 {
-  struct block *block;
+  const char *end = string + string_len;
+  const char *p = end;
 
-  if (symtab != NULL)
-    block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
-  else
+  while (p > string && *p != ' ')
+    --p;
+  if (p > string)
     {
-      enum language save_language;
-
-      /* get_selected_block can change the current language when there is
-        no selected frame yet.  */
-      save_language = current_language->la_language;
-      block = get_selected_block (0);
-      set_language (save_language);
+      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 block;
+  return -1;
 }
 
-/* A helper for find_method.  This finds all methods in type T which
-   match NAME.  It adds resulting symbol names to RESULT_NAMES, and
-   adds T's direct superclasses to SUPERCLASSES.  */
+/* Lex a string from the input in PARSER.  */
 
-static void
-find_methods (struct type *t, const char *name,
-             VEC (const_char_ptr) **result_names,
-             VEC (typep) **superclasses)
+static linespec_token
+linespec_lexer_lex_string (linespec_parser *parser)
 {
-  int i1 = 0;
-  int ibase;
-  char *class_name = type_name_no_tag (t);
-  char *canon;
+  linespec_token token;
+  const char *start = PARSER_STREAM (parser);
 
-  /* Ignore this class if it doesn't have a name.  This is ugly, but
-     unless we figure out how to get the physname without the name of
-     the class, then the loop can't do any good.  */
-  if (class_name)
+  token.type = LSTOKEN_STRING;
+
+  /* If the input stream starts with a quote character, skip to the next
+     quote character, regardless of the content.  */
+  if (strchr (linespec_quote_characters, *PARSER_STREAM (parser)))
     {
-      int method_counter;
-      int name_len = strlen (name);
+      const char *end;
+      char quote_char = *PARSER_STREAM (parser);
 
-      CHECK_TYPEDEF (t);
+      /* Special case: Ada operators.  */
+      if (PARSER_STATE (parser)->language->la_language == language_ada
+         && quote_char == '\"')
+       {
+         int len = is_ada_operator (PARSER_STREAM (parser));
 
-      /* Loop over each method name.  At this level, all overloads of a name
-         are counted as a single name.  There is an inner loop which loops over
-         each overload.  */
+         if (len != 0)
+           {
+             /* The input is an Ada operator.  Return the quoted string
+                as-is.  */
+             LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser);
+             LS_TOKEN_STOKEN (token).length = len;
+             PARSER_STREAM (parser) += len;
+             return token;
+           }
 
-      for (method_counter = TYPE_NFN_FIELDS (t) - 1;
-          method_counter >= 0;
-          --method_counter)
+         /* The input does not represent an Ada operator -- fall through
+            to normal quoted string handling.  */
+       }
+
+      /* Skip past the beginning quote.  */
+      ++(PARSER_STREAM (parser));
+
+      /* Mark the start of the string.  */
+      LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser);
+
+      /* Skip to the ending quote.  */
+      end = skip_quote_char (PARSER_STREAM (parser), quote_char);
+
+      /* This helps the completer mode decide whether we have a
+        complete string.  */
+      parser->completion_quote_char = quote_char;
+      parser->completion_quote_end = end;
+
+      /* Error if the input did not terminate properly, unless in
+        completion mode.  */
+      if (end == NULL)
        {
-         char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter);
-         char dem_opname[64];
+         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
+    {
+      const char *p;
+
+      /* Otherwise, only identifier characters are permitted.
+        Spaces are the exception.  In general, we keep spaces,
+        but only if the next characters in the input do not resolve
+        to one of the keywords.
 
-         if (strncmp (method_name, "__", 2) == 0 ||
-             strncmp (method_name, "op", 2) == 0 ||
-             strncmp (method_name, "type", 4) == 0)
+        This allows users to forgo quoting CV-qualifiers, template arguments,
+        and similar common language constructs.  */
+
+      while (1)
+       {
+         if (isspace (*PARSER_STREAM (parser)))
            {
-             if (cplus_demangle_opname (method_name, dem_opname, DMGL_ANSI))
-               method_name = dem_opname;
-             else if (cplus_demangle_opname (method_name, dem_opname, 0))
-               method_name = dem_opname;
+             p = skip_spaces (PARSER_STREAM (parser));
+             /* When we get here we know we've found something followed by
+                a space (we skip over parens and templates below).
+                So if we find a keyword now, we know it is a keyword and not,
+                say, a function name.  */
+             if (linespec_lexer_lex_keyword (p) != NULL)
+               {
+                 LS_TOKEN_STOKEN (token).ptr = start;
+                 LS_TOKEN_STOKEN (token).length
+                   = PARSER_STREAM (parser) - start;
+                 return token;
+               }
+
+             /* Advance past the whitespace.  */
+             PARSER_STREAM (parser) = p;
            }
 
-         if (strcmp_iw (method_name, name) == 0)
+         /* If the next character is EOI or (single) ':', the
+            string is complete;  return the token.  */
+         if (*PARSER_STREAM (parser) == 0)
            {
-             int field_counter;
+             LS_TOKEN_STOKEN (token).ptr = start;
+             LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;
+             return token;
+           }
+         else if (PARSER_STREAM (parser)[0] == ':')
+           {
+             /* Do not tokenize the C++ scope operator. */
+             if (PARSER_STREAM (parser)[1] == ':')
+               ++(PARSER_STREAM (parser));
 
-             for (field_counter = (TYPE_FN_FIELDLIST_LENGTH (t, method_counter)
-                                   - 1);
-                  field_counter >= 0;
-                  --field_counter)
+             /* Do not tokenize ABI tags such as "[abi:cxx11]".  */
+             else if (PARSER_STREAM (parser) - start > 4
+                      && startswith (PARSER_STREAM (parser) - 4, "[abi"))
                {
-                 struct fn_field *f;
-                 const char *phys_name;
+                 /* Nothing.  */
+               }
 
-                 f = TYPE_FN_FIELDLIST1 (t, method_counter);
-                 if (TYPE_FN_FIELD_STUB (f, field_counter))
-                   continue;
-                 phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
-                 VEC_safe_push (const_char_ptr, *result_names, phys_name);
+             /* 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
+                paths to be recognized as filenames without quoting it.  */
+             else if ((PARSER_STREAM (parser) - start) != 1
+                      || !IS_DIR_SEPARATOR (PARSER_STREAM (parser)[1]))
+               {
+                 LS_TOKEN_STOKEN (token).ptr = start;
+                 LS_TOKEN_STOKEN (token).length
+                   = PARSER_STREAM (parser) - start;
+                 return token;
+               }
+           }
+         /* Special case: permit quote-enclosed linespecs.  */
+         else if (parser->is_quote_enclosed
+                  && strchr (linespec_quote_characters,
+                             *PARSER_STREAM (parser))
+                  && is_closing_quote_enclosed (PARSER_STREAM (parser)))
+           {
+             LS_TOKEN_STOKEN (token).ptr = start;
+             LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;
+             return token;
+           }
+         /* Because commas may terminate a linespec and appear in
+            the middle of valid string input, special cases for
+            '<' and '(' are necessary.  */
+         else if (*PARSER_STREAM (parser) == '<'
+                  || *PARSER_STREAM (parser) == '(')
+           {
+             /* 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 *op = PARSER_STREAM (parser);
+
+                 while (op > start && isspace (op[-1]))
+                   op--;
+                 if (op - start >= CP_OPERATOR_LEN)
+                   {
+                     op -= CP_OPERATOR_LEN;
+                     if (strncmp (op, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+                         && (op == start
+                             || !(isalnum (op[-1]) || op[-1] == '_')))
+                       {
+                         /* This is an operator name.  Keep going.  */
+                         ++(PARSER_STREAM (parser));
+                         if (*PARSER_STREAM (parser) == '<')
+                           ++(PARSER_STREAM (parser));
+                         continue;
+                       }
+                   }
+               }
+
+             const char *end = find_parameter_list_end (PARSER_STREAM (parser));
+             PARSER_STREAM (parser) = end;
+
+             /* 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 (*end == '\0')
+               {
+                 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.  */
+         else if (*PARSER_STREAM (parser) == ',')
+           {
+             if ((PARSER_STATE (parser)->language->la_language
+                  == language_cplus)
+                 && (PARSER_STREAM (parser) - start) > CP_OPERATOR_LEN)
+               {
+                 const char *op = strstr (start, CP_OPERATOR_STR);
+
+                 if (op != NULL && is_operator_name (op))
+                   {
+                     /* This is an operator name.  Keep going.  */
+                     ++(PARSER_STREAM (parser));
+                     continue;
+                   }
                }
+
+             /* Comma terminates the string.  */
+             LS_TOKEN_STOKEN (token).ptr = start;
+             LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;
+             return token;
            }
+
+         /* Advance the stream.  */
+         gdb_assert (*(PARSER_STREAM (parser)) != '\0');
+         ++(PARSER_STREAM (parser));
        }
     }
 
-  for (ibase = 0; ibase < TYPE_N_BASECLASSES (t); ibase++)
-    VEC_safe_push (typep, *superclasses, TYPE_BASECLASS (t, ibase));
+  return token;
 }
 
-/* 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>.  */
+/* Lex a single linespec token from PARSER.  */
 
-static char *
-find_toplevel_char (char *s, char c)
+static linespec_token
+linespec_lexer_lex_one (linespec_parser *parser)
 {
-  int quoted = 0;              /* zero if we're not in quotes;
-                                  '"' if we're in a double-quoted string;
-                                  '\'' if we're in a single-quoted string.  */
-  int depth = 0;               /* Number of unclosed parens we've seen.  */
-  char *scan;
+  const char *keyword;
+
+  if (parser->lexer.current.type == LSTOKEN_CONSUMED)
+    {
+      /* Skip any whitespace.  */
+      PARSER_STREAM (parser) = skip_spaces (PARSER_STREAM (parser));
+
+      /* Check for a keyword, they end the linespec.  */
+      keyword = linespec_lexer_lex_keyword (PARSER_STREAM (parser));
+      if (keyword != NULL)
+       {
+         parser->lexer.current.type = LSTOKEN_KEYWORD;
+         LS_TOKEN_KEYWORD (parser->lexer.current) = keyword;
+         /* We do not advance the stream here intentionally:
+            we would like lexing to stop when a keyword is seen.
+
+            PARSER_STREAM (parser) +=  strlen (keyword);  */
+
+         return parser->lexer.current;
+       }
+
+      /* Handle other tokens.  */
+      switch (*PARSER_STREAM (parser))
+       {
+       case 0:
+         parser->lexer.current.type = LSTOKEN_EOI;
+         break;
+
+       case '+': case '-':
+       case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+           if (!linespec_lexer_lex_number (parser, &(parser->lexer.current)))
+            parser->lexer.current = linespec_lexer_lex_string (parser);
+          break;
+
+       case ':':
+         /* If we have a scope operator, lex the input as a string.
+            Otherwise, return LSTOKEN_COLON.  */
+         if (PARSER_STREAM (parser)[1] == ':')
+           parser->lexer.current = linespec_lexer_lex_string (parser);
+         else
+           {
+             parser->lexer.current.type = LSTOKEN_COLON;
+             ++(PARSER_STREAM (parser));
+           }
+         break;
+
+       case '\'': case '\"':
+         /* Special case: permit quote-enclosed linespecs.  */
+         if (parser->is_quote_enclosed
+             && is_closing_quote_enclosed (PARSER_STREAM (parser)))
+           {
+             ++(PARSER_STREAM (parser));
+             parser->lexer.current.type = LSTOKEN_EOI;
+           }
+         else
+           parser->lexer.current = linespec_lexer_lex_string (parser);
+         break;
+
+       case ',':
+         parser->lexer.current.type = LSTOKEN_COMMA;
+         LS_TOKEN_STOKEN (parser->lexer.current).ptr
+           = PARSER_STREAM (parser);
+         LS_TOKEN_STOKEN (parser->lexer.current).length = 1;
+         ++(PARSER_STREAM (parser));
+         break;
+
+       default:
+         /* If the input is not a number, it must be a string.
+            [Keywords were already considered above.]  */
+         parser->lexer.current = linespec_lexer_lex_string (parser);
+         break;
+       }
+    }
+
+  return parser->lexer.current;
+}
+
+/* Consume the current token and return the next token in PARSER's
+   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;
+  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.  */
+
+static linespec_token
+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;
+}
+
+/* Helper functions.  */
+
+/* Add SAL to SALS, and also update SELF->CANONICAL_NAMES to reflect
+   the new sal, if needed.  If not NULL, SYMNAME is the name of the
+   symbol to use when constructing the new canonical name.
+
+   If LITERAL_CANONICAL is non-zero, SYMNAME will be used as the
+   canonical name for the SAL.  */
+
+static void
+add_sal_to_sals (struct linespec_state *self,
+                std::vector<symtab_and_line> *sals,
+                struct symtab_and_line *sal,
+                const char *symname, int literal_canonical)
+{
+  sals->push_back (*sal);
+
+  if (self->canonical)
+    {
+      struct linespec_canonical_name *canonical;
+
+      self->canonical_names = XRESIZEVEC (struct linespec_canonical_name,
+                                         self->canonical_names,
+                                         sals->size ());
+      canonical = &self->canonical_names[sals->size () - 1];
+      if (!literal_canonical && sal->symtab)
+       {
+         symtab_to_fullname (sal->symtab);
+
+         /* Note that the filter doesn't have to be a valid linespec
+            input.  We only apply the ":LINE" treatment to Ada for
+            the time being.  */
+         if (symname != NULL && sal->line != 0
+             && self->language->la_language == language_ada)
+           canonical->suffix = xstrprintf ("%s:%d", symname, sal->line);
+         else if (symname != NULL)
+           canonical->suffix = xstrdup (symname);
+         else
+           canonical->suffix = xstrprintf ("%d", sal->line);
+         canonical->symtab = sal->symtab;
+       }
+      else
+       {
+         if (symname != NULL)
+           canonical->suffix = xstrdup (symname);
+         else
+           canonical->suffix = xstrdup ("<unknown>");
+         canonical->symtab = NULL;
+       }
+    }
+}
+
+/* A hash function for address_entry.  */
+
+static hashval_t
+hash_address_entry (const void *p)
+{
+  const struct address_entry *aep = (const struct address_entry *) p;
+  hashval_t hash;
+
+  hash = iterative_hash_object (aep->pspace, 0);
+  return iterative_hash_object (aep->addr, hash);
+}
+
+/* An equality function for address_entry.  */
+
+static int
+eq_address_entry (const void *a, const void *b)
+{
+  const struct address_entry *aea = (const struct address_entry *) a;
+  const struct address_entry *aeb = (const struct address_entry *) b;
+
+  return aea->pspace == aeb->pspace && aea->addr == aeb->addr;
+}
+
+/* Check whether the address, represented by PSPACE and ADDR, is
+   already in the set.  If so, return 0.  Otherwise, add it and return
+   1.  */
+
+static int
+maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
+{
+  struct address_entry e, *p;
+  void **slot;
+
+  e.pspace = pspace;
+  e.addr = addr;
+  slot = htab_find_slot (set, &e, INSERT);
+  if (*slot)
+    return 0;
+
+  p = XNEW (struct address_entry);
+  memcpy (p, &e, sizeof (struct address_entry));
+  *slot = p;
+
+  return 1;
+}
+
+/* A helper that walks over all matching symtabs in all objfiles and
+   calls CALLBACK for each symbol matching NAME.  If SEARCH_PSPACE is
+   not NULL, then the search is restricted to just that program
+   space.  If INCLUDE_INLINE is true then symbols representing
+   inlined instances of functions will be included in the result.  */
+
+static void
+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)
+{
+  struct program_space *pspace;
+
+  ALL_PSPACES (pspace)
+  {
+    if (search_pspace != NULL && search_pspace != pspace)
+      continue;
+    if (pspace->executing_startup)
+      continue;
+
+    set_current_program_space (pspace);
+
+    for (objfile *objfile : current_program_space->objfiles ())
+      {
+       if (objfile->sf)
+         objfile->sf->qf->expand_symtabs_matching (objfile,
+                                                   NULL,
+                                                   lookup_name,
+                                                   NULL, NULL,
+                                                   search_domain);
+
+       for (compunit_symtab *cu : objfile->compunits ())
+         {
+           struct symtab *symtab = COMPUNIT_FILETABS (cu);
+
+           iterate_over_file_blocks (symtab, lookup_name, name_domain,
+                                     callback);
+
+           if (include_inline)
+             {
+               const struct block *block;
+               int i;
+
+               for (i = FIRST_LOCAL_BLOCK;
+                    i < BLOCKVECTOR_NBLOCKS (SYMTAB_BLOCKVECTOR (symtab));
+                    i++)
+                 {
+                   block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), i);
+                   state->language->la_iterate_over_symbols
+                     (block, lookup_name, name_domain,
+                      [&] (block_symbol *bsym)
+                      {
+                        /* Restrict calls to CALLBACK to symbols
+                           representing inline symbols only.  */
+                        if (SYMBOL_INLINED (bsym->symbol))
+                          return callback (bsym);
+                        return true;
+                      });
+                 }
+             }
+         }
+      }
+  }
+}
+
+/* Returns the block to be used for symbol searches from
+   the current location.  */
+
+static const struct block *
+get_current_search_block (void)
+{
+  /* get_selected_block can change the current language when there is
+     no selected frame yet.  */
+  scoped_restore_current_language save_language;
+  return get_selected_block (0);
+}
+
+/* Iterate over static and global blocks.  */
+
+static void
+iterate_over_file_blocks
+  (struct symtab *symtab, const lookup_name_info &name,
+   domain_enum domain, gdb::function_view<symbol_found_callback_ftype> callback)
+{
+  const struct block *block;
+
+  for (block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), STATIC_BLOCK);
+       block != NULL;
+       block = BLOCK_SUPERBLOCK (block))
+    LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback);
+}
+
+/* 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, enum language t_lang, const char *name,
+             std::vector<const char *> *result_names,
+             std::vector<struct type *> *superclasses)
+{
+  int ibase;
+  const char *class_name = TYPE_NAME (t);
+
+  /* Ignore this class if it doesn't have a name.  This is ugly, but
+     unless we figure out how to get the physname without the name of
+     the class, then the loop can't do any good.  */
+  if (class_name)
+    {
+      int method_counter;
+      lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+      symbol_name_matcher_ftype *symbol_name_compare
+       = get_symbol_name_matcher (language_def (t_lang), lookup_name);
+
+      t = check_typedef (t);
+
+      /* Loop over each method name.  At this level, all overloads of a name
+         are counted as a single name.  There is an inner loop which loops over
+         each overload.  */
+
+      for (method_counter = TYPE_NFN_FIELDS (t) - 1;
+          method_counter >= 0;
+          --method_counter)
+       {
+         const char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter);
+
+         if (symbol_name_compare (method_name, lookup_name, NULL))
+           {
+             int field_counter;
+
+             for (field_counter = (TYPE_FN_FIELDLIST_LENGTH (t, method_counter)
+                                   - 1);
+                  field_counter >= 0;
+                  --field_counter)
+               {
+                 struct fn_field *f;
+                 const char *phys_name;
+
+                 f = TYPE_FN_FIELDLIST1 (t, method_counter);
+                 if (TYPE_FN_FIELD_STUB (f, field_counter))
+                   continue;
+                 phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
+                 result_names->push_back (phys_name);
+               }
+           }
+       }
+    }
+
+  for (ibase = 0; ibase < TYPE_N_BASECLASSES (t); ibase++)
+    superclasses->push_back (TYPE_BASECLASS (t, ibase));
+}
+
+/* Find an instance of the character C in the string S that is outside
+   of all parenthesis pairs, single-quoted strings, and double-quoted
+   strings.  Also, ignore the char within a template name, like a ','
+   within foo<int, int>, while considering C++ operator</operator<<.  */
+
+const char *
+find_toplevel_char (const char *s, char c)
+{
+  int quoted = 0;              /* zero if we're not in quotes;
+                                  '"' if we're in a double-quoted string;
+                                  '\'' if we're in a single-quoted string.  */
+  int depth = 0;               /* Number of unclosed parens we've seen.  */
+  const char *scan;
 
   for (scan = s; *scan; scan++)
     {
@@ -509,28 +1305,93 @@ find_toplevel_char (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;
 }
 
-/* Determines if the gives string corresponds to an Objective-C method
-   representation, such as -[Foo bar:] or +[Foo bar].  Objective-C symbols
-   are allowed to have spaces and parentheses in them.  */
+/* The string equivalent of find_toplevel_char.  Returns a pointer
+   to the location of NEEDLE in HAYSTACK, ignoring any occurrences
+   inside "()" and "<>".  Returns NULL if NEEDLE was not found.  */
 
-static int 
-is_objc_method_format (const char *s)
+static const char *
+find_toplevel_string (const char *haystack, const char *needle)
 {
-  if (s == NULL || *s == '\0')
-    return 0;
-  /* Handle arguments with the format FILENAME:SYMBOL.  */
-  if ((s[0] == ':') && (strchr ("+-", s[1]) != NULL) 
-      && (s[2] == '[') && strchr(s, ']'))
-    return 1;
-  /* Handle arguments that are just SYMBOL.  */
-  else if ((strchr ("+-", s[0]) != NULL) && (s[1] == '[') && strchr(s, ']'))
-    return 1;
-  return 0;
+  const char *s = haystack;
+
+  do
+    {
+      s = find_toplevel_char (s, *needle);
+
+      if (s != NULL)
+       {
+         /* Found first char in HAYSTACK;  check rest of string.  */
+         if (startswith (s, needle))
+           return s;
+
+         /* Didn't find it; loop over HAYSTACK, looking for the next
+            instance of the first character of NEEDLE.  */
+         ++s;
+       }
+    }
+  while (s != NULL && *s != '\0');
+
+  /* NEEDLE was not found in HAYSTACK.  */
+  return NULL;
+}
+
+/* Convert CANONICAL to its string representation using
+   symtab_to_fullname for SYMTAB.  */
+
+static std::string
+canonical_to_fullform (const struct linespec_canonical_name *canonical)
+{
+  if (canonical->symtab == NULL)
+    return canonical->suffix;
+  else
+    return string_printf ("%s:%s", symtab_to_fullname (canonical->symtab),
+                         canonical->suffix);
 }
 
 /* Given FILTERS, a list of canonical names, filter the sals in RESULT
@@ -538,274 +1399,1090 @@ is_objc_method_format (const char *s)
 
 static void
 filter_results (struct linespec_state *self,
-               struct symtabs_and_lines *result,
-               VEC (const_char_ptr) *filters)
+               std::vector<symtab_and_line> *result,
+               const std::vector<const char *> &filters)
 {
-  int i;
-  const char *name;
-
-  for (i = 0; VEC_iterate (const_char_ptr, filters, i, name); ++i)
+  for (const char *name : filters)
     {
-      struct linespec_sals lsal;
-      int j;
+      linespec_sals lsal;
+
+      for (size_t j = 0; j < result->size (); ++j)
+       {
+         const struct linespec_canonical_name *canonical;
+
+         canonical = &self->canonical_names[j];
+         std::string fullform = canonical_to_fullform (canonical);
+
+         if (name == fullform)
+           lsal.sals.push_back ((*result)[j]);
+       }
+
+      if (!lsal.sals.empty ())
+       {
+         lsal.canonical = xstrdup (name);
+         self->canonical->lsals.push_back (std::move (lsal));
+       }
+    }
+
+  self->canonical->pre_expanded = 0;
+}
+
+/* Store RESULT into SELF->CANONICAL.  */
+
+static void
+convert_results_to_lsals (struct linespec_state *self,
+                         std::vector<symtab_and_line> *result)
+{
+  struct linespec_sals lsal;
+
+  lsal.canonical = NULL;
+  lsal.sals = std::move (*result);
+  self->canonical->lsals.push_back (std::move (lsal));
+}
+
+/* A structure that contains two string representations of a struct
+   linespec_canonical_name:
+     - one where the symtab's fullname is used;
+     - one where the filename followed the "set filename-display"
+       setting.  */
+
+struct decode_line_2_item
+{
+  decode_line_2_item (std::string &&fullform_, std::string &&displayform_,
+                     bool selected_)
+    : fullform (std::move (fullform_)),
+      displayform (std::move (displayform_)),
+      selected (selected_)
+  {
+  }
+
+  /* The form using symtab_to_fullname.  */
+  std::string fullform;
+
+  /* The form using symtab_to_filename_for_display.  */
+  std::string displayform;
+
+  /* Field is initialized to zero and it is set to one if the user
+     requested breakpoint for this entry.  */
+  unsigned int selected : 1;
+};
+
+/* Helper for std::sort to sort decode_line_2_item entries by
+   DISPLAYFORM and secondarily by FULLFORM.  */
+
+static bool
+decode_line_2_compare_items (const decode_line_2_item &a,
+                            const decode_line_2_item &b)
+{
+  if (a.displayform != b.displayform)
+    return a.displayform < b.displayform;
+  return a.fullform < b.fullform;
+}
+
+/* Handle multiple results in RESULT depending on SELECT_MODE.  This
+   will either return normally, throw an exception on multiple
+   results, or present a menu to the user.  On return, the SALS vector
+   in SELF->CANONICAL is set up properly.  */
+
+static void
+decode_line_2 (struct linespec_state *self,
+              std::vector<symtab_and_line> *result,
+              const char *select_mode)
+{
+  const char *args;
+  const char *prompt;
+  int i;
+  std::vector<const char *> filters;
+  std::vector<struct decode_line_2_item> items;
+
+  gdb_assert (select_mode != multiple_symbols_all);
+  gdb_assert (self->canonical != NULL);
+  gdb_assert (!result->empty ());
+
+  /* Prepare ITEMS array.  */
+  for (i = 0; i < result->size (); ++i)
+    {
+      const struct linespec_canonical_name *canonical;
+      std::string displayform;
+
+      canonical = &self->canonical_names[i];
+      gdb_assert (canonical->suffix != NULL);
+
+      std::string fullform = canonical_to_fullform (canonical);
+
+      if (canonical->symtab == NULL)
+       displayform = canonical->suffix;
+      else
+       {
+         const char *fn_for_display;
+
+         fn_for_display = symtab_to_filename_for_display (canonical->symtab);
+         displayform = string_printf ("%s:%s", fn_for_display,
+                                      canonical->suffix);
+       }
+
+      items.emplace_back (std::move (fullform), std::move (displayform),
+                         false);
+    }
+
+  /* Sort the list of method names.  */
+  std::sort (items.begin (), items.end (), decode_line_2_compare_items);
+
+  /* Remove entries with the same FULLFORM.  */
+  items.erase (std::unique (items.begin (), items.end (),
+                           [] (const struct decode_line_2_item &a,
+                               const struct decode_line_2_item &b)
+                             {
+                               return a.fullform == b.fullform;
+                             }),
+              items.end ());
+
+  if (select_mode == multiple_symbols_cancel && items.size () > 1)
+    error (_("canceled because the command is ambiguous\n"
+            "See set/show multiple-symbol."));
+  
+  if (select_mode == multiple_symbols_all || items.size () == 1)
+    {
+      convert_results_to_lsals (self, result);
+      return;
+    }
+
+  printf_unfiltered (_("[0] cancel\n[1] all\n"));
+  for (i = 0; i < items.size (); i++)
+    printf_unfiltered ("[%d] %s\n", i + 2, items[i].displayform.c_str ());
+
+  prompt = getenv ("PS2");
+  if (prompt == NULL)
+    {
+      prompt = "> ";
+    }
+  args = command_line_input (prompt, "overload-choice");
+
+  if (args == 0 || *args == 0)
+    error_no_arg (_("one or more choice numbers"));
+
+  number_or_range_parser parser (args);
+  while (!parser.finished ())
+    {
+      int num = parser.get_number ();
+
+      if (num == 0)
+       error (_("canceled"));
+      else if (num == 1)
+       {
+         /* We intentionally make this result in a single breakpoint,
+            contrary to what older versions of gdb did.  The
+            rationale is that this lets a user get the
+            multiple_symbols_all behavior even with the 'ask'
+            setting; and he can get separate breakpoints by entering
+            "2-57" at the query.  */
+         convert_results_to_lsals (self, result);
+         return;
+       }
+
+      num -= 2;
+      if (num >= items.size ())
+       printf_unfiltered (_("No choice number %d.\n"), num);
+      else
+       {
+         struct decode_line_2_item *item = &items[num];
+
+         if (!item->selected)
+           {
+             filters.push_back (item->fullform.c_str ());
+             item->selected = 1;
+           }
+         else
+           {
+             printf_unfiltered (_("duplicate request for %d ignored.\n"),
+                                num + 2);
+           }
+       }
+    }
+
+  filter_results (self, result, filters);
+}
+
+\f
+
+/* The parser of linespec itself.  */
+
+/* Throw an appropriate error when SYMBOL is not found (optionally in
+   FILENAME).  */
+
+static void ATTRIBUTE_NORETURN
+symbol_not_found_error (const char *symbol, const char *filename)
+{
+  if (symbol == NULL)
+    symbol = "";
+
+  if (!have_full_symbols ()
+      && !have_partial_symbols ()
+      && !have_minimal_symbols ())
+    throw_error (NOT_FOUND_ERROR,
+                _("No symbol table is loaded.  Use the \"file\" command."));
+
+  /* If SYMBOL starts with '$', the user attempted to either lookup
+     a function/variable in his code starting with '$' or an internal
+     variable of that name.  Since we do not know which, be concise and
+     explain both possibilities.  */
+  if (*symbol == '$')
+    {
+      if (filename)
+       throw_error (NOT_FOUND_ERROR,
+                    _("Undefined convenience variable or function \"%s\" "
+                      "not defined in \"%s\"."), symbol, filename);
+      else
+       throw_error (NOT_FOUND_ERROR,
+                    _("Undefined convenience variable or function \"%s\" "
+                      "not defined."), symbol);
+    }
+  else
+    {
+      if (filename)
+       throw_error (NOT_FOUND_ERROR,
+                    _("Function \"%s\" not defined in \"%s\"."),
+                    symbol, filename);
+      else
+       throw_error (NOT_FOUND_ERROR,
+                    _("Function \"%s\" not defined."), symbol);
+    }
+}
+
+/* Throw an appropriate error when an unexpected token is encountered 
+   in the input.  */
+
+static void ATTRIBUTE_NORETURN
+unexpected_linespec_error (linespec_parser *parser)
+{
+  linespec_token token;
+  static const char * token_type_strings[]
+    = {"keyword", "colon", "string", "number", "comma", "end of input"};
+
+  /* Get the token that generated the error.  */
+  token = linespec_lexer_lex_one (parser);
+
+  /* Finally, throw the error.  */
+  if (token.type == LSTOKEN_STRING || token.type == LSTOKEN_NUMBER
+      || token.type == LSTOKEN_KEYWORD)
+    {
+      gdb::unique_xmalloc_ptr<char> string = copy_token_string (token);
+      throw_error (GENERIC_ERROR,
+                  _("malformed linespec error: unexpected %s, \"%s\""),
+                  token_type_strings[token.type], string.get ());
+    }
+  else
+    throw_error (GENERIC_ERROR,
+                _("malformed linespec error: unexpected %s"),
+                token_type_strings[token.type]);
+}
+
+/* Throw an undefined label error.  */
+
+static void ATTRIBUTE_NORETURN
+undefined_label_error (const char *function, const char *label)
+{
+  if (function != NULL)
+    throw_error (NOT_FOUND_ERROR,
+                _("No label \"%s\" defined in function \"%s\"."),
+                label, function);
+  else
+    throw_error (NOT_FOUND_ERROR,
+                _("No label \"%s\" defined in current function."),
+                label);
+}
+
+/* Throw a source file not found error.  */
+
+static void ATTRIBUTE_NORETURN
+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
+linespec_parse_line_offset (const char *string)
+{
+  const char *start = string;
+  struct line_offset line_offset = {0, LINE_OFFSET_NONE};
+
+  if (*string == '+')
+    {
+      line_offset.sign = LINE_OFFSET_PLUS;
+      ++string;
+    }
+  else if (*string == '-')
+    {
+      line_offset.sign = LINE_OFFSET_MINUS;
+      ++string;
+    }
+
+  if (*string != '\0' && !isdigit (*string))
+    error (_("malformed line offset: \"%s\""), start);
+
+  /* Right now, we only allow base 10 for offsets.  */
+  line_offset.offset = atoi (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 (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
+linespec_parse_basic (linespec_parser *parser)
+{
+  gdb::unique_xmalloc_ptr<char> name;
+  linespec_token token;
+  std::vector<block_symbol> symbols;
+  std::vector<block_symbol> *labels;
+  std::vector<bound_minimal_symbol> minimal_symbols;
+
+  /* Get the next token.  */
+  token = linespec_lexer_lex_one (parser);
+
+  /* If it is EOI or KEYWORD, issue an error.  */
+  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);
+      PARSER_EXPLICIT (parser)->line_offset
+       = linespec_parse_line_offset (name.get ());
+
+      /* Get the next token.  */
+      token = linespec_lexer_consume_token (parser);
+
+      /* If the next token is a comma, stop parsing and return.  */
+      if (token.type == LSTOKEN_COMMA)
+       {
+         parser->complete_what = linespec_complete_what::NOTHING;
+         return;
+       }
+
+      /* If the next token is anything but EOI or KEYWORD, issue
+        an error.  */
+      if (token.type != LSTOKEN_KEYWORD && token.type != LSTOKEN_EOI)
+       unexpected_linespec_error (parser);
+    }
+
+  if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI)
+    return;
+
+  /* Next token must be LSTOKEN_STRING.  */
+  if (token.type != LSTOKEN_STRING)
+    {
+      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);
+
+  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;
+         symbol_name_match_type match_type
+           = PARSER_EXPLICIT (parser)->func_name_match_type;
+
+         linespec_complete_function (tmp_tracker,
+                                     parser->completion_word,
+                                     match_type,
+                                     source_filename);
+
+         if (tmp_tracker.have_completions ())
+           {
+             PARSER_STREAM (parser)++;
+             LS_TOKEN_STOKEN (token).length++;
+
+             name.reset (savestring (parser->completion_word,
+                                     (PARSER_STREAM (parser)
+                                      - parser->completion_word)));
+           }
+       }
+
+      PARSER_EXPLICIT (parser)->function_name = name.release ();
+    }
+  else
+    {
+      /* Try looking it up as a function/method.  */
+      find_linespec_symbols (PARSER_STATE (parser),
+                            PARSER_RESULT (parser)->file_symtabs, name.get (),
+                            PARSER_EXPLICIT (parser)->func_name_match_type,
+                            &symbols, &minimal_symbols);
+
+      if (!symbols.empty () || !minimal_symbols.empty ())
+       {
+         PARSER_RESULT (parser)->function_symbols
+           = new std::vector<block_symbol> (std::move (symbols));
+         PARSER_RESULT (parser)->minimal_symbols
+           = new std::vector<bound_minimal_symbol>
+               (std::move (minimal_symbols));
+         PARSER_EXPLICIT (parser)->function_name = name.release ();
+       }
+      else
+       {
+         /* NAME was not a function or a method.  So it must be a label
+            name or user specified variable like "break foo.c:$zippo".  */
+         labels = find_label_symbols (PARSER_STATE (parser), NULL,
+                                      &symbols, name.get ());
+         if (labels != NULL)
+           {
+             PARSER_RESULT (parser)->labels.label_symbols = labels;
+             PARSER_RESULT (parser)->labels.function_symbols
+               = new std::vector<block_symbol> (std::move (symbols));
+             PARSER_EXPLICIT (parser)->label_name = name.release ();
+           }
+         else if (token.type == LSTOKEN_STRING
+                  && *LS_TOKEN_STOKEN (token).ptr == '$')
+           {
+             /* User specified a convenience variable or history value.  */
+             PARSER_EXPLICIT (parser)->line_offset
+               = linespec_parse_variable (PARSER_STATE (parser), name.get ());
+
+             if (PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN)
+               {
+                 /* The user-specified variable was not valid.  Do not
+                    throw an error here.  parse_linespec will do it for us.  */
+                 PARSER_EXPLICIT (parser)->function_name = name.release ();
+                 return;
+               }
+           }
+         else
+           {
+             /* The name is also not a label.  Abort parsing.  Do not throw
+                an error here.  parse_linespec will do it for us.  */
+
+             /* Save a copy of the name we were trying to lookup.  */
+             PARSER_EXPLICIT (parser)->function_name = name.release ();
+             return;
+           }
+       }
+    }
+
+  int previous_qc = parser->completion_quote_char;
+
+  /* Get the next token.  */
+  token = linespec_lexer_consume_token (parser);
+
+  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);
+
+      if (token.type == LSTOKEN_NUMBER)
+       {
+         /* 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);
+         PARSER_EXPLICIT (parser)->line_offset
+           = linespec_parse_line_offset (name.get ());
+
+         /* 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 (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
+           {
+             /* Grab a copy of the label's name and look it up.  */
+             name = copy_token_string (token);
+             labels
+               = find_label_symbols (PARSER_STATE (parser),
+                                     PARSER_RESULT (parser)->function_symbols,
+                                     &symbols, name.get ());
+
+             if (labels != NULL)
+               {
+                 PARSER_RESULT (parser)->labels.label_symbols = labels;
+                 PARSER_RESULT (parser)->labels.function_symbols
+                   = new std::vector<block_symbol> (std::move (symbols));
+                 PARSER_EXPLICIT (parser)->label_name = name.release ();
+               }
+             else
+               {
+                 /* We don't know what it was, but it isn't a label.  */
+                 undefined_label_error
+                   (PARSER_EXPLICIT (parser)->function_name, name.get ());
+               }
+
+           }
+
+         /* Check for a line offset.  */
+         token = save_stream_and_consume_token (parser);
+         if (token.type == LSTOKEN_COLON)
+           {
+             /* Get the next token.  */
+             token = linespec_lexer_consume_token (parser);
+
+             /* It must be a line offset.  */
+             if (token.type != LSTOKEN_NUMBER)
+               unexpected_linespec_error (parser);
+
+             /* Record the line offset and get the next token.  */
+             name = copy_token_string (token);
+
+             PARSER_EXPLICIT (parser)->line_offset
+               = linespec_parse_line_offset (name.get ());
+
+             /* Get the next token.  */
+             token = linespec_lexer_consume_token (parser);
+           }
+       }
+      else
+       {
+         /* Trailing ':' in the input. Issue an error.  */
+         unexpected_linespec_error (parser);
+       }
+    }
+}
+
+/* Canonicalize the linespec contained in LS.  The result is saved into
+   STATE->canonical.  This function handles both linespec and explicit
+   locations.  */
+
+static void
+canonicalize_linespec (struct linespec_state *state, const linespec_p ls)
+{
+  struct event_location *canon;
+  struct explicit_location *explicit_loc;
+
+  /* If canonicalization was not requested, no need to do anything.  */
+  if (!state->canonical)
+    return;
+
+  /* Save everything as an explicit location.  */
+  state->canonical->location
+    = new_explicit_location (&ls->explicit_loc);
+  canon = state->canonical->location.get ();
+  explicit_loc = get_explicit_location (canon);
+
+  if (explicit_loc->label_name != NULL)
+    {
+      state->canonical->special_display = 1;
+
+      if (explicit_loc->function_name == NULL)
+       {
+         /* No function was specified, so add the symbol name.  */
+         gdb_assert (!ls->labels.function_symbols->empty ()
+                     && (ls->labels.function_symbols->size () == 1));
+         block_symbol s = ls->labels.function_symbols->front ();
+         explicit_loc->function_name = xstrdup (s.symbol->natural_name ());
+       }
+    }
+
+  /* If this location originally came from a linespec, save a string
+     representation of it for display and saving to file.  */
+  if (state->is_linespec)
+    {
+      char *linespec = explicit_location_to_linespec (explicit_loc);
+
+      set_event_location_string (canon, linespec);
+      xfree (linespec);
+    }
+}
+
+/* Given a line offset in LS, construct the relevant SALs.  */
+
+static std::vector<symtab_and_line>
+create_sals_line_offset (struct linespec_state *self,
+                        linespec_p ls)
+{
+  int use_default = 0;
+
+  /* This is where we need to make sure we have good defaults.
+     We must guarantee that this section of code is never executed
+     when we are called with just a function name, since
+     set_default_source_symtab_and_line uses
+     select_source_symtab that calls us with such an argument.  */
+
+  if (ls->file_symtabs->size () == 1
+      && ls->file_symtabs->front () == nullptr)
+    {
+      set_current_program_space (self->program_space);
+
+      /* Make sure we have at least a default source line.  */
+      set_default_source_symtab_and_line ();
+      initialize_defaults (&self->default_symtab, &self->default_line);
+      *ls->file_symtabs
+       = collect_symtabs_from_filename (self->default_symtab->filename,
+                                        self->search_pspace);
+      use_default = 1;
+    }
+
+  symtab_and_line val;
+  val.line = ls->explicit_loc.line_offset.offset;
+  switch (ls->explicit_loc.line_offset.sign)
+    {
+    case LINE_OFFSET_PLUS:
+      if (ls->explicit_loc.line_offset.offset == 0)
+       val.line = 5;
+      if (use_default)
+       val.line = self->default_line + val.line;
+      break;
+
+    case LINE_OFFSET_MINUS:
+      if (ls->explicit_loc.line_offset.offset == 0)
+       val.line = 15;
+      if (use_default)
+       val.line = self->default_line - val.line;
+      else
+       val.line = -val.line;
+      break;
+
+    case LINE_OFFSET_NONE:
+      break;                   /* No need to adjust val.line.  */
+    }
+
+  std::vector<symtab_and_line> values;
+  if (self->list_mode)
+    values = decode_digits_list_mode (self, ls, val);
+  else
+    {
+      struct linetable_entry *best_entry = NULL;
+      int i, j;
+
+      std::vector<symtab_and_line> intermediate_results
+       = decode_digits_ordinary (self, ls, val.line, &best_entry);
+      if (intermediate_results.empty () && best_entry != NULL)
+       intermediate_results = decode_digits_ordinary (self, ls,
+                                                      best_entry->line,
+                                                      &best_entry);
+
+      /* For optimized code, the compiler can scatter one source line
+        across disjoint ranges of PC values, even when no duplicate
+        functions or inline functions are involved.  For example,
+        'for (;;)' inside a non-template, non-inline, and non-ctor-or-dtor
+        function can result in two PC ranges.  In this case, we don't
+        want to set a breakpoint on the first PC of each range.  To filter
+        such cases, we use containing blocks -- for each PC found
+        above, we see if there are other PCs that are in the same
+        block.  If yes, the other PCs are filtered out.  */
+
+      gdb::def_vector<int> filter (intermediate_results.size ());
+      gdb::def_vector<const block *> blocks (intermediate_results.size ());
+
+      for (i = 0; i < intermediate_results.size (); ++i)
+       {
+         set_current_program_space (intermediate_results[i].pspace);
+
+         filter[i] = 1;
+         blocks[i] = block_for_pc_sect (intermediate_results[i].pc,
+                                        intermediate_results[i].section);
+       }
+
+      for (i = 0; i < intermediate_results.size (); ++i)
+       {
+         if (blocks[i] != NULL)
+           for (j = i + 1; j < intermediate_results.size (); ++j)
+             {
+               if (blocks[j] == blocks[i])
+                 {
+                   filter[j] = 0;
+                   break;
+                 }
+             }
+       }
 
-      memset (&lsal, 0, sizeof (lsal));
+      for (i = 0; i < intermediate_results.size (); ++i)
+       if (filter[i])
+         {
+           struct symbol *sym = (blocks[i]
+                                 ? block_containing_function (blocks[i])
+                                 : NULL);
 
-      for (j = 0; j < result->nelts; ++j)
-       {
-         if (strcmp (name, self->canonical_names[j]) == 0)
-           add_sal_to_sals_basic (&lsal.sals, &result->sals[j]);
-       }
+           if (self->funfirstline)
+             skip_prologue_sal (&intermediate_results[i]);
+           intermediate_results[i].symbol = sym;
+           add_sal_to_sals (self, &values, &intermediate_results[i],
+                            sym ? sym->natural_name () : NULL, 0);
+         }
+    }
 
-      if (lsal.sals.nelts > 0)
-       {
-         lsal.canonical = xstrdup (name);
-         VEC_safe_push (linespec_sals, self->canonical->sals, &lsal);
-       }
+  if (values.empty ())
+    {
+      if (ls->explicit_loc.source_filename)
+       throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
+                    val.line, ls->explicit_loc.source_filename);
+      else
+       throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."),
+                    val.line);
     }
 
-  self->canonical->pre_expanded = 0;
+  return values;
 }
 
-/* Store RESULT into SELF->CANONICAL.  */
+/* Convert the given ADDRESS into SaLs.  */
 
-static void
-convert_results_to_lsals (struct linespec_state *self,
-                         struct symtabs_and_lines *result)
+static std::vector<symtab_and_line>
+convert_address_location_to_sals (struct linespec_state *self,
+                                 CORE_ADDR address)
 {
-  struct linespec_sals lsal;
+  symtab_and_line sal = find_pc_line (address, 0);
+  sal.pc = address;
+  sal.section = find_pc_overlay (address);
+  sal.explicit_pc = 1;
+  sal.symbol = find_pc_sect_containing_function (sal.pc, sal.section);
 
-  lsal.canonical = NULL;
-  lsal.sals = *result;
-  VEC_safe_push (linespec_sals, self->canonical->sals, &lsal);
+  std::vector<symtab_and_line> sals;
+  add_sal_to_sals (self, &sals, &sal, core_addr_to_string (address), 1);
+
+  return sals;
 }
 
-/* Handle multiple results in RESULT depending on SELECT_MODE.  This
-   will either return normally, throw an exception on multiple
-   results, or present a menu to the user.  On return, the SALS vector
-   in SELF->CANONICAL is set up properly.  */
+/* Create and return SALs from the linespec LS.  */
 
-static void
-decode_line_2 (struct linespec_state *self,
-              struct symtabs_and_lines *result,
-              const char *select_mode)
+static std::vector<symtab_and_line>
+convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
 {
-  const char *iter;
-  char *args, *prompt;
-  int i;
-  struct cleanup *old_chain;
-  VEC (const_char_ptr) *item_names = NULL, *filters = NULL;
-  struct get_number_or_range_state state;
-
-  gdb_assert (select_mode != multiple_symbols_all);
-  gdb_assert (self->canonical != NULL);
+  std::vector<symtab_and_line> sals;
 
-  old_chain = make_cleanup (VEC_cleanup (const_char_ptr), &item_names);
-  make_cleanup (VEC_cleanup (const_char_ptr), &filters);
-  for (i = 0; i < result->nelts; ++i)
+  if (ls->labels.label_symbols != NULL)
     {
-      int j, found = 0;
-      const char *iter;
+      /* We have just a bunch of functions/methods or labels.  */
+      struct symtab_and_line sal;
 
-      gdb_assert (self->canonical_names[i] != NULL);
-      for (j = 0; VEC_iterate (const_char_ptr, item_names, j, iter); ++j)
+      for (const auto &sym : *ls->labels.label_symbols)
        {
-         if (strcmp (iter, self->canonical_names[i]) == 0)
-           {
-             found = 1;
-             break;
-           }
-       }
-
-      if (!found)
-       VEC_safe_push (const_char_ptr, item_names, self->canonical_names[i]);
-    }
-
-  if (select_mode == multiple_symbols_cancel
-      && VEC_length (const_char_ptr, item_names) > 1)
-    error (_("canceled because the command is ambiguous\n"
-            "See set/show multiple-symbol."));
-  
-  if (select_mode == multiple_symbols_all
-      || VEC_length (const_char_ptr, item_names) == 1)
-    {
-      do_cleanups (old_chain);
-      convert_results_to_lsals (self, result);
-      return;
-    }
-
-  printf_unfiltered (_("[0] cancel\n[1] all\n"));
-  for (i = 0; VEC_iterate (const_char_ptr, item_names, i, iter); ++i)
-    printf_unfiltered ("[%d] %s\n", i + 2, iter);
+         struct program_space *pspace
+           = SYMTAB_PSPACE (symbol_symtab (sym.symbol));
 
-  prompt = getenv ("PS2");
-  if (prompt == NULL)
-    {
-      prompt = "> ";
+         if (symbol_to_sal (&sal, state->funfirstline, sym.symbol)
+             && maybe_add_address (state->addr_set, pspace, sal.pc))
+           add_sal_to_sals (state, &sals, &sal,
+                            sym.symbol->natural_name (), 0);
+       }
     }
-  args = command_line_input (prompt, 0, "overload-choice");
-
-  if (args == 0 || *args == 0)
-    error_no_arg (_("one or more choice numbers"));
-
-  init_number_or_range (&state, args);
-  while (!state.finished)
+  else if (ls->function_symbols != NULL || ls->minimal_symbols != NULL)
     {
-      int num;
+      /* We have just a bunch of functions and/or methods.  */
+      if (ls->function_symbols != NULL)
+       {
+         /* Sort symbols so that symbols with the same program space are next
+            to each other.  */
+         std::sort (ls->function_symbols->begin (),
+                    ls->function_symbols->end (),
+                    compare_symbols);
 
-      num = get_number_or_range (&state);
+         for (const auto &sym : *ls->function_symbols)
+           {
+             program_space *pspace
+               = SYMTAB_PSPACE (symbol_symtab (sym.symbol));
+             set_current_program_space (pspace);
+
+             /* Don't skip to the first line of the function if we
+                had found an ifunc minimal symbol for this function,
+                because that means that this function is an ifunc
+                resolver with the same name as the ifunc itself.  */
+             bool found_ifunc = false;
+
+             if (state->funfirstline
+                  && ls->minimal_symbols != NULL
+                  && SYMBOL_CLASS (sym.symbol) == LOC_BLOCK)
+               {
+                 const CORE_ADDR addr
+                   = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (sym.symbol));
+
+                 for (const auto &elem : *ls->minimal_symbols)
+                   {
+                     if (MSYMBOL_TYPE (elem.minsym) == mst_text_gnu_ifunc
+                         || MSYMBOL_TYPE (elem.minsym) == mst_data_gnu_ifunc)
+                       {
+                         CORE_ADDR msym_addr = BMSYMBOL_VALUE_ADDRESS (elem);
+                         if (MSYMBOL_TYPE (elem.minsym) == mst_data_gnu_ifunc)
+                           {
+                             struct gdbarch *gdbarch
+                               = get_objfile_arch (elem.objfile);
+                             msym_addr
+                               = (gdbarch_convert_from_func_ptr_addr
+                                  (gdbarch,
+                                   msym_addr,
+                                   current_top_target ()));
+                           }
+
+                         if (msym_addr == addr)
+                           {
+                             found_ifunc = true;
+                             break;
+                           }
+                       }
+                   }
+               }
 
-      if (num == 0)
-       error (_("canceled"));
-      else if (num == 1)
-       {
-         /* We intentionally make this result in a single breakpoint,
-            contrary to what older versions of gdb did.  The
-            rationale is that this lets a user get the
-            multiple_symbols_all behavior even with the 'ask'
-            setting; and he can get separate breakpoints by entering
-            "2-57" at the query.  */
-         do_cleanups (old_chain);
-         convert_results_to_lsals (self, result);
-         return;
+             if (!found_ifunc)
+               {
+                 symtab_and_line sal;
+                 if (symbol_to_sal (&sal, state->funfirstline, sym.symbol)
+                     && maybe_add_address (state->addr_set, pspace, sal.pc))
+                   add_sal_to_sals (state, &sals, &sal,
+                                    sym.symbol->natural_name (), 0);
+               }
+           }
        }
 
-      num -= 2;
-      if (num >= VEC_length (const_char_ptr, item_names))
-       printf_unfiltered (_("No choice number %d.\n"), num);
-      else
+      if (ls->minimal_symbols != NULL)
        {
-         const char *elt = VEC_index (const_char_ptr, item_names, num);
+         /* Sort minimal symbols by program space, too  */
+         std::sort (ls->minimal_symbols->begin (),
+                    ls->minimal_symbols->end (),
+                    compare_msymbols);
 
-         if (elt != NULL)
+         for (const auto &elem : *ls->minimal_symbols)
            {
-             VEC_safe_push (const_char_ptr, filters, elt);
-             VEC_replace (const_char_ptr, item_names, num, NULL);
-           }
-         else
-           {
-             printf_unfiltered (_("duplicate request for %d ignored.\n"),
-                                num);
+             program_space *pspace = elem.objfile->pspace;
+             set_current_program_space (pspace);
+             minsym_found (state, elem.objfile, elem.minsym, &sals);
            }
        }
     }
+  else if (ls->explicit_loc.line_offset.sign != LINE_OFFSET_UNKNOWN)
+    {
+      /* Only an offset was specified.  */
+       sals = create_sals_line_offset (state, ls);
 
-  filter_results (self, result, filters);
-  do_cleanups (old_chain);
-}
+       /* Make sure we have a filename for canonicalization.  */
+       if (ls->explicit_loc.source_filename == NULL)
+         {
+           const char *fullname = symtab_to_fullname (state->default_symtab);
 
-/* Valid delimiters for linespec keywords "if", "thread" or "task".  */
+           /* It may be more appropriate to keep DEFAULT_SYMTAB in its symtab
+              form so that displaying SOURCE_FILENAME can follow the current
+              FILENAME_DISPLAY_STRING setting.  But as it is used only rarely
+              it has been kept for code simplicity only in absolute form.  */
+           ls->explicit_loc.source_filename = xstrdup (fullname);
+         }
+    }
+  else
+    {
+      /* We haven't found any results...  */
+      return sals;
+    }
 
-static int
-is_linespec_boundary (char c)
-{
-  return c == ' ' || c == '\t' || c == '\0' || c == ',';
+  canonicalize_linespec (state, ls);
+
+  if (!sals.empty () && state->canonical != NULL)
+    state->canonical->pre_expanded = 1;
+
+  return sals;
 }
 
-/* A helper function for decode_line_1 and friends which skips P
-   past any method overload information at the beginning of P, e.g.,
-   "(const struct foo *)".
+/* Build RESULT from the explicit location components SOURCE_FILENAME,
+   FUNCTION_NAME, LABEL_NAME and LINE_OFFSET.  */
 
-   This function assumes that P has already been validated to contain
-   overload information, and it will assert if *P != '('.  */
-static char *
-find_method_overload_end (char *p)
+static void
+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)
 {
-  int depth = 0;
+  std::vector<block_symbol> symbols;
+  std::vector<block_symbol> *labels;
+  std::vector<bound_minimal_symbol> minimal_symbols;
 
-  gdb_assert (*p == '(');
+  result->explicit_loc.func_name_match_type = fname_match_type;
 
-  while (*p)
+  if (source_filename != NULL)
     {
-      if (*p == '(')
-       ++depth;
-      else if (*p == ')')
+      try
        {
-         if (--depth == 0)
-           {
-             ++p;
-             break;
-           }
+         *result->file_symtabs
+           = symtabs_from_filename (source_filename, self->search_pspace);
        }
-      ++p;
+      catch (const gdb_exception_error &except)
+       {
+         source_file_not_found_error (source_filename);
+       }
+      result->explicit_loc.source_filename = xstrdup (source_filename);
+    }
+  else
+    {
+      /* A NULL entry means to use the default symtab.  */
+      result->file_symtabs->push_back (nullptr);
     }
 
-  return p;
-}
-
-/* Keep important information used when looking up a name.  This includes
-   template parameters, overload information, and important keywords, including
-   the possible Java trailing type.  */
-
-static char *
-keep_name_info (char *p, int on_boundary)
-{
-  const char *quotes = get_gdb_completer_quote_characters ();
-  char *saved_p = p;
-  int nest = 0;
-
-  while (*p)
+  if (function_name != NULL)
     {
-      if (strchr (quotes, *p))
-       break;
+      find_linespec_symbols (self, result->file_symtabs,
+                            function_name, fname_match_type,
+                            &symbols, &minimal_symbols);
+
+      if (symbols.empty () && minimal_symbols.empty ())
+       symbol_not_found_error (function_name,
+                               result->explicit_loc.source_filename);
+
+      result->explicit_loc.function_name = xstrdup (function_name);
+      result->function_symbols
+       = new std::vector<block_symbol> (std::move (symbols));
+      result->minimal_symbols
+       = new std::vector<bound_minimal_symbol> (std::move (minimal_symbols));
+    }
 
-      if (*p == ',' && !nest)
-       break;
+  if (label_name != NULL)
+    {
+      labels = find_label_symbols (self, result->function_symbols,
+                                  &symbols, label_name);
 
-      if (on_boundary && !nest)
-       {
-         const char *const words[] = { "if", "thread", "task" };
-         int wordi;
+      if (labels == NULL)
+       undefined_label_error (result->explicit_loc.function_name,
+                              label_name);
 
-         for (wordi = 0; wordi < ARRAY_SIZE (words); wordi++)
-           if (strncmp (p, words[wordi], strlen (words[wordi])) == 0
-               && is_linespec_boundary (p[strlen (words[wordi])]))
-             break;
-         if (wordi < ARRAY_SIZE (words))
-           break;
-       }
+      result->explicit_loc.label_name = xstrdup (label_name);
+      result->labels.label_symbols = labels;
+      result->labels.function_symbols
+       = new std::vector<block_symbol> (std::move (symbols));
+    }
 
-      if (*p == '(' || *p == '<' || *p == '[')
-       nest++;
-      else if ((*p == ')' || *p == '>' || *p == ']') && nest > 0)
-       nest--;
+  if (line_offset.sign != LINE_OFFSET_UNKNOWN)
+    result->explicit_loc.line_offset = line_offset;
+}
 
-      p++;
+/* Convert the explicit location EXPLICIT_LOC into SaLs.  */
 
-      /* The ',' check could fail on "operator ,".  */
-      p += cp_validate_operator (p);
+static std::vector<symtab_and_line>
+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->func_name_match_type,
+                                        explicit_loc->label_name,
+                                        explicit_loc->line_offset);
+  return convert_linespec_to_sals (self, result);
+}
 
-      on_boundary = is_linespec_boundary (p[-1]);
-    }
+/* Parse a string that specifies a linespec.
 
-  while (p > saved_p && is_linespec_boundary (p[-1]))
-    p--;
+   The basic grammar of linespecs:
 
-  return p;
-}
+   linespec -> var_spec | basic_spec
+   var_spec -> '$' (STRING | NUMBER)
 
-\f
-/* The parser of linespec itself.  */
+   basic_spec -> file_offset_spec | function_spec | label_spec
+   file_offset_spec -> opt_file_spec offset_spec
+   function_spec -> opt_file_spec function_name_spec opt_label_spec
+   label_spec -> label_name_spec
 
-/* Parse a string that specifies a line number.
-   Pass the address of a char * variable; that variable will be
-   advanced over the characters actually parsed.
+   opt_file_spec -> "" | file_name_spec ':'
+   opt_label_spec -> "" | ':' label_name_spec
 
-   The string can be:
+   file_name_spec -> STRING
+   function_name_spec -> STRING
+   label_name_spec -> STRING
+   function_name_spec -> STRING
+   offset_spec -> NUMBER
+               -> '+' NUMBER
+              -> '-' NUMBER
 
-   LINENUM -- that line number in current file.  PC returned is 0.
-   FILE:LINENUM -- that line in that file.  PC returned is 0.
-   FUNCTION -- line number of openbrace of that function.
-   PC returned is the start of the function.
-   LABEL -- a label in the current scope
-   VARIABLE -- line number of definition of that variable.
-   PC returned is 0.
-   FILE:FUNCTION -- likewise, but prefer functions in that file.
-   *EXPR -- line in which address EXPR appears.
+   This may all be followed by several keywords such as "if EXPR",
+   which we ignore.
 
-   This may all be followed by an "if EXPR", which we ignore.
+   A comma will terminate parsing.
 
-   FUNCTION may be an undebuggable function found in minimal symbol table.
+   The function may be an undebuggable function found in minimal symbol table.
 
    If the argument FUNFIRSTLINE is nonzero, we want the first line
    of real code inside a function when a function is specified, and it is
@@ -825,379 +2502,716 @@ keep_name_info (char *p, int on_boundary)
    if no file is validly specified.  Callers must check that.
    Also, the line number returned may be invalid.  */
 
-/* We allow single quotes in various places.  This is a hideous
-   kludge, which exists because the completer can't yet deal with the
-   lack of single quotes.  FIXME: write a linespec_completer which we
-   can use as appropriate instead of make_symbol_completion_list.  */
-
-static struct symtabs_and_lines
-decode_line_internal (struct linespec_state *self, char **argptr)
-{
-  char *p;
-  char *q;
-
-  char *copy;
-  /* This says whether or not something in *ARGPTR is quoted with
-     completer_quotes (i.e. with single quotes).  */
-  int is_quoted;
-  /* Is *ARGPTR enclosed in double quotes?  */
-  int is_quote_enclosed;
-  int is_objc_method = 0;
-  char *saved_arg = *argptr;
-  /* If IS_QUOTED, the end of the quoted bit.  */
-  char *end_quote = NULL;
-  /* Is *ARGPTR enclosed in single quotes?  */
-  int is_squote_enclosed = 0;
-  /* The "first half" of the linespec.  */
-  char *first_half;
-
-  /* If we are parsing `function:label', this holds the symbols
-     matching the function name.  */
-  VEC (symbolp) *function_symbols = NULL;
-  /* If FUNCTION_SYMBOLS is not NULL, then this is the exception that
-     was thrown when trying to parse a filename.  */
-  volatile struct gdb_exception file_exception;
-
-  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
-
-  /* Defaults have defaults.  */
-
-  initialize_defaults (&self->default_symtab, &self->default_line);
-  
-  /* See if arg is *PC.  */
+/* Parse the linespec in ARG.  MATCH_TYPE indicates how function names
+   should be matched.  */
 
-  if (**argptr == '*')
+static std::vector<symtab_and_line>
+parse_linespec (linespec_parser *parser, const char *arg,
+               symbol_name_match_type match_type)
+{
+  linespec_token token;
+  struct gdb_exception file_exception;
+
+  /* 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 (parser->completion_tracker == NULL
+      && !is_ada_operator (arg)
+      && strchr (linespec_quote_characters, *arg) != NULL)
     {
-      do_cleanups (cleanup);
-      return decode_indirect (self, argptr);
+      const char *end;
+
+      end = skip_quote_char (arg + 1, *arg);
+      if (end != NULL && is_closing_quote_enclosed (end))
+       {
+         /* Here's the special case.  Skip ARG past the initial
+            quote.  */
+         ++arg;
+         parser->is_quote_enclosed = 1;
+       }
     }
 
-  is_quoted = (strchr (get_gdb_completer_quote_characters (),
-                      **argptr) != NULL);
+  parser->lexer.saved_arg = arg;
+  parser->lexer.stream = arg;
+  parser->completion_word = arg;
+  parser->complete_what = linespec_complete_what::FUNCTION;
+  PARSER_EXPLICIT (parser)->func_name_match_type = match_type;
+
+  /* Initialize the default symtab and line offset.  */
+  initialize_defaults (&PARSER_STATE (parser)->default_symtab,
+                      &PARSER_STATE (parser)->default_line);
 
-  if (is_quoted)
+  /* Objective-C shortcut.  */
+  if (parser->completion_tracker == NULL)
+    {
+      std::vector<symtab_and_line> values
+       = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
+      if (!values.empty ())
+       return values;
+    }
+  else
     {
-      end_quote = skip_quoted (*argptr);
-      if (*end_quote == '\0')
-       is_squote_enclosed = 1;
+      /* "-"/"+" 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 {};
     }
 
-  /* Check to see if it's a multipart linespec (with colons or
-     periods).  */
+  /* Start parsing.  */
+
+  /* Get the first token.  */
+  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 == '$')
+    {
+      /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
+      if (parser->completion_tracker == NULL)
+       PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
 
-  /* Locate the end of the first half of the linespec.
-     After the call, for instance, if the argptr string is "foo.c:123"
-     p will point at "123".  If there is only one part, like "foo", p
-     will point to "".  If this is a C++ name, like "A::B::foo", p will
-     point to "::B::foo".  Argptr is not changed by this call.  */
+      /* User specified a convenience variable or history value.  */
+      gdb::unique_xmalloc_ptr<char> var = copy_token_string (token);
+      PARSER_EXPLICIT (parser)->line_offset
+       = linespec_parse_variable (PARSER_STATE (parser), var.get ());
+
+      /* If a line_offset wasn't found (VAR is the name of a user
+        variable/function), then skip to normal symbol processing.  */
+      if (PARSER_EXPLICIT (parser)->line_offset.sign != LINE_OFFSET_UNKNOWN)
+       {
+         /* Consume this token.  */
+         linespec_lexer_consume_token (parser);
 
-  first_half = p = locate_first_half (argptr, &is_quote_enclosed);
+         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)
+    {
+      parser->complete_what = linespec_complete_what::NOTHING;
+      unexpected_linespec_error (parser);
+    }
 
-  /* First things first: if ARGPTR starts with a filename, get its
-     symtab and strip the filename from ARGPTR.
-     Avoid calling symtab_from_filename if we know can,
-     it can be expensive.  */
+  /* Shortcut: If the next token is not LSTOKEN_COLON, we know that
+     this token cannot represent a filename.  */
+  token = linespec_lexer_peek_token (parser);
 
-  if (*p != '\0')
+  if (token.type == LSTOKEN_COLON)
     {
-      TRY_CATCH (file_exception, RETURN_MASK_ERROR)
+      /* Get the current token again and extract the filename.  */
+      token = linespec_lexer_lex_one (parser);
+      gdb::unique_xmalloc_ptr<char> user_filename = copy_token_string (token);
+
+      /* Check if the input is a filename.  */
+      try
+       {
+         *PARSER_RESULT (parser)->file_symtabs
+           = symtabs_from_filename (user_filename.get (),
+                                    PARSER_STATE (parser)->search_pspace);
+       }
+      catch (gdb_exception_error &ex)
        {
-         self->file_symtabs = symtabs_from_filename (argptr, p,
-                                                     is_quote_enclosed,
-                                                     &self->user_filename);
+         file_exception = std::move (ex);
        }
 
       if (file_exception.reason >= 0)
        {
-         /* Check for single quotes on the non-filename part.  */
-         is_quoted = (**argptr
-                      && strchr (get_gdb_completer_quote_characters (),
-                                 **argptr) != NULL);
-         if (is_quoted)
-           end_quote = skip_quoted (*argptr);
+         /* Symtabs were found for the file.  Record the filename.  */
+         PARSER_EXPLICIT (parser)->source_filename = user_filename.release ();
 
-         /* Locate the next "half" of the linespec.  */
-         first_half = p = locate_first_half (argptr, &is_quote_enclosed);
-       }
+         /* Get the next token.  */
+         token = linespec_lexer_consume_token (parser);
 
-      if (VEC_empty (symtab_p, self->file_symtabs))
+         /* This is LSTOKEN_COLON; consume it.  */
+         linespec_lexer_consume_token (parser);
+       }
+      else
        {
          /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
-         VEC_safe_push (symtab_p, self->file_symtabs, NULL);
+         PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
        }
     }
+  /* If the next token is not EOI, KEYWORD, or COMMA, issue an error.  */
+  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.  */
+      linespec_lexer_consume_token (parser);
+      unexpected_linespec_error (parser);
+    }
   else
     {
       /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
-      VEC_safe_push (symtab_p, self->file_symtabs, NULL);
+      PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
+    }
+
+  /* Parse the rest of the linespec.  */
+  linespec_parse_basic (parser);
+
+  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)
+    {
+      /* The linespec didn't parse.  Re-throw the file exception if
+        there was one.  */
+      if (file_exception.reason < 0)
+       throw_exception (std::move (file_exception));
+
+      /* Otherwise, the symbol is not found.  */
+      symbol_not_found_error (PARSER_EXPLICIT (parser)->function_name,
+                             PARSER_EXPLICIT (parser)->source_filename);
     }
 
-  /* Check if this is an Objective-C method (anything that starts with
-     a '+' or '-' and a '[').  */
-  if (is_objc_method_format (p))
-    is_objc_method = 1;
+ convert_to_sals:
 
-  /* Check if the symbol could be an Objective-C selector.  */
+  /* Get the last token and record how much of the input was parsed,
+     if necessary.  */
+  token = linespec_lexer_lex_one (parser);
+  if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD)
+    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 (skip_to_space (PARSER_STREAM (parser)));
+      parser->complete_what = linespec_complete_what::EXPRESSION;
+    }
 
-  {
-    struct symtabs_and_lines values;
+  /* Convert the data in PARSER_RESULT to SALs.  */
+  if (parser->completion_tracker == NULL)
+    return convert_linespec_to_sals (PARSER_STATE (parser),
+                                    PARSER_RESULT (parser));
 
-    values = decode_objc (self, argptr);
-    if (values.sals != NULL)
-      {
-       do_cleanups (cleanup);
-       return values;
-      }
-  }
+  return {};
+}
 
-  /* Does it look like there actually were two parts?  */
-
-  if (p[0] == ':' || p[0] == '.')
-    {
-      /* Is it a C++ or Java compound data structure?
-        The check on p[1] == ':' is capturing the case of "::",
-        since p[0]==':' was checked above.
-        Note that the call to decode_compound does everything
-        for us, including the lookup on the symbol table, so we
-        can return now.  */
-       
-      if (p[0] == '.' || p[1] == ':')
-       {
-        /* We only perform this check for the languages where it might
-           make sense.  For instance, Ada does not use this type of
-           syntax, and trying to apply this logic on an Ada linespec
-           may trigger a spurious error (for instance, decode_compound
-           does not like expressions such as `ops."<"', which is a
-           valid function name in Ada).  */
-         if (current_language->la_language == language_c
-             || current_language->la_language == language_cplus
-             || current_language->la_language == language_java)
-           {
-             struct symtabs_and_lines values;
-             volatile struct gdb_exception ex;
-             char *saved_argptr = *argptr;
 
-             if (is_quote_enclosed)
-               ++saved_arg;
+/* A constructor for linespec_state.  */
+
+static void
+linespec_state_constructor (struct linespec_state *self,
+                           int flags, const struct language_defn *language,
+                           struct program_space *search_pspace,
+                           struct symtab *default_symtab,
+                           int default_line,
+                           struct linespec_result *canonical)
+{
+  memset (self, 0, sizeof (*self));
+  self->language = language;
+  self->funfirstline = (flags & DECODE_LINE_FUNFIRSTLINE) ? 1 : 0;
+  self->list_mode = (flags & DECODE_LINE_LIST_MODE) ? 1 : 0;
+  self->search_pspace = search_pspace;
+  self->default_symtab = default_symtab;
+  self->default_line = default_line;
+  self->canonical = canonical;
+  self->program_space = current_program_space;
+  self->addr_set = htab_create_alloc (10, hash_address_entry, eq_address_entry,
+                                     xfree, xcalloc, xfree);
+  self->is_linespec = 0;
+}
+
+/* Initialize a new linespec parser.  */
+
+linespec_parser::linespec_parser (int flags,
+                                 const struct language_defn *language,
+                                 struct program_space *search_pspace,
+                                 struct symtab *default_symtab,
+                                 int default_line,
+                                 struct linespec_result *canonical)
+{
+  lexer.current.type = LSTOKEN_CONSUMED;
+  PARSER_RESULT (this)->file_symtabs = new std::vector<symtab *> ();
+  PARSER_EXPLICIT (this)->func_name_match_type
+    = symbol_name_match_type::WILD;
+  PARSER_EXPLICIT (this)->line_offset.sign = LINE_OFFSET_UNKNOWN;
+  linespec_state_constructor (PARSER_STATE (this), flags, language,
+                             search_pspace,
+                             default_symtab, default_line, canonical);
+}
+
+/* A destructor for linespec_state.  */
+
+static void
+linespec_state_destructor (struct linespec_state *self)
+{
+  htab_delete (self->addr_set);
+  xfree (self->canonical_names);
+}
+
+/* Delete a linespec parser.  */
+
+linespec_parser::~linespec_parser ()
+{
+  xfree (PARSER_EXPLICIT (this)->source_filename);
+  xfree (PARSER_EXPLICIT (this)->label_name);
+  xfree (PARSER_EXPLICIT (this)->function_name);
+
+  delete PARSER_RESULT (this)->file_symtabs;
+  delete PARSER_RESULT (this)->function_symbols;
+  delete PARSER_RESULT (this)->minimal_symbols;
+  delete PARSER_RESULT (this)->labels.label_symbols;
+  delete PARSER_RESULT (this)->labels.function_symbols;
+
+  linespec_state_destructor (PARSER_STATE (this));
+}
+
+/* See description in linespec.h.  */
 
-             /* Initialize it just to avoid a GCC false warning.  */
-             memset (&values, 0, sizeof (values));
+void
+linespec_lex_to_end (const char **stringp)
+{
+  linespec_token token;
+  const char *orig;
+
+  if (stringp == NULL || *stringp == NULL)
+    return;
+
+  linespec_parser parser (0, current_language, NULL, NULL, 0, NULL);
+  parser.lexer.saved_arg = *stringp;
+  PARSER_STREAM (&parser) = orig = *stringp;
+
+  do
+    {
+      /* Stop before any comma tokens;  we need it to keep it
+        as the next token in the string.  */
+      token = linespec_lexer_peek_token (&parser);
+      if (token.type == LSTOKEN_COMMA)
+       break;
+      token = linespec_lexer_consume_token (&parser);
+    }
+  while (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD);
+
+  *stringp += PARSER_STREAM (&parser) - orig;
+}
+
+/* See linespec.h.  */
+
+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;
+
+  if (source_filename != NULL)
+    {
+      collect_file_symbol_completion_matches (tracker, mode, func_match_type,
+                                             function, function, source_filename);
+    }
+  else
+    {
+      collect_symbol_completion_matches (tracker, mode, func_match_type,
+                                        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;
 
-             TRY_CATCH (ex, RETURN_MASK_ERROR)
-               {
-                 values = decode_compound (self, argptr, saved_arg, p);
-               }
-             if ((is_quoted || is_squote_enclosed) && **argptr == '\'')
-               *argptr = *argptr + 1;
+      symbol_name_match_type match_type
+       = PARSER_EXPLICIT (parser)->func_name_match_type;
+      linespec_complete_function (tracker, text, match_type, 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 (ex.reason >= 0)
-               {
-                 do_cleanups (cleanup);
-                 return values;
-               }
+      /* 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 (ex.error != NOT_FOUND_ERROR)
-               throw_exception (ex);
+         /* 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] = { (char) tracker.quote_char () };
 
-             *argptr = saved_argptr;
+             fn = reconcat (fn, fn, quote_char_str, (char *) NULL);
+             tracker.set_quote_char (':');
            }
-       }
-      else
-       {
-         /* If there was an exception looking up a specified filename earlier,
-            then check whether we were really given `function:label'.   */
-         if (file_exception.reason < 0)
-           {
-             function_symbols = find_function_symbols (argptr, p,
-                                                       is_quote_enclosed,
-                                                       &self->user_function);
+         else
+           fn = reconcat (fn, fn, ":", (char *) NULL);
+         fn_list[0].reset (fn);
 
-             /* If we did not find a function, re-throw the original
-                exception.  */
-             if (!function_symbols)
-               throw_exception (file_exception);
+         /* Tell readline to skip appending a space.  */
+         tracker.set_suppress_append_ws (true);
+       }
+      tracker.add_completions (std::move (fn_list));
+    }
+}
 
-             make_cleanup (VEC_cleanup (symbolp), &function_symbols);
-           }
+/* 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.  */
 
-         /* Check for single quotes on the non-filename part.  */
-         if (!is_quoted)
-           {
-             is_quoted = (**argptr
-                          && strchr (get_gdb_completer_quote_characters (),
-                                     **argptr) != NULL);
-             if (is_quoted)
-               end_quote = skip_quoted (*argptr);
-           }
+static void
+complete_label (completion_tracker &tracker,
+               linespec_parser *parser,
+               const char *label_name)
+{
+  std::vector<block_symbol> label_function_symbols;
+  std::vector<block_symbol> *labels
+    = find_label_symbols (PARSER_STATE (parser),
+                         PARSER_RESULT (parser)->function_symbols,
+                         &label_function_symbols,
+                         label_name, true);
+
+  if (labels != nullptr)
+    {
+      for (const auto &label : *labels)
+       {
+         char *match = xstrdup (label.symbol->search_name ());
+         tracker.add_completion (gdb::unique_xmalloc_ptr<char> (match));
        }
+      delete labels;
     }
+}
 
-  /* self->file_symtabs holds the  specified file symtabs, or 0 if no file
-     specified.
-     If we are parsing `function:symbol', then FUNCTION_SYMBOLS holds the
-     functions before the `:'.
-     arg no longer contains the file name.  */
+/* See linespec.h.  */
+
+void
+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 (0, language, NULL, NULL, 0, NULL);
 
-  /* If the filename was quoted, we must re-check the quotation.  */
+  line_offset unknown_offset = { 0, LINE_OFFSET_UNKNOWN };
 
-  if (end_quote == first_half && *end_quote!= '\0')
+  try
     {
-      is_quoted = (**argptr
-                  && strchr (get_gdb_completer_quote_characters (),
-                             **argptr) != NULL);
-      if (is_quoted)
-       end_quote = skip_quoted (*argptr);
+      convert_explicit_location_to_linespec (PARSER_STATE (&parser),
+                                            PARSER_RESULT (&parser),
+                                            source_filename,
+                                            function_name,
+                                            func_name_match_type,
+                                            NULL, unknown_offset);
+    }
+  catch (const gdb_exception_error &ex)
+    {
+      return;
     }
 
-  /* Check whether arg is all digits (and sign).  */
+  complete_label (tracker, &parser, label_name);
+}
 
-  q = *argptr;
-  if (*q == '-' || *q == '+')
-    q++;
-  while (*q >= '0' && *q <= '9')
-    q++;
+/* See description in linespec.h.  */
 
-  if (q != *argptr && (*q == 0 || *q == ' ' || *q == '\t' || *q == ',')
-      && function_symbols == NULL)
-    {
-      struct symtabs_and_lines values;
+void
+linespec_complete (completion_tracker &tracker, const char *text,
+                  symbol_name_match_type match_type)
+{
+  const char *orig = text;
 
-      /* We found a token consisting of all digits -- at least one digit.  */
-      values = decode_all_digits (self, argptr, q);
-      do_cleanups (cleanup);
-      return values;
-    }
+  linespec_parser parser (0, current_language, NULL, NULL, 0, NULL);
+  parser.lexer.saved_arg = text;
+  PARSER_EXPLICIT (&parser)->func_name_match_type = match_type;
+  PARSER_STREAM (&parser) = text;
 
-  /* Arg token is not digits => try it as a variable name
-     Find the next token (everything up to end or next whitespace).  */
+  parser.completion_tracker = &tracker;
+  PARSER_STATE (&parser)->is_linespec = 1;
 
-  if (**argptr == '$')         /* May be a convenience variable.  */
-    /* One or two $ chars possible.  */
-    p = skip_quoted (*argptr + (((*argptr)[1] == '$') ? 2 : 1));
-  else if (is_quoted || is_squote_enclosed)
+  /* Parse as much as possible.  parser.completion_word will hold
+     furthest completion point we managed to parse to.  */
+  try
     {
-      p = end_quote;
-      if (p[-1] != '\'')
-       error (_("Unmatched single quote."));
+      parse_linespec (&parser, text, match_type);
     }
-  else if (is_objc_method)
+  catch (const gdb_exception_error &except)
     {
-      /* allow word separators in method names for Obj-C.  */
-      p = skip_quoted_chars (*argptr, NULL, "");
     }
-  else
+
+  if (parser.completion_quote_char != '\0'
+      && parser.completion_quote_end != NULL
+      && parser.completion_quote_end[1] == '\0')
     {
-      p = skip_quoted (*argptr);
-    }
+      /* 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:
 
-  /* Keep any important naming information.  */
-  p = keep_name_info (p, p == saved_arg || is_linespec_boundary (p[-1]));
+          before: "b function()"
+          after:  "b function() "
 
-  copy = (char *) alloca (p - *argptr + 1);
-  memcpy (copy, *argptr, p - *argptr);
-  copy[p - *argptr] = '\0';
-  if (p != *argptr
-      && copy[0]
-      && copy[0] == copy[p - *argptr - 1]
-      && strchr (get_gdb_completer_quote_characters (), copy[0]) != NULL)
-    {
-      copy[p - *argptr - 1] = '\0';
-      copy++;
+          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));
     }
-  else if (is_quoted || is_squote_enclosed)
-    copy[p - *argptr - 1] = '\0';
-  
-  *argptr = skip_spaces (p);
 
-  /* If it starts with $: may be a legitimate variable or routine name
-     (e.g. HP-UX millicode routines such as $$dyncall), or it may
-     be history value, or it may be a convenience variable.  */
+  tracker.set_quote_char (parser.completion_quote_char);
 
-  if (*copy == '$' && function_symbols == NULL)
+  if (parser.complete_what == linespec_complete_what::LABEL)
     {
-      struct symtabs_and_lines values;
+      parser.complete_what = linespec_complete_what::NOTHING;
 
-      values = decode_dollar (self, copy);
-      do_cleanups (cleanup);
-      return values;
-    }
+      const char *func_name = PARSER_EXPLICIT (&parser)->function_name;
+
+      std::vector<block_symbol> function_symbols;
+      std::vector<bound_minimal_symbol> minimal_symbols;
+      find_linespec_symbols (PARSER_STATE (&parser),
+                            PARSER_RESULT (&parser)->file_symtabs,
+                            func_name, match_type,
+                            &function_symbols, &minimal_symbols);
 
-  /* Try the token as a label, but only if no file was specified,
-     because we can only really find labels in the current scope.  */
+      PARSER_RESULT (&parser)->function_symbols
+       = new std::vector<block_symbol> (std::move (function_symbols));
+      PARSER_RESULT (&parser)->minimal_symbols
+       = new std::vector<bound_minimal_symbol> (std::move (minimal_symbols));
 
-  if (VEC_length (symtab_p, self->file_symtabs) == 1
-      && VEC_index (symtab_p, self->file_symtabs, 0) == NULL)
+      complete_label (tracker, &parser, parser.completion_word);
+    }
+  else if (parser.complete_what == linespec_complete_what::FUNCTION)
     {
-      struct symtabs_and_lines label_result;
-      if (decode_label (self, function_symbols, copy, &label_result))
+      /* 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 *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 ())
        {
-         do_cleanups (cleanup);
-         return label_result;
+         /* 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);
 
-  if (function_symbols)
-    throw_exception (file_exception);
+         key_start
+           = string_find_incomplete_keyword_at_end (linespec_keywords,
+                                                    parser.completion_word,
+                                                    wordlen);
 
-  /* Look up that token as a variable.
-     If file specified, use that file's per-file block to start with.  */
+         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 ();
+       }
+    }
 
-  {
-    struct symtabs_and_lines values;
+  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);
 
-    values = decode_variable (self, copy);
-    do_cleanups (cleanup);
-    return values;
-  }
+      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);
+       }
+    }
 }
 
-/* A constructor for linespec_state.  */
+/* A helper function for decode_line_full and decode_line_1 to
+   turn LOCATION into std::vector<symtab_and_line>.  */
 
-static void
-linespec_state_constructor (struct linespec_state *self,
-                           int flags,
-                           struct symtab *default_symtab,
-                           int default_line,
-                           struct linespec_result *canonical)
+static std::vector<symtab_and_line>
+event_location_to_sals (linespec_parser *parser,
+                       const struct event_location *location)
 {
-  memset (self, 0, sizeof (*self));
-  self->funfirstline = (flags & DECODE_LINE_FUNFIRSTLINE) ? 1 : 0;
-  self->list_mode = (flags & DECODE_LINE_LIST_MODE) ? 1 : 0;
-  self->default_symtab = default_symtab;
-  self->default_line = default_line;
-  self->canonical = canonical;
-  self->program_space = current_program_space;
-  self->addr_set = htab_create_alloc (10, hash_address_entry, eq_address_entry,
-                                     xfree, xcalloc, xfree);
-}
+  std::vector<symtab_and_line> result;
 
-/* A destructor for linespec_state.  */
+  switch (event_location_type (location))
+    {
+    case LINESPEC_LOCATION:
+      {
+       PARSER_STATE (parser)->is_linespec = 1;
+       try
+         {
+           const linespec_location *ls = get_linespec_location (location);
+           result = parse_linespec (parser,
+                                    ls->spec_string, ls->match_type);
+         }
+       catch (const gdb_exception_error &except)
+         {
+           throw;
+         }
+      }
+      break;
 
-static void
-linespec_state_destructor (void *arg)
-{
-  struct linespec_state *self = arg;
+    case ADDRESS_LOCATION:
+      {
+       const char *addr_string = get_address_string_location (location);
+       CORE_ADDR addr = get_address_location (location);
 
-  xfree (self->user_filename);
-  xfree (self->user_function);
-  VEC_free (symtab_p, self->file_symtabs);
-  htab_delete (self->addr_set);
+       if (addr_string != NULL)
+         {
+           addr = linespec_expression_to_pc (&addr_string);
+           if (PARSER_STATE (parser)->canonical != NULL)
+             PARSER_STATE (parser)->canonical->location
+               = copy_event_location (location);
+         }
+
+       result = convert_address_location_to_sals (PARSER_STATE (parser),
+                                                  addr);
+      }
+      break;
+
+    case EXPLICIT_LOCATION:
+      {
+       const struct explicit_location *explicit_loc;
+
+       explicit_loc = get_explicit_location_const (location);
+       result = convert_explicit_location_to_sals (PARSER_STATE (parser),
+                                                   PARSER_RESULT (parser),
+                                                   explicit_loc);
+      }
+      break;
+
+    case PROBE_LOCATION:
+      /* Probes are handled by their own decoders.  */
+      gdb_assert_not_reached ("attempt to decode probe location");
+      break;
+
+    default:
+      gdb_assert_not_reached ("unhandled event location type");
+    }
+
+  return result;
 }
 
 /* See linespec.h.  */
 
 void
-decode_line_full (char **argptr, int flags,
+decode_line_full (const struct event_location *location, int flags,
+                 struct program_space *search_pspace,
                  struct symtab *default_symtab,
                  int default_line, struct linespec_result *canonical,
                  const char *select_mode,
                  const char *filter)
 {
-  struct symtabs_and_lines result;
-  struct linespec_state state;
-  struct cleanup *cleanups;
-  char *arg_start = *argptr;
-  VEC (const_char_ptr) *filters = NULL;
+  std::vector<const char *> filters;
+  struct linespec_state *state;
 
   gdb_assert (canonical != NULL);
   /* The filter only makes sense for 'all'.  */
@@ -1208,37 +3222,30 @@ decode_line_full (char **argptr, int flags,
              || select_mode == multiple_symbols_cancel);
   gdb_assert ((flags & DECODE_LINE_LIST_MODE) == 0);
 
-  linespec_state_constructor (&state, flags,
-                             default_symtab, default_line, canonical);
-  cleanups = make_cleanup (linespec_state_destructor, &state);
-  save_current_program_space ();
+  linespec_parser parser (flags, current_language,
+                         search_pspace, default_symtab,
+                         default_line, canonical);
+
+  scoped_restore_current_program_space restore_pspace;
 
-  result = decode_line_internal (&state, argptr);
+  std::vector<symtab_and_line> result = event_location_to_sals (&parser,
+                                                               location);
+  state = PARSER_STATE (&parser);
 
-  gdb_assert (result.nelts == 1 || canonical->pre_expanded);
-  gdb_assert (canonical->addr_string != NULL);
+  gdb_assert (result.size () == 1 || canonical->pre_expanded);
   canonical->pre_expanded = 1;
 
-  /* Fill in the missing canonical names.  */
-  if (result.nelts > 0)
+  /* Arrange for allocated canonical names to be freed.  */
+  std::vector<gdb::unique_xmalloc_ptr<char>> hold_names;
+  for (int i = 0; i < result.size (); ++i)
     {
-      int i;
-
-      if (state.canonical_names == NULL)
-       state.canonical_names = xcalloc (result.nelts, sizeof (char *));
-      make_cleanup (xfree, state.canonical_names);
-      for (i = 0; i < result.nelts; ++i)
-       {
-         if (state.canonical_names[i] == NULL)
-           state.canonical_names[i] = savestring (arg_start,
-                                                  *argptr - arg_start);
-         make_cleanup (xfree, state.canonical_names[i]);
-       }
+      gdb_assert (state->canonical_names[i].suffix != NULL);
+      hold_names.emplace_back (state->canonical_names[i].suffix);
     }
 
   if (select_mode == NULL)
     {
-      if (ui_out_is_mi_like_p (interp_ui_out (top_level_interpreter ())))
+      if (top_level_interpreter ()->interp_ui_out ()->is_mi_like_p ())
        select_mode = multiple_symbols_all;
       else
        select_mode = multiple_symbols_select_mode ();
@@ -1248,41 +3255,82 @@ decode_line_full (char **argptr, int flags,
     {
       if (filter != NULL)
        {
-         make_cleanup (VEC_cleanup (const_char_ptr), &filters);
-         VEC_safe_push (const_char_ptr, filters, filter);
-         filter_results (&state, &result, filters);
+         filters.push_back (filter);
+         filter_results (state, &result, filters);
        }
       else
-       convert_results_to_lsals (&state, &result);
+       convert_results_to_lsals (state, &result);
     }
   else
-    decode_line_2 (&state, &result, select_mode);
-
-  do_cleanups (cleanups);
+    decode_line_2 (state, &result, select_mode);
 }
 
-struct symtabs_and_lines
-decode_line_1 (char **argptr, int flags,
+/* See linespec.h.  */
+
+std::vector<symtab_and_line>
+decode_line_1 (const struct event_location *location, int flags,
+              struct program_space *search_pspace,
               struct symtab *default_symtab,
               int default_line)
 {
-  struct symtabs_and_lines result;
-  struct linespec_state state;
-  struct cleanup *cleanups;
+  linespec_parser parser (flags, current_language,
+                         search_pspace, default_symtab,
+                         default_line, NULL);
 
-  linespec_state_constructor (&state, flags,
-                             default_symtab, default_line, NULL);
-  cleanups = make_cleanup (linespec_state_destructor, &state);
-  save_current_program_space ();
+  scoped_restore_current_program_space restore_pspace;
 
-  result = decode_line_internal (&state, argptr);
-  do_cleanups (cleanups);
-  return result;
+  return event_location_to_sals (&parser, location);
+}
+
+/* See linespec.h.  */
+
+std::vector<symtab_and_line>
+decode_line_with_current_source (const char *string, int flags)
+{
+  if (string == 0)
+    error (_("Empty line specification."));
+
+  /* We use whatever is set as the current source line.  We do not try
+     and get a default source symtab+line or it will recursively call us!  */
+  symtab_and_line cursal = get_current_source_symtab_and_line ();
+
+  event_location_up location = string_to_event_location (&string,
+                                                        current_language);
+  std::vector<symtab_and_line> sals
+    = decode_line_1 (location.get (), flags, NULL, cursal.symtab, cursal.line);
+
+  if (*string)
+    error (_("Junk at end of line specification: %s"), string);
+
+  return sals;
+}
+
+/* See linespec.h.  */
+
+std::vector<symtab_and_line>
+decode_line_with_last_displayed (const char *string, int flags)
+{
+  if (string == 0)
+    error (_("Empty line specification."));
+
+  event_location_up location = string_to_event_location (&string,
+                                                        current_language);
+  std::vector<symtab_and_line> sals
+    = (last_displayed_sal_is_valid ()
+       ? decode_line_1 (location.get (), flags, NULL,
+                       get_last_displayed_symtab (),
+                       get_last_displayed_line ())
+       : decode_line_1 (location.get (), flags, NULL, NULL, 0));
+
+  if (*string)
+    error (_("Junk at end of line specification: %s"), string);
+
+  return sals;
 }
 
 \f
 
-/* First, some functions to initialize stuff at the beggining of the
+/* First, some functions to initialize stuff at the beginning of the
    function.  */
 
 static void
@@ -1303,178 +3351,20 @@ initialize_defaults (struct symtab **default_symtab, int *default_line)
 
 \f
 
-/* Decode arg of the form *PC.  */
+/* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
+   advancing EXP_PTR past any parsed text.  */
 
-static struct symtabs_and_lines
-decode_indirect (struct linespec_state *self, char **argptr)
+CORE_ADDR
+linespec_expression_to_pc (const char **exp_ptr)
 {
-  struct symtabs_and_lines values;
-  CORE_ADDR pc;
-  char *initial = *argptr;
-  
   if (current_program_space->executing_startup)
     /* The error message doesn't really matter, because this case
        should only hit during breakpoint reset.  */
     throw_error (NOT_FOUND_ERROR, _("cannot evaluate expressions while "
                                    "program space is in startup"));
 
-  (*argptr)++;
-  pc = value_as_address (parse_to_comma_and_eval (argptr));
-
-  values.sals = (struct symtab_and_line *)
-    xmalloc (sizeof (struct symtab_and_line));
-
-  values.nelts = 1;
-  values.sals[0] = find_pc_line (pc, 0);
-  values.sals[0].pc = pc;
-  values.sals[0].section = find_pc_overlay (pc);
-  values.sals[0].explicit_pc = 1;
-
-  if (self->canonical)
-    self->canonical->addr_string = savestring (initial, *argptr - initial);
-
-  return values;
-}
-
-\f
-
-/* Locate the first half of the linespec, ending in a colon, period,
-   or whitespace.  (More or less.)  Also, check to see if *ARGPTR is
-   enclosed in double quotes; if so, set is_quote_enclosed, advance
-   ARGPTR past that and zero out the trailing double quote.
-   If ARGPTR is just a simple name like "main", p will point to ""
-   at the end.  */
-
-static char *
-locate_first_half (char **argptr, int *is_quote_enclosed)
-{
-  char *ii;
-  char *p, *p1;
-  int has_comma;
-
-  /* Check if the linespec starts with an Ada operator (such as "+",
-     or ">", for instance).  */
-  p = *argptr;
-  if (p[0] == '"'
-      && current_language->la_language == language_ada)
-    {
-      const struct ada_opname_map *op;
-
-      for (op = ada_opname_table; op->encoded != NULL; op++)
-        if (strncmp (op->decoded, p, strlen (op->decoded)) == 0)
-         break;
-      if (op->encoded != NULL)
-       {
-         *is_quote_enclosed = 0;
-         return p + strlen (op->decoded);
-       }
-    }
-
-  /* Maybe we were called with a line range FILENAME:LINENUM,FILENAME:LINENUM
-     and we must isolate the first half.  Outer layers will call again later
-     for the second half.
-
-     Don't count commas that appear in argument lists of overloaded
-     functions, or in quoted strings.  It's stupid to go to this much
-     trouble when the rest of the function is such an obvious roach hotel.  */
-  ii = find_toplevel_char (*argptr, ',');
-  has_comma = (ii != 0);
-
-  /* Temporarily zap out second half to not confuse the code below.
-     This is undone below.  Do not change ii!!  */
-  if (has_comma)
-    {
-      *ii = '\0';
-    }
-
-  /* Maybe arg is FILE : LINENUM or FILE : FUNCTION.  May also be
-     CLASS::MEMBER, or NAMESPACE::NAME.  Look for ':', but ignore
-     inside of <>.  */
-
-  p = *argptr;
-  if (p[0] == '"')
-    {
-      *is_quote_enclosed = 1;
-      (*argptr)++;
-      p++;
-    }
-  else
-    {
-      *is_quote_enclosed = 0;
-      if (strchr (get_gdb_completer_quote_characters (), *p))
-       {
-         ++(*argptr);
-         ++p;
-       }
-    }
-
-
-  /* Check for a drive letter in the filename.  This is done on all hosts
-     to capture cross-compilation environments.  On Unixen, directory
-     separators are illegal in filenames, so if the user enters "e:/foo.c",
-     he is referring to a directory named "e:" and a source file named
-     "foo.c", and we still want to keep these two pieces together.  */
-  if (isalpha (p[0]) && p[1] == ':' && IS_DIR_SEPARATOR (p[2]))
-    p += 3;
-
-  for (; *p; p++)
-    {
-      if (p[0] == '<')
-       {
-         char *temp_end = find_template_name_end (p);
-
-         if (!temp_end)
-           error (_("malformed template specification in command"));
-         p = temp_end;
-       }
-
-      if (p[0] == '(')
-       p = find_method_overload_end (p);
-
-      /* Check for a colon and a plus or minus and a [ (which
-         indicates an Objective-C method).  */
-      if (is_objc_method_format (p))
-       {
-         break;
-       }
-      /* Check for the end of the first half of the linespec.  End of
-         line, a tab, a colon or a space.  But if enclosed in double
-        quotes we do not break on enclosed spaces.  */
-      if (!*p
-         || p[0] == '\t'
-         || (p[0] == ':')
-         || ((p[0] == ' ') && !*is_quote_enclosed))
-       break;
-      if (p[0] == '.' && strchr (p, ':') == NULL)
-       {
-         /* Java qualified method.  Find the *last* '.', since the
-            others are package qualifiers.  Stop at any open parenthesis
-            which might provide overload information.  */
-         for (p1 = p; *p1 && *p1 != '('; p1++)
-           {
-             if (*p1 == '.')
-               p = p1;
-           }
-         break;
-       }
-    }
-  p = skip_spaces (p);
-
-  /* If the closing double quote was left at the end, remove it.  */
-  if (*is_quote_enclosed)
-    {
-      char *closing_quote = strchr (p - 1, '"');
-
-      if (closing_quote && closing_quote[1] == '\0')
-       *closing_quote = '\0';
-    }
-
-  /* Now that we've safely parsed the first half, put back ',' so
-     outer layers can see it.  */
-  if (has_comma)
-    *ii = ',';
-
-  return p;
+  (*exp_ptr)++;
+  return value_as_address (parse_to_comma_and_eval (exp_ptr));
 }
 
 \f
@@ -1486,436 +3376,224 @@ locate_first_half (char **argptr, int *is_quote_enclosed)
    than one method that could represent the selector, then use some of
    the existing C++ code to let the user choose one.  */
 
-static struct symtabs_and_lines
-decode_objc (struct linespec_state *self, char **argptr)
+static std::vector<symtab_and_line>
+decode_objc (struct linespec_state *self, linespec_p ls, const char *arg)
 {
   struct collect_info info;
-  VEC (const_char_ptr) *symbol_names = NULL;
-  char *new_argptr;
-  struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr),
-                                         &symbol_names);
+  std::vector<const char *> symbol_names;
+  const char *new_argptr;
 
   info.state = self;
-  info.result.sals = NULL;
-  info.result.nelts = 0;
-
-  new_argptr = find_imps (*argptr, &symbol_names); 
-  if (VEC_empty (const_char_ptr, symbol_names))
-    {
-      do_cleanups (cleanup);
-      return info.result;
-    }
-
-  add_all_symbol_names_from_pspace (&info, NULL, symbol_names);
-
-  if (info.result.nelts > 0)
-    {
-      char *saved_arg;
-
-      saved_arg = alloca (new_argptr - *argptr + 1);
-      memcpy (saved_arg, *argptr, new_argptr - *argptr);
-      saved_arg[new_argptr - *argptr] = '\0';
-
-      if (self->canonical)
-       {
-         self->canonical->pre_expanded = 1;
-         if (self->user_filename)
-           self->canonical->addr_string
-             = xstrprintf ("%s:%s", self->user_filename, saved_arg);
-         else
-           self->canonical->addr_string = xstrdup (saved_arg);
-       }
-    }
-
-  *argptr = new_argptr;
-
-  do_cleanups (cleanup);
-  return info.result;
-}
-
-/* This handles C++ and Java compound data structures.  P should point
-   at the first component separator, i.e. double-colon or period.  As
-   an example, on entrance to this function we could have ARGPTR
-   pointing to "AAA::inA::fun" and P pointing to "::inA::fun".  */
-
-static struct symtabs_and_lines
-decode_compound (struct linespec_state *self,
-                char **argptr, char *the_real_saved_arg, char *p)
-{
-  struct symtabs_and_lines values;
-  char *p2;
-  char *saved_arg2 = *argptr;
-  char *temp_end;
-  struct symbol *sym;
-  char *copy;
-  VEC (symbolp) *sym_classes;
-  char *saved_arg, *class_name;
-  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
-
-  /* If the user specified any completer quote characters in the input,
-     strip them.  They are superfluous.  */
-  saved_arg = alloca (strlen (the_real_saved_arg) + 1);
-  {
-    char *dst = saved_arg;
-    char *src = the_real_saved_arg;
-    char *quotes = get_gdb_completer_quote_characters ();
-    while (*src != '\0')
-      {
-       if (strchr (quotes, *src) == NULL)
-         *dst++ = *src;
-       ++src;
-      }
-    *dst = '\0';
-  }
-
-  /* First check for "global" namespace specification, of the form
-     "::foo".  If found, skip over the colons and jump to normal
-     symbol processing.  I.e. the whole line specification starts with
-     "::" (note the condition that *argptr == p).  */
-  if (p[0] == ':' 
-      && ((*argptr == p) || (p[-1] == ' ') || (p[-1] == '\t')))
-    saved_arg2 += 2;
-
-  /* Given our example "AAA::inA::fun", we have two cases to consider:
-
-     1) AAA::inA is the name of a class.  In that case, presumably it
-        has a method called "fun"; we then look up that method using
-        find_method.
+  std::vector<symtab *> symtabs;
+  symtabs.push_back (nullptr);
 
-     2) AAA::inA isn't the name of a class.  In that case, either the
-        user made a typo, AAA::inA is the name of a namespace, or it is
-        the name of a minimal symbol.
-       In this case we just delegate to decode_variable.
+  info.file_symtabs = &symtabs;
 
-     Thus, our first task is to find everything before the last set of
-     double-colons and figure out if it's the name of a class.  So we
-     first loop through all of the double-colons.  */
+  std::vector<block_symbol> symbols;
+  info.result.symbols = &symbols;
+  std::vector<bound_minimal_symbol> minimal_symbols;
+  info.result.minimal_symbols = &minimal_symbols;
 
-  p2 = p;              /* Save for restart.  */
+  new_argptr = find_imps (arg, &symbol_names);
+  if (symbol_names.empty ())
+    return {};
 
-  /* This is very messy.  Following the example above we have now the
-     following pointers:
-     p -> "::inA::fun"
-     argptr -> "AAA::inA::fun
-     saved_arg -> "AAA::inA::fun
-     saved_arg2 -> "AAA::inA::fun
-     p2 -> "::inA::fun".  */
+  add_all_symbol_names_from_pspace (&info, NULL, symbol_names,
+                                   FUNCTIONS_DOMAIN);
 
-  /* In the loop below, with these strings, we'll make 2 passes, each
-     is marked in comments.  */
-
-  while (1)
+  std::vector<symtab_and_line> values;
+  if (!symbols.empty () || !minimal_symbols.empty ())
     {
-      static char *break_characters = " \t(";
-
-      /* Move pointer up to next possible class/namespace token.  */
+      char *saved_arg;
 
-      p = p2 + 1;      /* Restart with old value +1.  */
+      saved_arg = (char *) alloca (new_argptr - arg + 1);
+      memcpy (saved_arg, arg, new_argptr - arg);
+      saved_arg[new_argptr - arg] = '\0';
 
-      /* PASS1: at this point p2->"::inA::fun", so p->":inA::fun",
-        i.e. if there is a double-colon, p will now point to the
-        second colon.  */
-      /* PASS2: p2->"::fun", p->":fun" */
+      ls->explicit_loc.function_name = xstrdup (saved_arg);
+      ls->function_symbols
+       = new std::vector<block_symbol> (std::move (symbols));
+      ls->minimal_symbols
+       = new std::vector<bound_minimal_symbol> (std::move (minimal_symbols));
+      values = convert_linespec_to_sals (self, ls);
 
-      /* Move pointer ahead to next double-colon.  */
-      while (*p
-            && strchr (break_characters, *p) == NULL
-            && strchr (get_gdb_completer_quote_characters (), *p) == NULL)
+      if (self->canonical)
        {
-         if (current_language->la_language == language_cplus)
-           p += cp_validate_operator (p);
-
-         if (p[0] == '<')
-           {
-             temp_end = find_template_name_end (p);
-             if (!temp_end)
-               error (_("malformed template specification in command"));
-             p = temp_end;
-           }
-         /* Note that, since, at the start of this loop, p would be
-            pointing to the second colon in a double-colon, we only
-            satisfy the condition below if there is another
-            double-colon to the right (after).  I.e. there is another
-            component that can be a class or a namespace.  I.e, if at
-            the beginning of this loop (PASS1), we had
-            p->":inA::fun", we'll trigger this when p has been
-            advanced to point to "::fun".  */
-         /* PASS2: we will not trigger this.  */
-         else if ((p[0] == ':') && (p[1] == ':'))
-           break;      /* Found double-colon.  */
-         else
-           {
-             /* PASS2: We'll keep getting here, until P points to one of the
-                break characters, at which point we exit this loop.  */
-             if (*p)
-               {
-                 if (p[1] == '('
-                     && strncmp (&p[1], CP_ANONYMOUS_NAMESPACE_STR,
-                                 CP_ANONYMOUS_NAMESPACE_LEN) == 0)
-                   p += CP_ANONYMOUS_NAMESPACE_LEN;
-                 else if (strchr (break_characters, *p) == NULL)
-                   ++p;
-               }
-           }
-       }
-
-      if (*p != ':')
-       break;          /* Out of the while (1).  This would happen
-                          for instance if we have looked up
-                          unsuccessfully all the components of the
-                          string, and p->""(PASS2).  */
-
-      /* We get here if p points to one of the break characters or "" (i.e.,
-        string ended).  */
-      /* Save restart for next time around.  */
-      p2 = p;
-      /* Restore argptr as it was on entry to this function.  */
-      *argptr = saved_arg2;
-      /* PASS1: at this point p->"::fun" argptr->"AAA::inA::fun",
-        p2->"::fun".  */
+         std::string holder;
+         const char *str;
 
-      /* All ready for next pass through the loop.  */
-    }                  /* while (1) */
-
-
-  /* Start of lookup in the symbol tables.  */
-
-  /* Lookup in the symbol table the substring between argptr and
-     p.  Note, this call changes the value of argptr.  */
-  /* Before the call, argptr->"AAA::inA::fun",
-     p->"", p2->"::fun".  After the call: argptr->"fun", p, p2
-     unchanged.  */
-  sym_classes = lookup_prefix_sym (argptr, p2, self->file_symtabs,
-                                  &class_name);
-  make_cleanup (VEC_cleanup (symbolp), &sym_classes);
-  make_cleanup (xfree, class_name);
+         self->canonical->pre_expanded = 1;
 
-  /* If a class has been found, then we're in case 1 above.  So we
-     look up "fun" as a method of those classes.  */
-  if (!VEC_empty (symbolp, sym_classes))
-    {
-      /* Arg token is not digits => try it as a function name.
-        Find the next token (everything up to end or next
-        blank).  */
-      if (**argptr
-         && strchr (get_gdb_completer_quote_characters (),
-                    **argptr) != NULL)
-       {
-         p = skip_quoted (*argptr);
-         *argptr = *argptr + 1;
-       }
-      else
-       {
-         /* At this point argptr->"fun".  */
-         char *a;
-
-         p = *argptr;
-         while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p != ':'
-                && *p != '(')
-           p++;
-         /* At this point p->"".  String ended.  */
-         /* Nope, C++ operators could have spaces in them
-            ("foo::operator <" or "foo::operator delete []").
-            I apologize, this is a bit hacky...  */
-         if (current_language->la_language == language_cplus
-             && *p == ' ' && p - 8 - *argptr + 1 > 0)
+         if (ls->explicit_loc.source_filename)
            {
-             /* The above loop has already swallowed "operator".  */
-             p += cp_validate_operator (p - 8) - 8;
+             holder = string_printf ("%s:%s",
+                                     ls->explicit_loc.source_filename,
+                                     saved_arg);
+             str = holder.c_str ();
            }
+         else
+           str = saved_arg;
 
-         /* Keep any important naming information.  */
-         p = keep_name_info (p, 1);
-       }
-
-      /* Allocate our own copy of the substring between argptr and
-        p.  */
-      copy = (char *) alloca (p - *argptr + 1);
-      memcpy (copy, *argptr, p - *argptr);
-      copy[p - *argptr] = '\0';
-      if (p != *argptr
-         && copy[p - *argptr - 1]
-         && strchr (get_gdb_completer_quote_characters (),
-                    copy[p - *argptr - 1]) != NULL)
-       copy[p - *argptr - 1] = '\0';
-
-      /* At this point copy->"fun", p->"".  */
-
-      /* No line number may be specified.  */
-      *argptr = skip_spaces (p);
-      /* At this point arptr->"".  */
-
-      /* Look for copy as a method of sym_class.  */
-      /* At this point copy->"fun", sym_class is "AAA:inA",
-        saved_arg->"AAA::inA::fun".  This concludes the scanning of
-        the string for possible components matches.  If we find it
-        here, we return.  If not, and we are at the and of the string,
-        we'll lookup the whole string in the symbol tables.  */
-
-      values = find_method (self, saved_arg, copy, class_name, sym_classes);
+         self->canonical->location
+           = new_linespec_location (&str, symbol_name_match_type::FULL);
+       }
+    }
 
-      do_cleanups (cleanup);
-      return values;
-    } /* End if symbol found.  */
+  return values;
+}
 
+namespace {
 
-  /* We couldn't find a class, so we're in case 2 above.  We check the
-     entire name as a symbol instead.  The simplest way to do this is
-     to just throw an exception and let our caller fall through to
-     decode_variable.  */
+/* A function object that serves as symbol_found_callback_ftype
+   callback for iterate_over_symbols.  This is used by
+   lookup_prefix_sym to collect type symbols.  */
+class decode_compound_collector
+{
+public:
+  decode_compound_collector ()
+  {
+    m_unique_syms = htab_create_alloc (1, htab_hash_pointer,
+                                      htab_eq_pointer, NULL,
+                                      xcalloc, xfree);
+  }
 
-  throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter"));
-}
+  ~decode_compound_collector ()
+  {
+    if (m_unique_syms != NULL)
+      htab_delete (m_unique_syms);
+  }
 
-/* An instance of this type is used when collecting prefix symbols for
-   decode_compound.  */
+  /* Return all symbols collected.  */
+  std::vector<block_symbol> release_symbols ()
+  {
+    return std::move (m_symbols);
+  }
 
-struct decode_compound_collector
-{
-  /* The result vector.  */
-  VEC (symbolp) *symbols;
+  /* Callable as a symbol_found_callback_ftype callback.  */
+  bool operator () (block_symbol *bsym);
 
+private:
   /* A hash table of all symbols we found.  We use this to avoid
      adding any symbol more than once.  */
-  htab_t unique_syms;
-};
+  htab_t m_unique_syms;
 
-/* A callback for iterate_over_symbols that is used by
-   lookup_prefix_sym to collect type symbols.  */
+  /* The result vector.  */
+  std::vector<block_symbol>  m_symbols;
+};
 
-static int
-collect_one_symbol (struct symbol *sym, void *d)
+bool
+decode_compound_collector::operator () (block_symbol *bsym)
 {
-  struct decode_compound_collector *collector = d;
   void **slot;
   struct type *t;
+  struct symbol *sym = bsym->symbol;
 
   if (SYMBOL_CLASS (sym) != LOC_TYPEDEF)
-    return 1;
+    return true; /* Continue iterating.  */
 
   t = SYMBOL_TYPE (sym);
-  CHECK_TYPEDEF (t);
+  t = check_typedef (t);
   if (TYPE_CODE (t) != TYPE_CODE_STRUCT
       && TYPE_CODE (t) != TYPE_CODE_UNION
       && TYPE_CODE (t) != TYPE_CODE_NAMESPACE)
-    return 1;
+    return true; /* Continue iterating.  */
 
-  slot = htab_find_slot (collector->unique_syms, sym, INSERT);
+  slot = htab_find_slot (m_unique_syms, sym, INSERT);
   if (!*slot)
     {
       *slot = sym;
-      VEC_safe_push (symbolp, collector->symbols, sym);
+      m_symbols.push_back (*bsym);
     }
 
-  return 1;
+  return true; /* Continue iterating.  */
 }
 
-/* Return the symbol corresponding to the substring of *ARGPTR ending
-   at P, allowing whitespace.  Also, advance *ARGPTR past the symbol
-   name in question, the compound object separator ("::" or "."), and
-   whitespace.  Note that *ARGPTR is changed whether or not the
-   this call finds anything (i.e we return NULL).  As an
-   example, say ARGPTR is "AAA::inA::fun" and P is "::inA::fun".  */
-
-static VEC (symbolp) *
-lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs,
-                  char **class_name)
-{
-  char *p1;
-  char *copy;
-  int ix;
-  struct symtab *elt;
-  struct decode_compound_collector collector;
-  struct cleanup *outer;
-  struct cleanup *cleanup;
-  struct block *search_block;
-
-  /* Extract the class name.  */
-  p1 = p;
-  while (p != *argptr && p[-1] == ' ')
-    --p;
-  copy = (char *) xmalloc (p - *argptr + 1);
-  memcpy (copy, *argptr, p - *argptr);
-  copy[p - *argptr] = 0;
-  *class_name = copy;
-  outer = make_cleanup (xfree, copy);
-
-  /* Discard the class name from the argptr.  */
-  p = p1 + (p1[0] == ':' ? 2 : 1);
-  p = skip_spaces (p);
-  *argptr = p;
+} // namespace
 
-  /* At this point p1->"::inA::fun", p->"inA::fun" copy->"AAA",
-     argptr->"inA::fun".  */
+/* Return any symbols corresponding to CLASS_NAME in FILE_SYMTABS.  */
 
-  collector.symbols = NULL;
-  make_cleanup (VEC_cleanup (symbolp), &collector.symbols);
+static std::vector<block_symbol>
+lookup_prefix_sym (struct linespec_state *state,
+                  std::vector<symtab *> *file_symtabs,
+                  const char *class_name)
+{
+  decode_compound_collector collector;
 
-  collector.unique_syms = htab_create_alloc (1, htab_hash_pointer,
-                                            htab_eq_pointer, NULL,
-                                            xcalloc, xfree);
-  cleanup = make_cleanup_htab_delete (collector.unique_syms);
+  lookup_name_info lookup_name (class_name, symbol_name_match_type::FULL);
 
-  for (ix = 0; VEC_iterate (symtab_p, file_symtabs, ix, elt); ++ix)
+  for (const auto &elt : *file_symtabs)
     {
-      if (elt == NULL)
+      if (elt == nullptr)
        {
-         iterate_over_all_matching_symtabs (copy, STRUCT_DOMAIN,
-                                            collect_one_symbol, &collector,
-                                            NULL);
-         iterate_over_all_matching_symtabs (copy, VAR_DOMAIN,
-                                            collect_one_symbol, &collector,
-                                            NULL);
+         iterate_over_all_matching_symtabs (state, lookup_name,
+                                            STRUCT_DOMAIN, ALL_DOMAIN,
+                                            NULL, false, collector);
+         iterate_over_all_matching_symtabs (state, lookup_name,
+                                            VAR_DOMAIN, ALL_DOMAIN,
+                                            NULL, false, collector);
        }
       else
        {
-         struct block *search_block;
-
          /* Program spaces that are executing startup should have
             been filtered out earlier.  */
          gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
          set_current_program_space (SYMTAB_PSPACE (elt));
-         search_block = get_search_block (elt);
-         LA_ITERATE_OVER_SYMBOLS (search_block, copy, STRUCT_DOMAIN,
-                                  collect_one_symbol, &collector);
-         LA_ITERATE_OVER_SYMBOLS (search_block, copy, VAR_DOMAIN,
-                                  collect_one_symbol, &collector);
+         iterate_over_file_blocks (elt, lookup_name, STRUCT_DOMAIN, collector);
+         iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN, collector);
        }
     }
 
-  do_cleanups (cleanup);
-  discard_cleanups (outer);
-  return collector.symbols;
+  return collector.release_symbols ();
 }
 
-/* A qsort comparison function for symbols.  The resulting order does
+/* A std::sort comparison function for symbols.  The resulting order does
    not actually matter; we just need to be able to sort them so that
    symbols with the same program space end up next to each other.  */
 
-static int
-compare_symbols (const void *a, const void *b)
+static bool
+compare_symbols (const block_symbol &a, const block_symbol &b)
 {
-  struct symbol * const *sa = a;
-  struct symbol * const *sb = b;
   uintptr_t uia, uib;
 
-  uia = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sa));
-  uib = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sb));
+  uia = (uintptr_t) SYMTAB_PSPACE (symbol_symtab (a.symbol));
+  uib = (uintptr_t) SYMTAB_PSPACE (symbol_symtab (b.symbol));
 
   if (uia < uib)
-    return -1;
+    return true;
   if (uia > uib)
-    return 1;
+    return false;
+
+  uia = (uintptr_t) a.symbol;
+  uib = (uintptr_t) b.symbol;
+
+  if (uia < uib)
+    return true;
+
+  return false;
+}
 
-  uia = (uintptr_t) *sa;
-  uib = (uintptr_t) *sb;
+/* Like compare_symbols but for minimal symbols.  */
+
+static bool
+compare_msymbols (const bound_minimal_symbol &a, const bound_minimal_symbol &b)
+{
+  uintptr_t uia, uib;
+
+  uia = (uintptr_t) a.objfile->pspace;
+  uib = (uintptr_t) a.objfile->pspace;
 
   if (uia < uib)
-    return -1;
+    return true;
   if (uia > uib)
-    return 1;
+    return false;
 
-  return 0;
+  uia = (uintptr_t) a.minsym;
+  uib = (uintptr_t) b.minsym;
+
+  if (uia < uib)
+    return true;
+
+  return false;
 }
 
 /* Look for all the matching instances of each symbol in NAMES.  Only
@@ -1926,84 +3604,64 @@ compare_symbols (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)
+                                 const std::vector<const char *> &names,
+                                 enum search_domain search_domain)
 {
-  int ix;
-  const char *iter;
-
-  for (ix = 0; VEC_iterate (const_char_ptr, names, ix, iter); ++ix)
-    add_matching_symbols_to_info (iter, info, pspace);
+  for (const char *iter : names)
+    add_matching_symbols_to_info (iter,
+                                 symbol_name_match_type::FULL,
+                                 search_domain, info, pspace);
 }
 
 static void
-find_superclass_methods (VEC (typep) *superclasses,
-                        const char *name,
-                        VEC (const_char_ptr) **result_names)
+find_superclass_methods (std::vector<struct type *> &&superclasses,
+                        const char *name, enum language name_lang,
+                        std::vector<const char *> *result_names)
 {
-  int old_len = VEC_length (const_char_ptr, *result_names);
-  VEC (typep) *iter_classes;
-  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+  size_t old_len = result_names->size ();
 
-  iter_classes = superclasses;
   while (1)
     {
-      VEC (typep) *new_supers = NULL;
-      int ix;
-      struct type *t;
+      std::vector<struct type *> new_supers;
 
-      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);
+      for (type *t : superclasses)
+       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))
+      if (result_names->size () != old_len || new_supers.empty ())
        break;
 
-      iter_classes = new_supers;
+      superclasses = std::move (new_supers);
     }
-
-  do_cleanups (cleanup);
 }
 
-/* This finds the method COPY in the class whose type is given by one
-   of the symbols in SYM_CLASSES.  */
+/* This finds the method METHOD_NAME in the class CLASS_NAME whose type is
+   given by one of the symbols in SYM_CLASSES.  Matches are returned
+   in SYMBOLS (for debug symbols) and MINSYMS (for minimal symbols).  */
 
-static struct symtabs_and_lines
-find_method (struct linespec_state *self, char *saved_arg,
-            char *copy, const char *class_name, VEC (symbolp) *sym_classes)
+static void
+find_method (struct linespec_state *self, std::vector<symtab *> *file_symtabs,
+            const char *class_name, const char *method_name,
+            std::vector<block_symbol> *sym_classes,
+            std::vector<block_symbol> *symbols,
+            std::vector<bound_minimal_symbol> *minsyms)
 {
-  char *canon;
-  struct symbol *sym;
-  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
-  int ix;
-  int last_result_len;
-  VEC (typep) *superclass_vec;
-  VEC (const_char_ptr) *result_names;
+  size_t last_result_len;
+  std::vector<struct type *> superclass_vec;
+  std::vector<const char *> result_names;
   struct collect_info info;
-  char *name_iter;
-
-  /* NAME is typed by the user: it needs to be canonicalized before
-     searching the symbol tables.  */
-  canon = cp_canonicalize_string_no_typedefs (copy);
-  if (canon != NULL)
-    {
-      copy = canon;
-      make_cleanup (xfree, copy);
-    }
 
   /* Sort symbols so that symbols with the same program space are next
      to each other.  */
-  qsort (VEC_address (symbolp, sym_classes),
-        VEC_length (symbolp, sym_classes),
-        sizeof (symbolp),
-        compare_symbols);
+  std::sort (sym_classes->begin (), sym_classes->end (),
+            compare_symbols);
 
   info.state = self;
-  info.result.sals = NULL;
-  info.result.nelts = 0;
+  info.file_symtabs = file_symtabs;
+  info.result.symbols = symbols;
+  info.result.minimal_symbols = minsyms;
 
   /* Iterate over all the types, looking for the names of existing
-     methods matching COPY.  If we cannot find a direct method in a
+     methods matching METHOD_NAME.  If we cannot find a direct method in a
      given program space, then we consider inherited methods; this is
      not ideal (ideal would be to respect C++ hiding rules), but it
      seems good enough and is what GDB has historically done.  We only
@@ -2011,760 +3669,588 @@ find_method (struct linespec_state *self, char *saved_arg,
      those names.  This loop is written in a somewhat funny way
      because we collect data across the program space before deciding
      what to do.  */
-  superclass_vec = NULL;
-  make_cleanup (VEC_cleanup (typep), &superclass_vec);
-  result_names = NULL;
-  make_cleanup (VEC_cleanup (const_char_ptr), &result_names);
   last_result_len = 0;
-  for (ix = 0; VEC_iterate (symbolp, sym_classes, ix, sym); ++ix)
+  unsigned int ix = 0;
+  for (const auto &elt : *sym_classes)
     {
       struct type *t;
       struct program_space *pspace;
+      struct symbol *sym = elt.symbol;
 
       /* Program spaces that are executing startup should have
         been filtered out earlier.  */
-      gdb_assert (!SYMTAB_PSPACE (SYMBOL_SYMTAB (sym))->executing_startup);
-      pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
+      pspace = SYMTAB_PSPACE (symbol_symtab (sym));
+      gdb_assert (!pspace->executing_startup);
       set_current_program_space (pspace);
       t = check_typedef (SYMBOL_TYPE (sym));
-      find_methods (t, copy, &result_names, &superclass_vec);
+      find_methods (t, sym->language (),
+                   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.  */
-      if (ix == VEC_length (symbolp, sym_classes) - 1
+      if (ix == sym_classes->size () - 1
          || (pspace
-             != SYMTAB_PSPACE (SYMBOL_SYMTAB (VEC_index (symbolp, sym_classes,
-                                                         ix + 1)))))
+             != SYMTAB_PSPACE (symbol_symtab (sym_classes->at (ix + 1).symbol))))
        {
          /* If we did not find a direct implementation anywhere in
             this program space, consider superclasses.  */
-         if (VEC_length (const_char_ptr, result_names) == last_result_len)
-           find_superclass_methods (superclass_vec, copy, &result_names);
+         if (result_names.size () == last_result_len)
+           find_superclass_methods (std::move (superclass_vec), method_name,
+                                    sym->language (), &result_names);
 
          /* 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);
+         superclass_vec.clear ();
+         last_result_len = result_names.size ();
+         ++ix;
        }
     }
 
-  if (info.result.nelts > 0)
-    {
-      if (self->canonical)
-       {
-         self->canonical->pre_expanded = 1;
-         if (self->user_filename)
-           self->canonical->addr_string
-             = xstrprintf ("%s:%s", self->user_filename, saved_arg);
-         else
-           self->canonical->addr_string = xstrdup (saved_arg);
-       }
-
-      do_cleanups (cleanup);
-
-      return info.result;
-    }
+  if (!symbols->empty () || !minsyms->empty ())
+    return;
 
-  if (copy[0] == '~')
-    cplusplus_error (saved_arg,
-                    "the class `%s' does not have destructor defined\n",
-                    class_name);
-  else
-    cplusplus_error (saved_arg,
-                    "the class %s does not have any method named %s\n",
-                    class_name, copy);
+  /* Throw an NOT_FOUND_ERROR.  This will be caught by the caller
+     and other attempts to locate the symbol will be made.  */
+  throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter"));
 }
 
 \f
 
-/* This object is used when collecting all matching symtabs.  */
-
-struct symtab_collector
-{
-  /* The result vector of symtabs.  */
-  VEC (symtab_p) *symtabs;
-
-  /* This is used to ensure the symtabs are unique.  */
-  htab_t symtab_table;
-};
+namespace {
 
-/* Callback for iterate_over_symtabs.  */
+/* This function object is a callback for iterate_over_symtabs, used
+   when collecting all matching symtabs.  */
 
-static int
-add_symtabs_to_list (struct symtab *symtab, void *d)
+class symtab_collector
 {
-  struct symtab_collector *data = d;
-  void **slot;
-
-  slot = htab_find_slot (data->symtab_table, symtab, INSERT);
-  if (!*slot)
-    {
-      *slot = symtab;
-      VEC_safe_push (symtab_p, data->symtabs, symtab);
-    }
-
-  return 0;
-}
-
-/* Given a file name, return a VEC of all matching symtabs.  */
+public:
+  symtab_collector ()
+  {
+    m_symtab_table = htab_create (1, htab_hash_pointer, htab_eq_pointer,
+                                 NULL);
+  }
 
-static VEC (symtab_p) *
-collect_symtabs_from_filename (const char *file)
-{
-  struct symtab_collector collector;
-  struct cleanup *cleanups;
-  struct program_space *pspace;
+  ~symtab_collector ()
+  {
+    if (m_symtab_table != NULL)
+      htab_delete (m_symtab_table);
+  }
 
-  collector.symtabs = NULL;
-  collector.symtab_table = htab_create (1, htab_hash_pointer, htab_eq_pointer,
-                                       NULL);
-  cleanups = make_cleanup_htab_delete (collector.symtab_table);
+  /* Callable as a symbol_found_callback_ftype callback.  */
+  bool operator () (symtab *sym);
 
-  /* Find that file's data.  */
-  ALL_PSPACES (pspace)
+  /* Return an rvalue reference to the collected symtabs.  */
+  std::vector<symtab *> &&release_symtabs ()
   {
-    if (pspace->executing_startup)
-      continue;
-
-    set_current_program_space (pspace);
-    iterate_over_symtabs (file, add_symtabs_to_list, &collector);
+    return std::move (m_symtabs);
   }
 
-  do_cleanups (cleanups);
-  return collector.symtabs;
-}
+private:
+  /* The result vector of symtabs.  */
+  std::vector<symtab *> m_symtabs;
 
-/* Return all the symtabs associated to the filename given by the
-   substring of *ARGPTR ending at P, and advance ARGPTR past that
-   filename.  */
+  /* This is used to ensure the symtabs are unique.  */
+  htab_t m_symtab_table;
+};
 
-static VEC (symtab_p) *
-symtabs_from_filename (char **argptr, char *p, int is_quote_enclosed,
-                      char **user_filename)
+bool
+symtab_collector::operator () (struct symtab *symtab)
 {
-  char *p1;
-  char *copy;
-  struct cleanup *outer;
-  VEC (symtab_p) *result;
-  
-  p1 = p;
-  while (p != *argptr && p[-1] == ' ')
-    --p;
-  if ((*p == '"') && is_quote_enclosed)
-    --p;
-  copy = xmalloc (p - *argptr + 1);
-  outer = make_cleanup (xfree, copy);
-  memcpy (copy, *argptr, p - *argptr);
-  /* It may have the ending quote right after the file name.  */
-  if ((is_quote_enclosed && copy[p - *argptr - 1] == '"')
-      || copy[p - *argptr - 1] == '\'')
-    copy[p - *argptr - 1] = 0;
-  else
-    copy[p - *argptr] = 0;
-
-  result = collect_symtabs_from_filename (copy);
+  void **slot;
 
-  if (VEC_empty (symtab_p, result))
+  slot = htab_find_slot (m_symtab_table, symtab, INSERT);
+  if (!*slot)
     {
-      if (!have_full_symbols () && !have_partial_symbols ())
-       throw_error (NOT_FOUND_ERROR,
-                    _("No symbol table is loaded.  "
-                      "Use the \"file\" command."));
-      throw_error (NOT_FOUND_ERROR, _("No source file named %s."), copy);
+      *slot = symtab;
+      m_symtabs.push_back (symtab);
     }
 
-  /* Discard the file name from the arg.  */
-  if (*p1 == '\0')
-    *argptr = p1;
-  else
-    *argptr = skip_spaces (p1 + 1);
-
-  discard_cleanups (outer);
-  *user_filename = copy;
-  return result;
+  return false;
 }
 
-/* A callback used by iterate_over_all_matching_symtabs that collects
-   symbols for find_function_symbols.  */
-
-static int
-collect_function_symbols (struct symbol *sym, void *arg)
-{
-  VEC (symbolp) **syms = arg;
-
-  if (SYMBOL_CLASS (sym) == LOC_BLOCK)
-    VEC_safe_push (symbolp, *syms, sym);
-
-  return 1;
-}
+} // namespace
 
-/* Look up a function symbol in *ARGPTR.  If found, advance *ARGPTR
-   and return the symbol.  If not found, return NULL.  */
+/* Given a file name, return a list of all matching symtabs.  If
+   SEARCH_PSPACE is not NULL, the search is restricted to just that
+   program space.  */
 
-static VEC (symbolp) *
-find_function_symbols (char **argptr, char *p, int is_quote_enclosed,
-                      char **user_function)
+static std::vector<symtab *>
+collect_symtabs_from_filename (const char *file,
+                              struct program_space *search_pspace)
 {
-  char *p1;
-  char *copy;
-  VEC (symbolp) *result = NULL;
+  symtab_collector collector;
 
-  p1 = p;
-  while (p != *argptr && p[-1] == ' ')
-    --p;
-  if ((*p == '"') && is_quote_enclosed)
-    --p;
-  copy = (char *) xmalloc (p - *argptr + 1);
-  *user_function = copy;
-  memcpy (copy, *argptr, p - *argptr);
-  /* It may have the ending quote right after the file name.  */
-  if ((is_quote_enclosed && copy[p - *argptr - 1] == '"')
-      || copy[p - *argptr - 1] == '\'')
-    copy[p - *argptr - 1] = 0;
-  else
-    copy[p - *argptr] = 0;
+  /* Find that file's data.  */
+  if (search_pspace == NULL)
+    {
+      struct program_space *pspace;
 
-  iterate_over_all_matching_symtabs (copy, VAR_DOMAIN,
-                                    collect_function_symbols, &result, NULL);
+      ALL_PSPACES (pspace)
+        {
+         if (pspace->executing_startup)
+           continue;
 
-  if (VEC_empty (symbolp, result))
-    VEC_free (symbolp, result);
+         set_current_program_space (pspace);
+         iterate_over_symtabs (file, collector);
+       }
+    }
   else
     {
-      /* Discard the file name from the arg.  */
-      *argptr = skip_spaces (p1 + 1);
+      set_current_program_space (search_pspace);
+      iterate_over_symtabs (file, collector);
     }
 
-  return result;
+  return collector.release_symtabs ();
 }
 
-\f
-
-/* A helper for decode_all_digits that handles the 'list_mode' case.  */
+/* Return all the symtabs associated to the FILENAME.  If SEARCH_PSPACE is
+   not NULL, the search is restricted to just that program space.  */
 
-static void
-decode_digits_list_mode (struct linespec_state *self,
-                        struct symtabs_and_lines *values,
-                        struct symtab_and_line val)
+static std::vector<symtab *>
+symtabs_from_filename (const char *filename,
+                      struct program_space *search_pspace)
 {
-  int ix;
-  struct symtab *elt;
+  std::vector<symtab *> result
+    = collect_symtabs_from_filename (filename, search_pspace);
 
-  gdb_assert (self->list_mode);
-
-  for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
+  if (result.empty ())
     {
-      /* The logic above should ensure this.  */
-      gdb_assert (elt != NULL);
-
-      set_current_program_space (SYMTAB_PSPACE (elt));
-
-      /* Simplistic search just for the list command.  */
-      val.symtab = find_line_symtab (elt, val.line, NULL, NULL);
-      if (val.symtab == NULL)
-       val.symtab = elt;
-      val.pspace = SYMTAB_PSPACE (elt);
-      val.pc = 0;
-      val.explicit_line = 1;
-
-      add_sal_to_sals (self, values, &val, NULL);
+      if (!have_full_symbols () && !have_partial_symbols ())
+       throw_error (NOT_FOUND_ERROR,
+                    _("No symbol table is loaded.  "
+                      "Use the \"file\" command."));
+      source_file_not_found_error (filename);
     }
-}
-
-/* A helper for decode_all_digits that iterates over the symtabs,
-   adding lines to the VEC.  */
-
-static void
-decode_digits_ordinary (struct linespec_state *self,
-                       int line,
-                       struct symtabs_and_lines *sals,
-                       struct linetable_entry **best_entry)
-{
-  int ix;
-  struct symtab *elt;
-
-  for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
-    {
-      int i;
-      VEC (CORE_ADDR) *pcs;
-      CORE_ADDR pc;
-
-      /* The logic above should ensure this.  */
-      gdb_assert (elt != NULL);
-
-      set_current_program_space (SYMTAB_PSPACE (elt));
-
-      pcs = find_pcs_for_symtab_line (elt, line, best_entry);
-      for (i = 0; VEC_iterate (CORE_ADDR, pcs, i, pc); ++i)
-       {
-         struct symtab_and_line sal;
-
-         init_sal (&sal);
-         sal.pspace = SYMTAB_PSPACE (elt);
-         sal.symtab = elt;
-         sal.line = line;
-         sal.pc = pc;
-         add_sal_to_sals_basic (sals, &sal);
-       }
 
-      VEC_free (CORE_ADDR, pcs);
-    }
+  return result;
 }
 
-/* This decodes a line where the argument is all digits (possibly
-   preceded by a sign).  Q should point to the end of those digits;
-   the other arguments are as usual.  */
+/* See symtab.h.  */
 
-static struct symtabs_and_lines
-decode_all_digits (struct linespec_state *self,
-                  char **argptr,
-                  char *q)
+void
+symbol_searcher::find_all_symbols (const std::string &name,
+                                  const struct language_defn *language,
+                                  enum search_domain search_domain,
+                                  std::vector<symtab *> *search_symtabs,
+                                  struct program_space *search_pspace)
 {
-  struct symtabs_and_lines values;
-  struct symtab_and_line val;
-  int use_default = 0;
-  char *saved_arg = *argptr;
-
-  enum sign
-    {
-      none, plus, minus
-    }
-  sign = none;
-
-  init_sal (&val);
-  values.sals = NULL;
-  values.nelts = 0;
-
-  /* This is where we need to make sure that we have good defaults.
-     We must guarantee that this section of code is never executed
-     when we are called with just a function name, since
-     set_default_source_symtab_and_line uses
-     select_source_symtab that calls us with such an argument.  */
-
-  if (VEC_length (symtab_p, self->file_symtabs) == 1
-      && VEC_index (symtab_p, self->file_symtabs, 0) == NULL)
-    {
-      set_current_program_space (self->program_space);
-
-      /* Make sure we have at least a default source file.  */
-      set_default_source_symtab_and_line ();
-      initialize_defaults (&self->default_symtab, &self->default_line);
-      VEC_pop (symtab_p, self->file_symtabs);
-      VEC_free (symtab_p, self->file_symtabs);
-      self->file_symtabs
-       = collect_symtabs_from_filename (self->default_symtab->filename);
-      use_default = 1;
-    }
-
-  if (**argptr == '+')
-    sign = plus, (*argptr)++;
-  else if (**argptr == '-')
-    sign = minus, (*argptr)++;
-  val.line = atoi (*argptr);
-  switch (sign)
-    {
-    case plus:
-      if (q == *argptr)
-       val.line = 5;
-      if (use_default)
-       val.line = self->default_line + val.line;
-      break;
-    case minus:
-      if (q == *argptr)
-       val.line = 15;
-      if (use_default)
-       val.line = self->default_line - val.line;
-      else
-       val.line = 1;
-      break;
-    case none:
-      break;           /* No need to adjust val.line.  */
-    }
-
-  *argptr = skip_spaces (q);
-
-  if (self->list_mode)
-    decode_digits_list_mode (self, &values, val);
-  else
-    {
-      struct linetable_entry *best_entry = NULL;
-      int *filter;
-      struct block **blocks;
-      struct cleanup *cleanup;
-      struct symtabs_and_lines intermediate_results;
-      int i, j;
-
-      intermediate_results.sals = NULL;
-      intermediate_results.nelts = 0;
-
-      decode_digits_ordinary (self, val.line, &intermediate_results,
-                             &best_entry);
-      if (intermediate_results.nelts == 0 && best_entry != NULL)
-       decode_digits_ordinary (self, best_entry->line, &intermediate_results,
-                               &best_entry);
-
-      cleanup = make_cleanup (xfree, intermediate_results.sals);
-
-      /* For optimized code, compiler can scatter one source line
-        accross disjoint ranges of PC values, even when no duplicate
-        functions or inline functions are involved.  For example,
-        'for (;;)' inside non-template non-inline non-ctor-or-dtor
-        function can result in two PC ranges.  In this case, we don't
-        want to set breakpoint on first PC of each range.  To filter
-        such cases, we use containing blocks -- for each PC found
-        above we see if there are other PCs that are in the same
-        block.  If yes, the other PCs are filtered out.  */
-
-      filter = xmalloc (intermediate_results.nelts * sizeof (int));
-      make_cleanup (xfree, filter);
-      blocks = xmalloc (intermediate_results.nelts * sizeof (struct block *));
-      make_cleanup (xfree, blocks);
-
-      for (i = 0; i < intermediate_results.nelts; ++i)
-       {
-         set_current_program_space (intermediate_results.sals[i].pspace);
-
-         filter[i] = 1;
-         blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc,
-                                        intermediate_results.sals[i].section);
-       }
-
-      for (i = 0; i < intermediate_results.nelts; ++i)
-       {
-         if (blocks[i] != NULL)
-           for (j = i + 1; j < intermediate_results.nelts; ++j)
-             {
-               if (blocks[j] == blocks[i])
-                 {
-                   filter[j] = 0;
-                   break;
-                 }
-             }
-       }
-
-      for (i = 0; i < intermediate_results.nelts; ++i)
-       if (filter[i])
-         {
-           struct symbol *sym = (blocks[i]
-                                 ? block_containing_function (blocks[i])
-                                 : NULL);
-
-           if (self->funfirstline)
-             skip_prologue_sal (&intermediate_results.sals[i]);
-           /* Make sure the line matches the request, not what was
-              found.  */
-           intermediate_results.sals[i].line = val.line;
-           add_sal_to_sals (self, &values, &intermediate_results.sals[i],
-                            sym ? SYMBOL_NATURAL_NAME (sym) : NULL);
-         }
-
-      do_cleanups (cleanup);
-    }
+  symbol_searcher_collect_info info;
+  struct linespec_state state;
 
-  if (values.nelts == 0)
-    {
-      if (self->user_filename)
-       throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
-                    val.line, self->user_filename);
-      else
-       throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."),
-                    val.line);
-    }
+  memset (&state, 0, sizeof (state));
+  state.language = language;
+  info.state = &state;
 
-  if (self->canonical)
+  info.result.symbols = &m_symbols;
+  info.result.minimal_symbols = &m_minimal_symbols;
+  std::vector<symtab *> all_symtabs;
+  if (search_symtabs == nullptr)
     {
-      char *copy = savestring (saved_arg, q - saved_arg);
-
-      self->canonical->pre_expanded = 1;
-      gdb_assert (self->user_filename || use_default);
-      self->canonical->addr_string
-       = xstrprintf ("%s:%s", (self->user_filename
-                               ? self->user_filename
-                               : self->default_symtab->filename),
-                     copy);
-      xfree (copy);
+      all_symtabs.push_back (nullptr);
+      search_symtabs = &all_symtabs;
     }
+  info.file_symtabs = search_symtabs;
 
-  return values;
+  add_matching_symbols_to_info (name.c_str (), symbol_name_match_type::WILD,
+                               search_domain, &info, search_pspace);
 }
 
-\f
-
-/* Decode a linespec starting with a dollar sign.  */
+/* Look up a function symbol named NAME in symtabs FILE_SYMTABS.  Matching
+   debug symbols are returned in SYMBOLS.  Matching minimal symbols are
+   returned in MINSYMS.  */
 
-static struct symtabs_and_lines
-decode_dollar (struct linespec_state *self, char *copy)
+static void
+find_function_symbols (struct linespec_state *state,
+                      std::vector<symtab *> *file_symtabs, const char *name,
+                      symbol_name_match_type name_match_type,
+                      std::vector<block_symbol> *symbols,
+                      std::vector<bound_minimal_symbol> *minsyms)
 {
-  LONGEST valx;
-  int index = 0;
-  struct symtabs_and_lines values;
-  struct symtab_and_line val;
-  char *p;
-  struct symbol *sym;
-  struct minimal_symbol *msymbol;
-  int ix;
-  struct symtab *elt;
-
-  p = (copy[1] == '$') ? copy + 2 : copy + 1;
-  while (*p >= '0' && *p <= '9')
-    p++;
-  if (!*p)             /* Reached end of token without hitting non-digit.  */
-    {
-      /* We have a value history reference.  */
-      struct value *val_history;
-
-      sscanf ((copy[1] == '$') ? copy + 2 : copy + 1, "%d", &index);
-      val_history = access_value_history ((copy[1] == '$') ? -index : index);
-      if (TYPE_CODE (value_type (val_history)) != TYPE_CODE_INT)
-       error (_("History values used in line "
-                "specs must have integer values."));
-      valx = value_as_long (val_history);
-    }
+  struct collect_info info;
+  std::vector<const char *> symbol_names;
+
+  info.state = state;
+  info.result.symbols = symbols;
+  info.result.minimal_symbols = minsyms;
+  info.file_symtabs = file_symtabs;
+
+  /* Try NAME as an Objective-C selector.  */
+  find_imps (name, &symbol_names);
+  if (!symbol_names.empty ())
+    add_all_symbol_names_from_pspace (&info, state->search_pspace,
+                                     symbol_names, FUNCTIONS_DOMAIN);
   else
-    {
-      /* Not all digits -- may be user variable/function or a
-        convenience variable.  */
+    add_matching_symbols_to_info (name, name_match_type, FUNCTIONS_DOMAIN,
+                                 &info, state->search_pspace);
+}
 
-      volatile struct gdb_exception exc;
+/* Find all symbols named NAME in FILE_SYMTABS, returning debug symbols
+   in SYMBOLS and minimal symbols in MINSYMS.  */
+
+static void
+find_linespec_symbols (struct linespec_state *state,
+                      std::vector<symtab *> *file_symtabs,
+                      const char *lookup_name,
+                      symbol_name_match_type name_match_type,
+                      std::vector <block_symbol> *symbols,
+                      std::vector<bound_minimal_symbol> *minsyms)
+{
+  std::string canon = cp_canonicalize_string_no_typedefs (lookup_name);
+  if (!canon.empty ())
+    lookup_name = canon.c_str ();
+
+  /* It's important to not call expand_symtabs_matching unnecessarily
+     as it can really slow things down (by unnecessarily expanding
+     potentially 1000s of symtabs, which when debugging some apps can
+     cost 100s of seconds).  Avoid this to some extent by *first* calling
+     find_function_symbols, and only if that doesn't find anything
+     *then* call find_method.  This handles two important cases:
+     1) break (anonymous namespace)::foo
+     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
+     the name into class and method names and searching the class and its
+     baseclasses.  */
+  if (symbols->empty () && minsyms->empty ())
+    {
+      std::string klass, method;
+      const char *last, *p, *scope_op;
 
-      /* Avoid "may be used uninitialized" warning.  */
-      values.sals = NULL;
-      values.nelts = 0;
+      /* See if we can find a scope operator and break this symbol
+        name into namespaces${SCOPE_OPERATOR}class_name and method_name.  */
+      scope_op = "::";
+      p = find_toplevel_string (lookup_name, scope_op);
 
-      TRY_CATCH (exc, RETURN_MASK_ERROR)
+      last = NULL;
+      while (p != NULL)
        {
-         values = decode_variable (self, copy);
+         last = p;
+         p = find_toplevel_string (p + strlen (scope_op), scope_op);
        }
 
-      if (exc.reason == 0)
-       return values;
+      /* If no scope operator was found, there is nothing more we can do;
+        we already attempted to lookup the entire name as a symbol
+        and failed.  */
+      if (last == NULL)
+       return;
 
-      if (exc.error != NOT_FOUND_ERROR)
-       throw_exception (exc);
+      /* LOOKUP_NAME points to the class name.
+        LAST points to the method name.  */
+      klass = std::string (lookup_name, last - lookup_name);
 
-      /* Not a user variable or function -- must be convenience variable.  */
-      if (!get_internalvar_integer (lookup_internalvar (copy + 1), &valx))
-       error (_("Convenience variables used in line "
-                "specs must have integer values."));
-    }
+      /* Skip past the scope operator.  */
+      last += strlen (scope_op);
+      method = last;
+
+      /* Find a list of classes named KLASS.  */
+      std::vector<block_symbol> classes
+       = lookup_prefix_sym (state, file_symtabs, klass.c_str ());
+      if (!classes.empty ())
+       {
+         /* Now locate a list of suitable methods named METHOD.  */
+         try
+           {
+             find_method (state, file_symtabs,
+                          klass.c_str (), method.c_str (),
+                          &classes, symbols, minsyms);
+           }
 
-  init_sal (&val);
+         /* If successful, we're done.  If NOT_FOUND_ERROR
+            was not thrown, rethrow the exception that we did get.  */
+         catch (const gdb_exception_error &except)
+           {
+             if (except.error != NOT_FOUND_ERROR)
+               throw;
+           }
+       }
+    }
+}
 
-  values.sals = NULL;
-  values.nelts = 0;
+/* 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.  */
 
-  for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
+static void
+find_label_symbols_in_block (const struct block *block,
+                            const char *name, struct symbol *fn_sym,
+                            bool completion_mode,
+                            std::vector<block_symbol> *result,
+                            std::vector<block_symbol> *label_funcs_ret)
+{
+  if (completion_mode)
     {
-      if (elt == NULL)
-       {
-         elt = self->default_symtab;
-         set_current_program_space (self->program_space);
-       }
-      else
-       set_current_program_space (SYMTAB_PSPACE (elt));
+      struct block_iterator iter;
+      struct symbol *sym;
+      size_t name_len = strlen (name);
 
-      /* Either history value or convenience value from above, in valx.  */
-      val.symtab = elt;
-      val.line = valx;
-      val.pc = 0;
-      val.pspace = elt ? SYMTAB_PSPACE (elt) : current_program_space;
+      int (*cmp) (const char *, const char *, size_t);
+      cmp = case_sensitivity == case_sensitive_on ? strncmp : strncasecmp;
 
-      add_sal_to_sals (self, &values, &val, NULL);
+      ALL_BLOCK_SYMBOLS (block, iter, sym)
+       {
+         if (symbol_matches_domain (sym->language (),
+                                    SYMBOL_DOMAIN (sym), LABEL_DOMAIN)
+             && cmp (sym->search_name (), name, name_len) == 0)
+           {
+             result->push_back ({sym, block});
+             label_funcs_ret->push_back ({fn_sym, block});
+           }
+       }
     }
-
-  if (self->canonical)
+  else
     {
-      self->canonical->pre_expanded = 1;
-      if (self->user_filename)
-       self->canonical->addr_string = xstrprintf ("%s:%s",
-                                                  self->user_filename, copy);
-      else
-       self->canonical->addr_string = xstrdup (copy);
-    }
+      struct block_symbol label_sym
+       = lookup_symbol (name, block, LABEL_DOMAIN, 0);
 
-  return values;
+      if (label_sym.symbol != NULL)
+       {
+         result->push_back (label_sym);
+         label_funcs_ret->push_back ({fn_sym, block});
+       }
+    }
 }
 
-\f
+/* Return all labels that match name NAME in FUNCTION_SYMBOLS or NULL
+   if no matches were found.
 
-/* A helper for decode_line_1 that tries to find a label.  The label
-   is searched for in the current block.
-   FUNCTION_SYMBOLS is a list of the enclosing functions; or NULL if none
-   specified.
-   COPY is the name of the label to find.
-   CANONICAL is the same as the "canonical" argument to decode_line_1.
-   RESULT is a pointer to a symtabs_and_lines structure which will be
-   filled in on success.
-   This function returns 1 if a label was found, 0 otherwise.  */
+   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 int
-decode_label (struct linespec_state *self,
-             VEC (symbolp) *function_symbols, char *copy,
-             struct symtabs_and_lines *result)
+
+static std::vector<block_symbol> *
+find_label_symbols (struct linespec_state *self,
+                   std::vector<block_symbol> *function_symbols,
+                   std::vector<block_symbol> *label_funcs_ret,
+                   const char *name,
+                   bool completion_mode)
 {
+  const struct block *block;
   struct symbol *fn_sym;
-  int ix;
+  std::vector<block_symbol> result;
 
   if (function_symbols == NULL)
     {
-      struct block *block;
-      struct symbol *sym;
-      struct symtab_and_line sal;
-      struct symtabs_and_lines values;
-
-      values.nelts = 0;
-      values.sals = NULL;
-
       set_current_program_space (self->program_space);
-      block = get_search_block (NULL);
+      block = get_current_search_block ();
 
       for (;
           block && !BLOCK_FUNCTION (block);
           block = BLOCK_SUPERBLOCK (block))
        ;
       if (!block)
-       return 0;
+       return NULL;
       fn_sym = BLOCK_FUNCTION (block);
 
-      sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0);
+      find_label_symbols_in_block (block, name, fn_sym, completion_mode,
+                                  &result, label_funcs_ret);
+    }
+  else
+    {
+      for (const auto &elt : *function_symbols)
+       {
+         fn_sym = elt.symbol;
+         set_current_program_space (SYMTAB_PSPACE (symbol_symtab (fn_sym)));
+         block = SYMBOL_BLOCK_VALUE (fn_sym);
 
-      if (sym == NULL)
-       return 0;
+         find_label_symbols_in_block (block, name, fn_sym, completion_mode,
+                                      &result, label_funcs_ret);
+       }
+    }
 
-      symbol_to_sal (&sal, self->funfirstline, sym);
-      add_sal_to_sals (self, &values, &sal,
-                      SYMBOL_NATURAL_NAME (fn_sym));
+  if (!result.empty ())
+    return new std::vector<block_symbol> (std::move (result));
+  return nullptr;
+}
 
-      if (self->canonical)
-       {
-         self->canonical->special_display = 1;
-         self->canonical->addr_string
-           = xstrprintf ("%s:%s", SYMBOL_NATURAL_NAME (fn_sym),
-                         copy);
-       }
+\f
 
-      *result = values;
+/* A helper for create_sals_line_offset that handles the 'list_mode' case.  */
 
-      return 1;
-    }
+static std::vector<symtab_and_line>
+decode_digits_list_mode (struct linespec_state *self,
+                        linespec_p ls,
+                        struct symtab_and_line val)
+{
+  gdb_assert (self->list_mode);
 
-  result->sals = NULL;
-  result->nelts = 0;
+  std::vector<symtab_and_line> values;
 
-  for (ix = 0; VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix)
+  for (const auto &elt : *ls->file_symtabs)
     {
-      struct block *block;
-      struct symbol *sym;
+      /* The logic above should ensure this.  */
+      gdb_assert (elt != NULL);
 
-      set_current_program_space (SYMTAB_PSPACE (SYMBOL_SYMTAB (fn_sym)));
-      block = SYMBOL_BLOCK_VALUE (fn_sym);
-      sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0);
+      set_current_program_space (SYMTAB_PSPACE (elt));
 
-      if (sym != NULL)
-       {
-         struct symtab_and_line sal;
-         char *symname;
+      /* Simplistic search just for the list command.  */
+      val.symtab = find_line_symtab (elt, val.line, NULL, NULL);
+      if (val.symtab == NULL)
+       val.symtab = elt;
+      val.pspace = SYMTAB_PSPACE (elt);
+      val.pc = 0;
+      val.explicit_line = true;
 
-         symbol_to_sal (&sal, self->funfirstline, sym);
-         symname = xstrprintf ("%s:%s",
-                               SYMBOL_NATURAL_NAME (fn_sym),
-                               SYMBOL_NATURAL_NAME (sym));
-         add_sal_to_sals (self, result, &sal, symname);
-         xfree (symname);
-       }
+      add_sal_to_sals (self, &values, &val, NULL, 0);
     }
 
-  if (self->canonical && result->nelts > 0)
+  return values;
+}
+
+/* A helper for create_sals_line_offset that iterates over the symtabs
+   associated with LS and returns a vector of corresponding symtab_and_line
+   structures.  */
+
+static std::vector<symtab_and_line>
+decode_digits_ordinary (struct linespec_state *self,
+                       linespec_p ls,
+                       int line,
+                       struct linetable_entry **best_entry)
+{
+  std::vector<symtab_and_line> sals;
+  for (const auto &elt : *ls->file_symtabs)
     {
-      self->canonical->pre_expanded = 1;
-      self->canonical->special_display = 1;
+      std::vector<CORE_ADDR> pcs;
+
+      /* The logic above should ensure this.  */
+      gdb_assert (elt != NULL);
+
+      set_current_program_space (SYMTAB_PSPACE (elt));
 
-      gdb_assert (self->user_function);
-      self->canonical->addr_string
-       = xstrprintf ("%s:%s", self->user_function, copy);
+      pcs = find_pcs_for_symtab_line (elt, line, best_entry);
+      for (CORE_ADDR pc : pcs)
+       {
+         symtab_and_line sal;
+         sal.pspace = SYMTAB_PSPACE (elt);
+         sal.symtab = elt;
+         sal.line = line;
+         sal.explicit_line = true;
+         sal.pc = pc;
+         sals.push_back (std::move (sal));
+       }
     }
 
-  return result->nelts > 0;
+  return sals;
 }
 
-/* A callback used to possibly add a symbol to the results.  */
+\f
 
-static int
-collect_symbols (struct symbol *sym, void *data)
+/* Return the line offset represented by VARIABLE.  */
+
+static struct line_offset
+linespec_parse_variable (struct linespec_state *self, const char *variable)
 {
-  struct collect_info *info = data;
-  struct symtab_and_line sal;
+  int index = 0;
+  const char *p;
+  struct line_offset offset = {0, LINE_OFFSET_NONE};
 
-  if (symbol_to_sal (&sal, info->state->funfirstline, sym)
-      && maybe_add_address (info->state->addr_set,
-                           SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)),
-                           sal.pc))
-    add_sal_to_sals (info->state, &info->result, &sal,
-                    SYMBOL_NATURAL_NAME (sym));
+  p = (variable[1] == '$') ? variable + 2 : variable + 1;
+  if (*p == '$')
+    ++p;
+  while (*p >= '0' && *p <= '9')
+    ++p;
+  if (!*p)             /* Reached end of token without hitting non-digit.  */
+    {
+      /* We have a value history reference.  */
+      struct value *val_history;
 
-  return 1;
+      sscanf ((variable[1] == '$') ? variable + 2 : variable + 1, "%d", &index);
+      val_history
+       = access_value_history ((variable[1] == '$') ? -index : index);
+      if (TYPE_CODE (value_type (val_history)) != TYPE_CODE_INT)
+       error (_("History values used in line "
+                "specs must have integer values."));
+      offset.offset = value_as_long (val_history);
+    }
+  else
+    {
+      /* Not all digits -- may be user variable/function or a
+        convenience variable.  */
+      LONGEST valx;
+      struct internalvar *ivar;
+
+      /* Try it as a convenience variable.  If it is not a convenience
+        variable, return and allow normal symbol lookup to occur.  */
+      ivar = lookup_only_internalvar (variable + 1);
+      if (ivar == NULL)
+       /* No internal variable with that name.  Mark the offset
+          as unknown to allow the name to be looked up as a symbol.  */
+       offset.sign = LINE_OFFSET_UNKNOWN;
+      else
+       {
+         /* We found a valid variable name.  If it is not an integer,
+            throw an error.  */
+         if (!get_internalvar_integer (ivar, &valx))
+           error (_("Convenience variables used in line "
+                    "specs must have integer values."));
+         else
+           offset.offset = valx;
+       }
+    }
+
+  return offset;
 }
+\f
 
-/* We've found a minimal symbol MSYMBOL to associate with our
-   linespec; add it to the result symtabs_and_lines.  */
+/* We've found a minimal symbol MSYMBOL in OBJFILE to associate with our
+   linespec; return the SAL in RESULT.  This function should return SALs
+   matching those from find_function_start_sal, otherwise false
+   multiple-locations breakpoints could be placed.  */
 
 static void
 minsym_found (struct linespec_state *self, struct objfile *objfile,
              struct minimal_symbol *msymbol,
-             struct symtabs_and_lines *result)
+             std::vector<symtab_and_line> *result)
 {
-  struct gdbarch *gdbarch = get_objfile_arch (objfile);
-  CORE_ADDR pc;
-  struct symtab_and_line sal;
-
-  sal = find_pc_sect_line (SYMBOL_VALUE_ADDRESS (msymbol),
-                          (struct obj_section *) 0, 0);
-  sal.section = SYMBOL_OBJ_SECTION (msymbol);
-
-  /* The minimal symbol might point to a function descriptor;
-     resolve it to the actual code address instead.  */
-  pc = gdbarch_convert_from_func_ptr_addr (gdbarch, sal.pc, &current_target);
-  if (pc != sal.pc)
-    sal = find_pc_sect_line (pc, NULL, 0);
-
-  if (self->funfirstline)
-    skip_prologue_sal (&sal);
-
-  if (maybe_add_address (self->addr_set, objfile->pspace, sal.pc))
-    add_sal_to_sals (self, result, &sal, SYMBOL_NATURAL_NAME (msymbol));
-}
-
-/* A helper struct which just holds a minimal symbol and the object
-   file from which it came.  */
+  bool want_start_sal;
 
-typedef struct minsym_and_objfile
-{
-  struct minimal_symbol *minsym;
-  struct objfile *objfile;
-} minsym_and_objfile_d;
+  CORE_ADDR func_addr;
+  bool is_function = msymbol_is_function (objfile, msymbol, &func_addr);
 
-DEF_VEC_O (minsym_and_objfile_d);
+  if (is_function)
+    {
+      const char *msym_name = msymbol->linkage_name ();
 
-/* A helper struct to pass some data through
-   iterate_over_minimal_symbols.  */
+      if (MSYMBOL_TYPE (msymbol) == mst_text_gnu_ifunc
+         || MSYMBOL_TYPE (msymbol) == mst_data_gnu_ifunc)
+       want_start_sal = gnu_ifunc_resolve_name (msym_name, &func_addr);
+      else
+       want_start_sal = true;
+    }
 
-struct collect_minsyms
-{
-  /* The objfile we're examining.  */
-  struct objfile *objfile;
+  symtab_and_line sal;
 
-  /* The funfirstline setting from the initial call.  */
-  int funfirstline;
+  if (is_function && want_start_sal)
+    sal = find_function_start_sal (func_addr, NULL, self->funfirstline);
+  else
+    {
+      sal.objfile = objfile;
+      sal.msymbol = msymbol;
+      /* Store func_addr, not the minsym's address in case this was an
+        ifunc that hasn't been resolved yet.  */
+      if (is_function)
+       sal.pc = func_addr;
+      else
+       sal.pc = MSYMBOL_VALUE_ADDRESS (objfile, msymbol);
+      sal.pspace = current_program_space;
+    }
 
-  /* The list_mode setting from the initial call.  */
-  int list_mode;
+  sal.section = MSYMBOL_OBJ_SECTION (objfile, msymbol);
 
-  /* The resulting symbols.  */
-  VEC (minsym_and_objfile_d) *msyms;
-};
+  if (maybe_add_address (self->addr_set, objfile->pspace, sal.pc))
+    add_sal_to_sals (self, result, &sal, msymbol->natural_name (), 0);
+}
 
 /* A helper function to classify a minimal_symbol_type according to
    priority.  */
@@ -2790,123 +4276,124 @@ classify_mtype (enum minimal_symbol_type t)
     }
 }
 
-/* Callback for qsort that sorts symbols by priority.  */
+/* Callback for std::sort that sorts symbols by priority.  */
 
-static int
-compare_msyms (const void *a, const void *b)
+static bool
+compare_msyms (const bound_minimal_symbol &a, const bound_minimal_symbol &b)
 {
-  const minsym_and_objfile_d *moa = a;
-  const minsym_and_objfile_d *mob = b;
-  enum minimal_symbol_type ta = MSYMBOL_TYPE (moa->minsym);
-  enum minimal_symbol_type tb = MSYMBOL_TYPE (mob->minsym);
+  enum minimal_symbol_type ta = MSYMBOL_TYPE (a.minsym);
+  enum minimal_symbol_type tb = MSYMBOL_TYPE (b.minsym);
 
-  return classify_mtype (ta) - classify_mtype (tb);
+  return classify_mtype (ta) < classify_mtype (tb);
 }
 
-/* Callback for iterate_over_minimal_symbols that adds the symbol to
-   the result.  */
+/* Helper for search_minsyms_for_name that adds the symbol to the
+   result.  */
 
 static void
-add_minsym (struct minimal_symbol *minsym, void *d)
+add_minsym (struct minimal_symbol *minsym, struct objfile *objfile,
+           struct symtab *symtab, int list_mode,
+           std::vector<struct bound_minimal_symbol> *msyms)
 {
-  struct collect_minsyms *info = d;
-  minsym_and_objfile_d mo;
+  if (symtab != NULL)
+    {
+      /* We're looking for a label for which we don't have debug
+        info.  */
+      CORE_ADDR func_addr;
+      if (msymbol_is_function (objfile, minsym, &func_addr))
+       {
+         symtab_and_line sal = find_pc_sect_line (func_addr, NULL, 0);
 
-  /* Exclude data symbols when looking for breakpoint locations.   */
-  if (!info->list_mode)
-    switch (minsym->type)
-      {
-       case mst_slot_got_plt:
-       case mst_data:
-       case mst_bss:
-       case mst_abs:
-       case mst_file_data:
-       case mst_file_bss:
-         {
-           /* Make sure this minsym is not a function descriptor
-              before we decide to discard it.  */
-           struct gdbarch *gdbarch = info->objfile->gdbarch;
-           CORE_ADDR addr = gdbarch_convert_from_func_ptr_addr
-                              (gdbarch, SYMBOL_VALUE_ADDRESS (minsym),
-                               &current_target);
-
-           if (addr == SYMBOL_VALUE_ADDRESS (minsym))
-             return;
-         }
-      }
+         if (symtab != sal.symtab)
+           return;
+       }
+    }
 
-  mo.minsym = minsym;
-  mo.objfile = info->objfile;
-  VEC_safe_push (minsym_and_objfile_d, info->msyms, &mo);
+  /* Exclude data symbols when looking for breakpoint locations.  */
+  if (!list_mode && !msymbol_is_function (objfile, minsym))
+    return;
+
+  struct bound_minimal_symbol mo = {minsym, objfile};
+  msyms->push_back (mo);
+  return;
 }
 
-/* Search minimal symbols in all objfiles for NAME.  If SEARCH_PSPACE
+/* Search for minimal symbols called NAME.  If SEARCH_PSPACE
    is not NULL, the search is restricted to just that program
-   space.  */
+   space.
+
+   If SYMTAB is NULL, search all objfiles, otherwise
+   restrict results to the given SYMTAB.  */
 
 static void
-search_minsyms_for_name (struct collect_info *info, const char *name,
-                        struct program_space *search_pspace)
+search_minsyms_for_name (struct collect_info *info,
+                        const lookup_name_info &name,
+                        struct program_space *search_pspace,
+                        struct symtab *symtab)
 {
-  struct objfile *objfile;
-  struct program_space *pspace;
-
-  ALL_PSPACES (pspace)
-  {
-    struct collect_minsyms local;
-    struct cleanup *cleanup;
-
-    if (search_pspace != NULL && search_pspace != pspace)
-      continue;
-    if (pspace->executing_startup)
-      continue;
+  std::vector<struct bound_minimal_symbol> minsyms;
 
-    set_current_program_space (pspace);
+  if (symtab == NULL)
+    {
+      struct program_space *pspace;
 
-    memset (&local, 0, sizeof (local));
-    local.funfirstline = info->state->funfirstline;
-    local.list_mode = info->state->list_mode;
+      ALL_PSPACES (pspace)
+      {
+       if (search_pspace != NULL && search_pspace != pspace)
+         continue;
+       if (pspace->executing_startup)
+         continue;
 
-    cleanup = make_cleanup (VEC_cleanup (minsym_and_objfile_d),
-                           &local.msyms);
+       set_current_program_space (pspace);
 
-    ALL_OBJFILES (objfile)
+       for (objfile *objfile : current_program_space->objfiles ())
+         {
+           iterate_over_minimal_symbols (objfile, name,
+                                         [&] (struct minimal_symbol *msym)
+                                         {
+                                           add_minsym (msym, objfile, nullptr,
+                                                       info->state->list_mode,
+                                                       &minsyms);
+                                           return false;
+                                         });
+         }
+      }
+    }
+  else
     {
-      local.objfile = objfile;
-      iterate_over_minimal_symbols (objfile, name, add_minsym, &local);
+      if (search_pspace == NULL || SYMTAB_PSPACE (symtab) == search_pspace)
+       {
+         set_current_program_space (SYMTAB_PSPACE (symtab));
+         iterate_over_minimal_symbols
+           (SYMTAB_OBJFILE (symtab), name,
+            [&] (struct minimal_symbol *msym)
+              {
+                add_minsym (msym, SYMTAB_OBJFILE (symtab), symtab,
+                            info->state->list_mode, &minsyms);
+                return false;
+              });
+       }
     }
 
-    if (!VEC_empty (minsym_and_objfile_d, local.msyms))
-      {
-       int classification;
-       int ix;
-       minsym_and_objfile_d *item;
-
-       qsort (VEC_address (minsym_and_objfile_d, local.msyms),
-              VEC_length (minsym_and_objfile_d, local.msyms),
-              sizeof (minsym_and_objfile_d),
-              compare_msyms);
-
-       /* Now the minsyms are in classification order.  So, we walk
-          over them and process just the minsyms with the same
-          classification as the very first minsym in the list.  */
-       item = VEC_index (minsym_and_objfile_d, local.msyms, 0);
-       classification = classify_mtype (MSYMBOL_TYPE (item->minsym));
-
-       for (ix = 0;
-            VEC_iterate (minsym_and_objfile_d, local.msyms, ix, item);
-            ++ix)
-         {
-           if (classify_mtype (MSYMBOL_TYPE (item->minsym)) != classification)
-             break;
+  if (!minsyms.empty ())
+    {
+      int classification;
 
-           minsym_found (info->state, item->objfile, item->minsym,
-                         &info->result);
-         }
-      }
+      std::sort (minsyms.begin (), minsyms.end (), compare_msyms);
 
-    do_cleanups (cleanup);
-  }
+      /* Now the minsyms are in classification order.  So, we walk
+        over them and process just the minsyms with the same
+        classification as the very first minsym in the list.  */
+      classification = classify_mtype (MSYMBOL_TYPE (minsyms[0].minsym));
+
+      for (const bound_minimal_symbol &item : minsyms)
+       {
+         if (classify_mtype (MSYMBOL_TYPE (item.minsym)) != classification)
+           break;
+
+         info->result.minimal_symbols->push_back (item);
+       }
+    }
 }
 
 /* A helper function to add all symbols matching NAME to INFO.  If
@@ -2915,97 +4402,47 @@ 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,
+                             enum search_domain search_domain,
                              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_p, info->state->file_symtabs, ix, elt); ++ix)
+  for (const auto &elt : *info->file_symtabs)
     {
-      struct symbol *sym;
-
-      if (elt == NULL)
+      if (elt == nullptr)
        {
-         iterate_over_all_matching_symtabs (name, VAR_DOMAIN,
-                                            collect_symbols, info,
-                                            pspace);
-         search_minsyms_for_name (info, name, pspace);
+         iterate_over_all_matching_symtabs (info->state, lookup_name,
+                                            VAR_DOMAIN, search_domain,
+                                            pspace, true,
+                                            [&] (block_symbol *bsym)
+           { return info->add_symbol (bsym); });
+         search_minsyms_for_name (info, lookup_name, pspace, NULL);
        }
       else if (pspace == NULL || pspace == SYMTAB_PSPACE (elt))
        {
+         int prev_len = info->result.symbols->size ();
+
          /* Program spaces that are executing startup should have
             been filtered out earlier.  */
          gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
          set_current_program_space (SYMTAB_PSPACE (elt));
-         LA_ITERATE_OVER_SYMBOLS (get_search_block (elt), name,
-                                  VAR_DOMAIN, collect_symbols,
-                                  info);
-       }
-    }
-}
-
-/* Decode a linespec that's a variable.  If FILE_SYMTAB is non-NULL,
-   look in that symtab's static variables first.  */ 
-
-static struct symtabs_and_lines
-decode_variable (struct linespec_state *self, char *copy)
-{
-  struct collect_info info;
-  const char *lookup_name;
-  char *canon;
-  struct cleanup *cleanup;
-
-  info.state = self;
-  info.result.sals = NULL;
-  info.result.nelts = 0;
-
-  cleanup = demangle_for_lookup (copy, current_language->la_language,
-                                &lookup_name);
-  if (current_language->la_language == language_ada)
-    {
-      /* In Ada, the symbol lookups are performed using the encoded
-         name rather than the demangled name.  */
-      lookup_name = ada_name_for_lookup (copy);
-      make_cleanup (xfree, (void *) lookup_name);
-    }
-
-  canon = cp_canonicalize_string_no_typedefs (lookup_name);
-  if (canon != NULL)
-    {
-      make_cleanup (xfree, canon);
-      lookup_name = canon;
-    }
-
-  add_matching_symbols_to_info (lookup_name, &info, NULL);
-
-  if (info.result.nelts > 0)
-    {
-      if (self->canonical)
-       {
-         self->canonical->pre_expanded = 1;
-         if (self->user_filename)
-           self->canonical->addr_string
-             = xstrprintf ("%s:%s", self->user_filename, copy);
-         else
-           self->canonical->addr_string = xstrdup (copy);
+         iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN,
+                                   [&] (block_symbol *bsym)
+           { return info->add_symbol (bsym); });
+
+         /* If no new symbols were found in this iteration and this symtab
+            is in assembler, we might actually be looking for a label for
+            which we don't have debug info.  Check for a minimal symbol in
+            this case.  */
+         if (prev_len == info->result.symbols->size ()
+             && elt->language == language_asm)
+           search_minsyms_for_name (info, lookup_name, pspace, elt);
        }
-      return info.result;
     }
-
-  if (!have_full_symbols ()
-      && !have_partial_symbols ()
-      && !have_minimal_symbols ())
-    throw_error (NOT_FOUND_ERROR,
-                _("No symbol table is loaded.  Use the \"file\" command."));
-  if (self->user_filename)
-    throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined in \"%s\"."),
-                copy, self->user_filename);
-  else
-    throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined."), copy);
 }
 
-
 \f
 
 /* Now come some functions that are called from multiple places within
@@ -3024,11 +4461,12 @@ symbol_to_sal (struct symtab_and_line *result,
     {
       if (SYMBOL_CLASS (sym) == LOC_LABEL && SYMBOL_VALUE_ADDRESS (sym) != 0)
        {
-         init_sal (result);
-         result->symtab = SYMBOL_SYMTAB (sym);
+         *result = {};
+         result->symtab = symbol_symtab (sym);
+         result->symbol = sym;
          result->line = SYMBOL_LINE (sym);
          result->pc = SYMBOL_VALUE_ADDRESS (sym);
-         result->pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
+         result->pspace = SYMTAB_PSPACE (result->symtab);
          result->explicit_pc = 1;
          return 1;
        }
@@ -3039,10 +4477,12 @@ symbol_to_sal (struct symtab_and_line *result,
       else if (SYMBOL_LINE (sym) != 0)
        {
          /* We know its line number.  */
-         init_sal (result);
-         result->symtab = SYMBOL_SYMTAB (sym);
+         *result = {};
+         result->symtab = symbol_symtab (sym);
+         result->symbol = sym;
          result->line = SYMBOL_LINE (sym);
-         result->pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
+         result->pc = SYMBOL_VALUE_ADDRESS (sym);
+         result->pspace = SYMTAB_PSPACE (result->symtab);
          return 1;
        }
     }
@@ -3050,43 +4490,16 @@ symbol_to_sal (struct symtab_and_line *result,
   return 0;
 }
 
-/* See the comment in linespec.h.  */
-
-void
-init_linespec_result (struct linespec_result *lr)
-{
-  memset (lr, 0, sizeof (*lr));
-}
-
-/* See the comment in linespec.h.  */
-
-void
-destroy_linespec_result (struct linespec_result *ls)
-{
-  int i;
-  struct linespec_sals *lsal;
-
-  xfree (ls->addr_string);
-  for (i = 0; VEC_iterate (linespec_sals, ls->sals, i, lsal); ++i)
-    {
-      xfree (lsal->canonical);
-      xfree (lsal->sals.sals);
-    }
-  VEC_free (linespec_sals, ls->sals);
-}
-
-/* Cleanup function for a linespec_result.  */
-
-static void
-cleanup_linespec_result (void *a)
+linespec_result::~linespec_result ()
 {
-  destroy_linespec_result (a);
+  for (linespec_sals &lsal : lsals)
+    xfree (lsal.canonical);
 }
 
-/* See the comment in linespec.h.  */
+/* Return the quote characters permitted by the linespec parser.  */
 
-struct cleanup *
-make_cleanup_destroy_linespec_result (struct linespec_result *ls)
+const char *
+get_gdb_linespec_parser_quote_characters (void)
 {
-  return make_cleanup (cleanup_linespec_result, ls);
+  return linespec_quote_characters;
 }
This page took 0.196806 seconds and 4 git commands to generate.