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