[PATCH 32/40] Make "break foo" find "A::foo", A::B::foo", etc. [C++ and wild matching]

Pedro Alves
2017-06-02T12:30:52Z

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

Here's a contrived example:

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

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

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

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

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

vs:

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

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

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

Implementation-wise, what the patch does is:

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

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

- add unit tests.

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

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

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

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

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

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 70c0e02..328b6db 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -15694,7 +15694,10 @@ Explicit locations are similar to linespecs but use an option/argument\n\
syntax to specify location parameters.\n\
Example: To specify the start of the label named \"the_top\" in the\n\
function \"fact\" in the file \"factorial.c\", use \"-source factorial.c\n\
--function fact -label the_top\".\n"
+-function fact -label the_top\".\n\
+For C++, \"-function\" matches all functions with the given name ignoring\n\
+missing leading specifiers (namespaces and classes). You can override\n\
+that by instead specifying a fully qualified name using \"-qualified\".\n"

/* This help string is used for the break, hbreak, tbreak and thbreak
commands. It is defined as a macro to prevent duplication.
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 49077c7..8d96f94 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -1016,7 +1016,7 @@ extern const struct language_defn cplus_language_defn =
c_watch_location_expression,
cp_get_symbol_name_matcher,
iterate_over_symbols,
- default_search_name_hash,
+ cp_search_name_hash,
&cplus_varobj_ops,
NULL,
NULL,
diff --git a/gdb/completer.c b/gdb/completer.c
index eabbce7..99e40a3 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -76,6 +76,9 @@ enum explicit_location_match_type
/* The name of a function or method. */
MATCH_FUNCTION,

+ /* The fully-qualified name of a function or method. */
+ MATCH_QUALIFIED,
+
/* A line number. */
MATCH_LINE,

@@ -609,6 +612,7 @@ static const char *const explicit_options[] =
{
"-source",
"-function",
+ "-qualified",
"-line",
"-label",
NULL
@@ -660,9 +664,11 @@ collect_explicit_location_matches (completion_tracker &tracker,
break;

case MATCH_FUNCTION:
+ case MATCH_QUALIFIED:
{
const char *function = string_or_empty (explicit_loc->function_name);
linespec_complete_function (tracker, function,
+ explicit_loc->func_name_match_type,
explicit_loc->source_filename);
}
break;
@@ -677,6 +683,7 @@ collect_explicit_location_matches (completion_tracker &tracker,
linespec_complete_label (tracker, language,
explicit_loc->source_filename,
explicit_loc->function_name,
+ explicit_loc->func_name_match_type,
label);
}
break;
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 397c738..84d8a6b 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -1642,7 +1642,96 @@ gdb_sniff_from_mangled_name (const char *mangled, char **demangled)
return *demangled != NULL;
}

-/* C++ symbol_name_matcher_ftype implementation. */
+/* Produce an unsigned hash value from SEARCH_NAME that is compatible
+ with cp_symbol_name_matches. Only the last component in
+ "foo::bar::function()" is considered for hashing purposes (i.e.,
+ the entire prefix is skipped), so that later on looking up for
+ "function" or "bar::function" in all namespaces is possible. */
+
+unsigned int
+cp_search_name_hash (const char *search_name)
+{
+ /* cp_entire_prefix_len assumes a fully-qualified name with no
+ leading "::". */
+ if (startswith (search_name, "::"))
+ search_name += 2;
+
+ unsigned int prefix_len = cp_entire_prefix_len (search_name);
+ if (prefix_len != 0)
+ search_name += prefix_len + 2;
+
+ return default_search_name_hash (search_name);
+}
+
+/* Helper for cp_symbol_name_matches (i.e., symbol_name_matcher_ftype
+ implementation for symbol_name_match_type::WILD matching). Split
+ to a separate function for unit-testing convenience.
+
+ If SYMBOL_SEARCH_NAME has more scopes than LOOKUP_NAME, we try to
+ match ignoring the extra leading scopes of SYMBOL_SEARCH_NAME.
+ This allows conveniently setting breakpoints on functions/methods
+ inside any namespace/class without specifying the fully-qualified
+ name.
+
+ E.g., these match:
+
+ [symbol search name] [lookup name]
+ foo::bar::func foo::bar::func
+ foo::bar::func bar::func
+ foo::bar::func func
+
+ While these don't:
+
+ [symbol search name] [lookup name]
+ foo::zbar::func bar::func
+ foo::bar::func foo::func
+
+ See more examples in the test_cp_symbol_name_matches selftest
+ function below.
+
+ See symbol_name_matcher_ftype for description of SYMBOL_SEARCH_NAME
+ and COMP_MATCH_RES. .
+
+ LOOKUP_NAME/LOOKUP_NAME_LEN is the name we're looking up.
+
+ See strncmp_iw_with_mode for description of MODE.
+*/
+
+static bool
+cp_symbol_name_matches_1 (const char *symbol_search_name,
+ const char *lookup_name,
+ size_t lookup_name_len,
+ strncmp_iw_mode mode,
+ completion_match_result *comp_match_res)
+{
+ const char *sname = symbol_search_name;
+
+ while (true)
+ {
+ if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len,
+ mode) == 0)
+ {
+ if (comp_match_res != NULL)
+ {
+ comp_match_res->match.set_match (symbol_search_name);
+ comp_match_res->match_for_lcd.set_match (sname);
+ }
+ return true;
+ }
+
+ unsigned int len = cp_find_first_component (sname);
+
+ if (sname[len] == '\0')
+ return false;
+
+ gdb_assert (sname[len] == ':');
+ /* Skip the '::'. */
+ sname += len + 2;
+ }
+}
+
+/* C++ symbol_name_matcher_ftype implementation for fully qualified
+ matches. */

static bool
cp_fq_symbol_name_matches (const char *symbol_search_name,
@@ -1671,19 +1760,151 @@ cp_fq_symbol_name_matches (const char *symbol_search_name,
return false;
}

+/* C++ symbol_name_matcher_ftype implementation for wild matches.
+ Defers work to cp_symbol_name_ncmp. */
+
+static bool
+cp_symbol_name_matches (const char *symbol_search_name,
+ const lookup_name_info &lookup_name,
+ completion_match_result *comp_match_res)
+{
+ const std::string &name = lookup_name.cplus ().lookup_name ();
+
+ strncmp_iw_mode mode = (lookup_name.completion_mode ()
+ ? strncmp_iw_mode::NORMAL
+ : strncmp_iw_mode::MATCH_PARAMS);
+
+ return cp_symbol_name_matches_1 (symbol_search_name,
+ name.c_str (), name.size (),
+ mode, comp_match_res);
+}
+
/* Implement the "la_get_symbol_name_matcher" language_defn method for
C++. */

symbol_name_matcher_ftype *
cp_get_symbol_name_matcher (const lookup_name_info &lookup_name)
{
- return cp_fq_symbol_name_matches;
+ switch (lookup_name.match_type ())
+ {
+ case symbol_name_match_type::FULL:
+ case symbol_name_match_type::EXPRESSION:
+ return cp_fq_symbol_name_matches;
+ case symbol_name_match_type::WILD:
+ return cp_symbol_name_matches;
+ }
+
+ gdb_assert_not_reached ("");
}

#if GDB_SELF_TEST

namespace selftests {

+void
+test_cp_symbol_name_cmp ()
+{
+#define CHECK_MATCH(SYMBOL, INPUT) \
+ SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL, \
+ INPUT, sizeof (INPUT) - 1, \
+ strncmp_iw_mode::MATCH_PARAMS, \
+ NULL))
+
+#define CHECK_NOT_MATCH(SYMBOL, INPUT) \
+ SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL, \
+ INPUT, sizeof (INPUT) - 1, \
+ strncmp_iw_mode::MATCH_PARAMS, \
+ NULL))
+
+ /* Like CHECK_MATCH, and also check that INPUT (and all substrings
+ that start at index 0) completes to SYMBOL. */
+#define CHECK_MATCH_C(SYMBOL, INPUT) \
+ CHECK_MATCH (SYMBOL, INPUT); \
+ for (size_t i = 0; i < sizeof (INPUT) - 1; i++) \
+ SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL, INPUT, i, \
+ strncmp_iw_mode::NORMAL, \
+ NULL))
+
+ /* Like CHECK_NOT_MATCH, and also check that INPUT does NOT complete
+ to SYMBOL. */
+#define CHECK_NOT_MATCH_C(SYMBOL, INPUT) \
+ CHECK_NOT_MATCH (SYMBOL, INPUT); \
+ SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL, INPUT, \
+ sizeof (INPUT) - 1, \
+ strncmp_iw_mode::NORMAL, \
+ NULL))
+
+ /* Lookup name without parens matches all overloads. */
+ CHECK_MATCH_C ("function()", "function");
+ CHECK_MATCH_C ("function(int)", "function");
+
+ /* Check whitespace around parameters is ignored. */
+ CHECK_MATCH_C ("function()", "function ()");
+ CHECK_MATCH_C ("function ( )", "function()");
+ CHECK_MATCH_C ("function ()", "function( )");
+ CHECK_MATCH_C ("func(int)", "func( int )");
+ CHECK_MATCH_C ("func(int)", "func ( int ) ");
+ CHECK_MATCH_C ("func ( int )", "func( int )");
+ CHECK_MATCH_C ("func ( int )", "func ( int ) ");
+
+ /* Check symbol name prefixes aren't incorrectly matched. */
+ CHECK_NOT_MATCH ("func", "function");
+ CHECK_NOT_MATCH ("function", "func");
+ CHECK_NOT_MATCH ("function()", "func");
+
+ /* Check that if the lookup name includes parameters, only the right
+ overload matches. */
+ CHECK_MATCH_C ("function(int)", "function(int)");
+ CHECK_NOT_MATCH_C ("function(int)", "function()");
+
+ /* Tests matching symbols in some scope. */
+ CHECK_MATCH_C ("foo::function()", "function");
+ CHECK_MATCH_C ("foo::function(int)", "function");
+ CHECK_MATCH_C ("foo::bar::function()", "function");
+ CHECK_MATCH_C ("bar::function()", "bar::function");
+ CHECK_MATCH_C ("foo::bar::function()", "bar::function");
+ CHECK_MATCH_C ("foo::bar::function(int)", "bar::function");
+
+ /* Same, with parameters in the lookup name. */
+ CHECK_MATCH_C ("foo::function()", "function()");
+ CHECK_MATCH_C ("foo::bar::function()", "function()");
+ CHECK_MATCH_C ("foo::function(int)", "function(int)");
+ CHECK_MATCH_C ("foo::function()", "foo::function()");
+ CHECK_MATCH_C ("foo::bar::function()", "bar::function()");
+ CHECK_MATCH_C ("foo::bar::function(int)", "bar::function(int)");
+ CHECK_NOT_MATCH_C ("foo::bar::function(int)", "bar::function()");
+
+ CHECK_MATCH_C ("(anonymous namespace)::bar::function(int)",
+ "bar::function(int)");
+ CHECK_MATCH_C ("foo::(anonymous namespace)::bar::function(int)",
+ "function(int)");
+
+ /* Lookup scope wider than symbol scope, should not match. */
+ CHECK_NOT_MATCH_C ("function()", "bar::function");
+ CHECK_NOT_MATCH_C ("function()", "bar::function()");
+
+ /* An explicit global scope forces a fully qualified match. */
+ CHECK_NOT_MATCH_C ("foo::function()", "::function");
+ CHECK_NOT_MATCH_C ("foo::function()", "::function()");
+ CHECK_NOT_MATCH_C ("foo::function(int)", "::function()");
+ CHECK_NOT_MATCH_C ("foo::function(int)", "::function(int)");
+
+ CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi()");
+ CHECK_MATCH_C ("abc::def::ghi ( )", "abc::def::ghi()");
+ CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi ( )");
+ CHECK_MATCH_C ("function()", "function()");
+ CHECK_MATCH_C ("foo::function()", "function()");
+ CHECK_MATCH_C ("foo::bar::function()", "function()");
+ CHECK_MATCH_C ("bar::function()", "bar::function()");
+ CHECK_MATCH_C ("foo::bar::function()", "bar::function");
+ CHECK_MATCH_C ("(anonymous namespace)::bar::function(int)",
+ "function(int)");
+ CHECK_MATCH_C ("foo::(anonymous namespace)::bar::function(int)",
+ "function(int)");
+ CHECK_NOT_MATCH_C ("function()", "bar::function");
+ CHECK_NOT_MATCH_C ("foo::function()", "::function");
+}
+
/* If non-NULL, return STR wrapped in quotes. Otherwise, return a
"<null>" string (with no quotes). */

@@ -1890,6 +2111,7 @@ display the offending symbol."),
#endif

#if GDB_SELF_TEST
+ register_self_test (selftests::test_cp_symbol_name_cmp);
register_self_test (selftests::test_cp_remove_params);
#endif
}
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index 1cef3f7..600720f 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -110,6 +110,8 @@ extern struct symbol **make_symbol_overload_list_adl (struct type **arg_types,
extern struct type *cp_lookup_rtti_type (const char *name,
struct block *block);

+extern unsigned int cp_search_name_hash (const char *search_name);
+
extern symbol_name_matcher_ftype *cp_get_symbol_name_matcher
(const lookup_name_info &lookup_name);

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index a267377..60a909f 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -4548,6 +4548,8 @@ static const char *test_symbols[] = {
"ns::foo<char*>",
"ns::foo<int>",
"ns::foo<long>",
+ "ns2::tmpl<int>::foo2",
+ "(anonymous namespace)::A::B::C",

/* A name with all sorts of complications. Starts with "z" to make
it easier for the completion tests below. */
@@ -4627,6 +4629,8 @@ run_test ()
{
CHECK_MATCH ("w", symbol_name_match_type::FULL, true,
EXPECT ("w1::w2"));
+ CHECK_MATCH ("w", symbol_name_match_type::WILD, true,
+ EXPECT ("w1::w2"));
}

/* Same, with a "complicated" symbol. */
@@ -4654,6 +4658,10 @@ run_test ()
{
CHECK_MATCH ("std::zfunction(int)", symbol_name_match_type::FULL, true,
EXPECT ("std::zfunction", "std::zfunction2"));
+ CHECK_MATCH ("zfunction(int)", symbol_name_match_type::WILD, true,
+ EXPECT ("std::zfunction", "std::zfunction2"));
+ CHECK_MATCH ("zfunc", symbol_name_match_type::WILD, true,
+ EXPECT ("std::zfunction", "std::zfunction2"));
}

/* Check that whitespace is ignored appropriately. A symbol with a
@@ -4662,6 +4670,8 @@ run_test ()
static const char expected[] = "ns::foo<int>";
CHECK_MATCH ("ns :: foo < int > ", symbol_name_match_type::FULL, false,
EXPECT (expected));
+ CHECK_MATCH ("foo < int > ", symbol_name_match_type::WILD, false,
+ EXPECT (expected));
}

/* Check that whitespace is ignored appropriately. A symbol with a
@@ -4674,9 +4684,13 @@ run_test ()
{
CHECK_MATCH ("ns :: foo < char * >", symbol_name_match_type::FULL,
completion_mode[i], EXPECT (expected));
+ CHECK_MATCH ("foo < char * >", symbol_name_match_type::WILD,
+ completion_mode[i], EXPECT (expected));

CHECK_MATCH ("ns :: foo < char * > (int)", symbol_name_match_type::FULL,
completion_mode[i], EXPECT (expected));
+ CHECK_MATCH ("foo < char * > (int)", symbol_name_match_type::WILD,
+ completion_mode[i], EXPECT (expected));
}
}

@@ -4687,14 +4701,48 @@ run_test ()
symbol_name_match_type::FULL, true, EXPECT (expected));
CHECK_MATCH ("ns :: foo < char * > ( int ) &&",
symbol_name_match_type::FULL, true, EXPECT (expected));
+ CHECK_MATCH ("foo < char * > ( int ) const",
+ symbol_name_match_type::WILD, true, EXPECT (expected));
+ CHECK_MATCH ("foo < char * > ( int ) &&",
+ symbol_name_match_type::WILD, true, EXPECT (expected));
}

/* Test lookup names that don't match anything. */
{
+ CHECK_MATCH ("bar2", symbol_name_match_type::WILD, false,
+ {});
+
CHECK_MATCH ("doesntexist", symbol_name_match_type::FULL, false,
{});
}

+ /* Some wild matching tests, exercising "(anonymous namespace)",
+ which should not be confused with a parameter list. */
+ {
+ static const char *syms[] = {
+ "A::B::C",
+ "B::C",
+ "C",
+ "A :: B :: C ( int )",
+ "B :: C ( int )",
+ "C ( int )",
+ };
+
+ for (const char *s : syms)
+ {
+ CHECK_MATCH (s, symbol_name_match_type::WILD, false,
+ EXPECT ("(anonymous namespace)::A::B::C"));
+ }
+ }
+
+ {
+ static const char expected[] = "ns2::tmpl<int>::foo2";
+ CHECK_MATCH ("tmp", symbol_name_match_type::WILD, true,
+ EXPECT (expected));
+ CHECK_MATCH ("tmpl<", symbol_name_match_type::WILD, true,
+ EXPECT (expected));
+ }
+
SELF_CHECK (!any_mismatch);

#undef EXPECT
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 5d95308..d17dcfa 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -351,6 +351,7 @@ static VEC (symbolp) *find_label_symbols (struct linespec_state *self,
static void find_linespec_symbols (struct linespec_state *self,
VEC (symtab_ptr) *file_symtabs,
const char *name,
+ symbol_name_match_type name_match_type,
VEC (symbolp) **symbols,
VEC (bound_minimal_symbol_d) **minsyms);

@@ -1873,6 +1874,7 @@ linespec_parse_basic (linespec_parser *parser)

linespec_complete_function (tmp_tracker,
parser->completion_word,
+ symbol_name_match_type::WILD,
source_filename);

if (tmp_tracker.have_completions ())
@@ -1897,6 +1899,7 @@ linespec_parse_basic (linespec_parser *parser)
/* Try looking it up as a function/method. */
find_linespec_symbols (PARSER_STATE (parser),
PARSER_RESULT (parser)->file_symtabs, name,
+ symbol_name_match_type::WILD,
&symbols, &minimal_symbols);

if (symbols != NULL || minimal_symbols != NULL)
@@ -2405,6 +2408,7 @@ convert_explicit_location_to_linespec (struct linespec_state *self,
linespec_p result,
const char *source_filename,
const char *function_name,
+ symbol_name_match_type fname_match_type,
const char *label_name,
struct line_offset line_offset)
{
@@ -2434,8 +2438,8 @@ convert_explicit_location_to_linespec (struct linespec_state *self,
if (function_name != NULL)
{
find_linespec_symbols (self, result->file_symtabs,
- function_name, &symbols,
- &minimal_symbols);
+ function_name, fname_match_type,
+ &symbols, &minimal_symbols);

if (symbols == NULL && minimal_symbols == NULL)
symbol_not_found_error (function_name,
@@ -2475,6 +2479,7 @@ convert_explicit_location_to_sals (struct linespec_state *self,
convert_explicit_location_to_linespec (self, result,
explicit_loc->source_filename,
explicit_loc->function_name,
+ explicit_loc->func_name_match_type,
explicit_loc->label_name,
explicit_loc->line_offset);
return convert_linespec_to_sals (self, result);
@@ -2853,10 +2858,10 @@ linespec_lex_to_end (char **stringp)
void
linespec_complete_function (completion_tracker &tracker,
const char *function,
+ symbol_name_match_type func_match_type,
const char *source_filename)
{
complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
- symbol_name_match_type func_match_type = symbol_name_match_type::WILD;

if (source_filename != NULL)
{
@@ -2895,7 +2900,8 @@ complete_linespec_component (linespec_parser *parser,
{
completion_list fn_list;

- linespec_complete_function (tracker, text, source_filename);
+ linespec_complete_function (tracker, text, symbol_name_match_type::WILD,
+ source_filename);
if (source_filename == NULL)
fn_list = complete_source_filenames (text);

@@ -2960,6 +2966,7 @@ linespec_complete_label (completion_tracker &tracker,
const struct language_defn *language,
const char *source_filename,
const char *function_name,
+ symbol_name_match_type func_name_match_type,
const char *label_name)
{
linespec_parser parser;
@@ -2976,6 +2983,7 @@ linespec_complete_label (completion_tracker &tracker,
PARSER_RESULT (&parser),
source_filename,
function_name,
+ func_name_match_type,
NULL, unknown_offset);
}
CATCH (ex, RETURN_MASK_ERROR)
@@ -3059,7 +3067,7 @@ linespec_complete (completion_tracker &tracker, const char *text)
VEC (bound_minimal_symbol_d) *minimal_symbols;
find_linespec_symbols (PARSER_STATE (&parser),
PARSER_RESULT (&parser)->file_symtabs,
- func_name,
+ func_name, symbol_name_match_type::WILD,
&function_symbols, &minimal_symbols);

PARSER_RESULT (&parser)->function_symbols = function_symbols;
@@ -3940,6 +3948,7 @@ symtabs_from_filename (const char *filename,
static void
find_function_symbols (struct linespec_state *state,
VEC (symtab_ptr) *file_symtabs, const char *name,
+ symbol_name_match_type name_match_type,
VEC (symbolp) **symbols,
VEC (bound_minimal_symbol_d) **minsyms)
{
@@ -3959,8 +3968,7 @@ find_function_symbols (struct linespec_state *state,
add_all_symbol_names_from_pspace (&info, state->search_pspace,
symbol_names, FUNCTIONS_DOMAIN);
else
- add_matching_symbols_to_info (name, symbol_name_match_type::WILD,
- FUNCTIONS_DOMAIN,
+ add_matching_symbols_to_info (name, name_match_type, FUNCTIONS_DOMAIN,
&info, state->search_pspace);

do_cleanups (cleanup);
@@ -3989,6 +3997,7 @@ static void
find_linespec_symbols (struct linespec_state *state,
VEC (symtab_ptr) *file_symtabs,
const char *lookup_name,
+ symbol_name_match_type name_match_type,
VEC (symbolp) **symbols,
VEC (bound_minimal_symbol_d) **minsyms)
{
@@ -4006,6 +4015,7 @@ find_linespec_symbols (struct linespec_state *state,
2) break class::method where method is in class (and not a baseclass) */

find_function_symbols (state, file_symtabs, lookup_name,
+ name_match_type,
symbols, minsyms);

/* If we were unable to locate a symbol of the same name, try dividing
diff --git a/gdb/linespec.h b/gdb/linespec.h
index 27d237a..8759173 100644
--- a/gdb/linespec.h
+++ b/gdb/linespec.h
@@ -191,12 +191,14 @@ extern const char * const linespec_keywords[];
extern void linespec_complete (completion_tracker &tracker,
const char *text);

-/* Complete a function symbol, in linespec mode. If SOURCE_FILENAME
- is non-NULL, limits completion to the list of functions defined in
- source files that match SOURCE_FILENAME. */
+/* Complete a function symbol, in linespec mode, according to
+ FUNC_MATCH_TYPE. If SOURCE_FILENAME is non-NULL, limits completion
+ to the list of functions defined in source files that match
+ SOURCE_FILENAME. */

extern void linespec_complete_function (completion_tracker &tracker,
const char *function,
+ symbol_name_match_type func_match_type,
const char *source_filename);

/* Complete a label symbol, in linespec mode. Only labels of
@@ -208,6 +210,7 @@ extern void linespec_complete_label (completion_tracker &tracker,
const struct language_defn *language,
const char *source_filename,
const char *function_name,
+ symbol_name_match_type name_match_type,
const char *label_name);

/* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
diff --git a/gdb/location.c b/gdb/location.c
index 19d3232..0a9978a 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -245,7 +245,13 @@ explicit_to_string_internal (int as_linespec,
if (need_space)
buf.putc (space);
if (!as_linespec)
- buf.puts ("-function ");
+ {
+ if (explicit_loc->func_name_match_type
+ == symbol_name_match_type::FULL)
+ buf.puts ("-qualified ");
+ else
+ buf.puts ("-function ");
+ }
buf.puts (explicit_loc->function_name);
need_space = 1;
}
@@ -775,6 +781,16 @@ string_to_explicit_location (const char **argp,
set_oarg (explicit_location_lex_one_function (argp, language,
completion_info));
EL_EXPLICIT (location)->function_name = oarg.release ();
+ EL_EXPLICIT (location)->func_name_match_type
+ = symbol_name_match_type::WILD;
+ }
+ else if (strncmp (opt.get (), "-qualified", len) == 0)
+ {
+ set_oarg (explicit_location_lex_one_function (argp, language,
+ completion_info));
+ EL_EXPLICIT (location)->function_name = oarg.release ();
+ EL_EXPLICIT (location)->func_name_match_type
+ = symbol_name_match_type::FULL;
}
else if (strncmp (opt.get (), "-line", len) == 0)
{
diff --git a/gdb/location.h b/gdb/location.h
index 58536e0..0200d42 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -79,6 +79,9 @@ struct explicit_location
/* The function name. Malloc'd. */
char *function_name;

+ /* Whether the function name is fully-qualified or not. */
+ symbol_name_match_type func_name_match_type;
+
/* The name of a label. Malloc'd. */
char *label_name;

diff --git a/gdb/testsuite/gdb.base/langs.exp b/gdb/testsuite/gdb.base/langs.exp
index 8dcd5ee..03c690c 100644
--- a/gdb/testsuite/gdb.base/langs.exp
+++ b/gdb/testsuite/gdb.base/langs.exp
@@ -38,7 +38,7 @@ if [get_compiler_info] {
return -1
}

-gdb_test_multiple "b langs0" "break on nonexistent function in langs.exp" {
+gdb_test_multiple "b -qualified langs0" "break on nonexistent function in langs.exp" {
-re "Function \"langs0\" not defined\..*Make breakpoint pending on future shared library load.*y or .n.. $" {

gdb_test "n" "" "break on nonexistent function in langs.exp"
diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp
index 08f1464..9234fe1 100644
--- a/gdb/testsuite/gdb.cp/meth-typedefs.exp
+++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp
@@ -145,15 +145,37 @@ foreach test $methods {
set func [lindex $test 0]
set result [lindex $test 1]

- gdb_test "list $func" $result
- gdb_test "list '$func'" $result
- if {[gdb_breakpoint $func]} {
- pass "break $func"
+ gdb_test "list -qualified $func" $result
+ gdb_test "list -qualified '$func'" $result
+ if {[gdb_breakpoint "-qualified $func"]} {
+ pass "break -qualified $func"
}
- if {[gdb_breakpoint '$func']} {
- pass "break '$func'"
+ if {[gdb_breakpoint "-qualified '$func'"]} {
+ pass "break -qualified '$func'"
}
}

+# The tests above use -qualified to explicitly pick the one "test"
+# symbol each test cares about. Now check that both "break test(..)"
+# and "list test(..)" without -qualified find "test(..)" in all the 3
+# scopes that have the this particular overload.
+set func "test(aenum, astruct const&, aunion const***)"
+set line1 [gdb_get_line_number " A::FOO::$func"]
+set line2 [gdb_get_line_number " B::$func"]
+set line3 [gdb_get_line_number " $func"]
+
+foreach f [list "$func" "'$func'"] {
+ gdb_test \
+ "list $f" \
+ [multi_line \
+ "file: \".*$srcfile\", line number: $line1" \
+ "file: \".*$srcfile\", line number: $line2" \
+ "file: \".*$srcfile\", line number: $line3"] \
+ "list $f"
+
+ delete_breakpoints
+ gdb_test "break $f" "\\(3 locations\\)"
+}
+
gdb_exit
return 0
diff --git a/gdb/testsuite/gdb.cp/namespace.exp b/gdb/testsuite/gdb.cp/namespace.exp
index 640ee4f..4a6b863 100644
--- a/gdb/testsuite/gdb.cp/namespace.exp
+++ b/gdb/testsuite/gdb.cp/namespace.exp
@@ -120,7 +120,7 @@ gdb_test "break AAA::xyzq" \

# Break on a function in the global namespace.

-gdb_test "break ::ensureOtherRefs" \
+gdb_test "break -qualified ::ensureOtherRefs" \
"Breakpoint.*at $hex: file.*$srcfile2, line $decimal\\."

# Call a function in a nested namespace
--
2.5.5