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