From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 4285 invoked by alias); 2 Jun 2017 12:23:40 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 4214 invoked by uid 89); 2 Jun 2017 12:23:40 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.8 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_STOCKGEN,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy= X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 02 Jun 2017 12:23:33 +0000 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EC3C07AE83 for ; Fri, 2 Jun 2017 12:23:20 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EC3C07AE83 Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=palves@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com EC3C07AE83 Received: from cascais.lan (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id 297164DA6B for ; Fri, 2 Jun 2017 12:23:19 +0000 (UTC) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 39/40] Breakpoints in symbols with ABI tags (PR c++/19436) Date: Fri, 02 Jun 2017 12:23:00 -0000 Message-Id: <1496406158-12663-40-git-send-email-palves@redhat.com> In-Reply-To: <1496406158-12663-1-git-send-email-palves@redhat.com> References: <1496406158-12663-1-git-send-email-palves@redhat.com> X-SW-Source: 2017-06/txt/msg00028.txt.bz2 Trying to set a breakpoint in a function with an ABI tag does not work currently. E.g., debugging gdb itself, we see this with the "string_printf" function: (top-gdb) b string_print [TAB] (top-gdb) b string_printf[abi:cxx11](char const*, ...) [RET] No source file named string_printf[abi. Make breakpoint pending on future shared library load? (y or [n]) Quoting doesn't help: (top-gdb) b 'string_printf[abi:cxx11]'(char const*, ...) malformed linespec error: unexpected string, "(char const*, ...)" (top-gdb) b 'string_printf[abi:cxx11](char const*, ...)' No source file named string_printf[abi. Make breakpoint pending on future shared library load? (y or [n]) n This patch fixes this, and takes it a bit further. The actual symbol name as demangled by libiberty's demangler is really string_printf[abi:cxx11](char const*, ...) however, this patch makes it possible to set the breakpoint with string_printf(char const*, ...) too. I.e., ignoring the ABI tag. And to match, it teaches the completer to complete the symbol name without the ABI tag, i.e., "string_pri" -> "string_printf(char const*, ...)" If however, you really want to break on a symbol with the tag, then you simply start writing the tag, and GDB will preserve it, like: "string_printf[a" -> "string_printf[abi:cxx11](char const*, ...)" gdb/ChangeLog: yyyy-mm-dd Pedro Alves * completer.h (completion_match_for_lcd) : New methods. : Consider ignored ranges. : Clear ignored ranges. : New fields. * cp-support.c (cp_search_name_hash): Ignore ABI tags. (cp_symbol_name_matches_1, cp_fq_symbol_name_matches): Pass the completion_match_for_lcd pointer to strncmp_iw_with_mode. (test_cp_symbol_name_cmp): Add [abi:...] tags unit tests. * language.c (default_symbol_name_matcher): Pass the completion_match_for_lcd pointer to strncmp_iw_with_mode. * linespec.c (linespec_lexer_lex_string): Don't tokenize ABI tags. * utils.c (skip_abi_tag): New function. (strncmp_iw_with_mode): Add completion_match_for_lcd parameter. Handle ABI tags. * utils.h (strncmp_iw_with_mode): Add completion_match_for_lcd parameter. gdb/testsuite/ChangeLog: yyyy-mm-dd Pedro Alves * gdb.linespec/cpls-abi-tag.cc: New file. * gdb.linespec/cpls-abi-tag.exp: New file. --- gdb/completer.h | 62 +++++- gdb/cp-support.c | 53 ++++- gdb/language.c | 5 +- gdb/linespec.c | 5 + gdb/testsuite/gdb.linespec/cpls-abi-tag.cc | 93 +++++++++ gdb/testsuite/gdb.linespec/cpls-abi-tag.exp | 312 ++++++++++++++++++++++++++++ gdb/utils.c | 96 ++++++++- gdb/utils.h | 14 +- 8 files changed, 622 insertions(+), 18 deletions(-) create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.cc create mode 100644 gdb/testsuite/gdb.linespec/cpls-abi-tag.exp diff --git a/gdb/completer.h b/gdb/completer.h index db781ab..16b1593 100644 --- a/gdb/completer.h +++ b/gdb/completer.h @@ -122,27 +122,81 @@ private: "b push_ba" on a C++ program usually completes to std::vector<...>::push_back, std::string::push_back etc. In such case, the symbol comparison routine will set the LCD match to point - into the "push_back" substring within the symbol's name string. */ + into the "push_back" substring within the symbol's name string. + Also, in some cases, the symbol comparison routine will want to + ignore parts of the symbol name for LCD purposes, such as for + example symbols with abi tags in C++. In such cases, the symbol + comparison routine will set MARK_IGNORED_RANGE to mark the ignored + substrings of the matched string. The resulting LCD string with + the ignored parts stripped out is computed at the end of a + completion match sequence iff we had a positive match. */ class completion_match_for_lcd { public: + /* Get the resulting LCD, after a successful match. */ + const char *match () + { return m_match; } + /* Set the match for LCD. See m_match's description. */ void set_match (const char *match) { m_match = match; } - /* Get the resulting LCD, after a successful match. */ + /* Mark the range between [BEGIN, END) as ignored. */ + void mark_ignored_range (const char *begin, const char *end) + { m_ignored_ranges.emplace_back (begin, end); } + + /* Get the resulting LCD, after a successful match. If there are + ignored ranges, then this builds a new string with the ignored + parts removed (and stores it internally). As such, the result of + this call is only good for the current completion match + sequence. */ const char *finish () - { return m_match; } + { + if (m_ignored_ranges.empty ()) + return m_match; + else + { + m_finished_storage.clear (); + + const char *prev = m_match; + for (const auto &range : m_ignored_ranges) + { + m_finished_storage.append (prev, range.first); + prev = range.second; + } + m_finished_storage.append (prev); + + return m_finished_storage.c_str (); + } + } /* Prepare for another completion matching sequence. */ void clear () - { m_match = NULL; } + { + m_match = NULL; + m_ignored_ranges.clear (); + } private: /* The completion match result for LCD. This is usually either a pointer into to a substring within a symbol's name, or to the storage of the pairing completion_match object. */ const char *m_match; + + /* The ignored substring ranges within M_MATCH. E.g., if we were + looking for completion matches for C++ functions starting with + "functio" + and successfully match: + "function[abi:cxx11](int)" + the ignored ranges vector will contain an entry that delimits the + "[abi:cxx11]" substring, such that calling finish() results in: + "function(int)" + */ + std::vector> m_ignored_ranges; + + /* Storage used by the finish() method, if it has to compute a new + string. */ + std::string m_finished_storage; }; /* Convenience aggregate holding info returned by the symbol name diff --git a/gdb/cp-support.c b/gdb/cp-support.c index 4c353c5..0494a41 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -1660,7 +1660,23 @@ cp_search_name_hash (const char *search_name) if (prefix_len != 0) search_name += prefix_len + 2; - return default_search_name_hash (search_name); + unsigned int hash = 0; + for (const char *string = search_name; *string != '\0'; ++string) + { + string = skip_spaces_const (string); + + if (*string == '(') + break; + + /* Ignore ABI tags such as "[abi:cxx11]. */ + if (*string == '[' + && startswith (string + 1, "abi:") + && string[5] != ':') + break; + + hash = SYMBOL_HASH_NEXT (hash, *string); + } + return hash; } /* Helper for cp_symbol_name_matches (i.e., symbol_name_matcher_ftype @@ -1705,11 +1721,13 @@ cp_symbol_name_matches_1 (const char *symbol_search_name, completion_match_result *comp_match_res) { const char *sname = symbol_search_name; + completion_match_for_lcd *match_for_lcd + = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); while (true) { if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len, - mode, language_cplus) == 0) + mode, language_cplus, match_for_lcd) == 0) { if (comp_match_res != NULL) { @@ -1740,14 +1758,15 @@ cp_fq_symbol_name_matches (const char *symbol_search_name, { /* Get the demangled name. */ const std::string &name = lookup_name.cplus ().lookup_name (); - + completion_match_for_lcd *match_for_lcd + = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); strncmp_iw_mode mode = (lookup_name.completion_mode () ? strncmp_iw_mode::NORMAL : strncmp_iw_mode::MATCH_PARAMS); if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (), - mode, language_cplus) == 0) + mode, language_cplus, match_for_lcd) == 0) { if (comp_match_res != NULL) { @@ -1964,6 +1983,32 @@ test_cp_symbol_name_cmp () "function(int)"); CHECK_NOT_MATCH_C ("function()", "bar::function"); CHECK_NOT_MATCH_C ("foo::function()", "::function"); + + /* Test ABI tag matching/ignoring. */ + + /* If the symbol name has an ABI tag, but the lookup name doesn't, + then the ABI tag in the symbol name is ignored. */ + CHECK_MATCH_C ("function[abi:foo]()", "function"); + CHECK_MATCH_C ("function[abi:foo](int)", "function"); + CHECK_MATCH_C ("function[abi:foo]()", "function ()"); + CHECK_NOT_MATCH_C ("function[abi:foo]()", "function (int)"); + + CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo](int)", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo] ()"); + CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function"); + CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function"); + CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function[abi:foo]"); + CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] ()"); + CHECK_NOT_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] (int)"); + + CHECK_MATCH_C ("function [abi:foo][abi:bar] ( )", "function [abi:foo]"); + + /* If the symbol name does not have an ABI tag, while the lookup + name has one, then there's no match. */ + CHECK_NOT_MATCH_C ("function()", "function[abi:foo]()"); + CHECK_NOT_MATCH_C ("function()", "function[abi:foo]"); } /* If non-NULL, return STR wrapped in quotes. Otherwise, return a diff --git a/gdb/language.c b/gdb/language.c index 377d748..6332c82 100644 --- a/gdb/language.c +++ b/gdb/language.c @@ -717,13 +717,14 @@ default_symbol_name_matcher (const char *symbol_search_name, completion_match_result *comp_match_res) { const std::string &name = lookup_name.name (); - + completion_match_for_lcd *match_for_lcd + = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); strncmp_iw_mode mode = (lookup_name.completion_mode () ? strncmp_iw_mode::NORMAL : strncmp_iw_mode::MATCH_PARAMS); if (strncmp_iw_with_mode (symbol_search_name, name.c_str (), name.size (), - mode, language_minimal) == 0) + mode, language_minimal, match_for_lcd) == 0) { if (comp_match_res != NULL) { diff --git a/gdb/linespec.c b/gdb/linespec.c index d17dcfa..d593cd6 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -737,6 +737,11 @@ linespec_lexer_lex_string (linespec_parser *parser) if (PARSER_STREAM (parser)[1] == ':') ++(PARSER_STREAM (parser)); + /* Do not tokenize ABI tags such as "[abi:cxx11]". */ + else if (PARSER_STREAM (parser) - start > 4 + && startswith (PARSER_STREAM (parser) - 4, "[abi")) + ++(PARSER_STREAM (parser)); + /* Do not tokenify if the input length so far is one (i.e, a single-letter drive name) and the next character is a directory separator. This allows Windows-style diff --git a/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc b/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc new file mode 100644 index 0000000..3916eda --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpls-abi-tag.cc @@ -0,0 +1,93 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2017 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#define ABI1 __attribute__ ((abi_tag ("tag1"))) +#define ABI2 __attribute__ ((abi_tag ("tag2"))) +#define ABI3 __attribute__ ((abi_tag ("tag3"))) + +void ABI1 +test_abi_tag_function (int) +{ +} + +void ABI1 +test_abi_tag_ovld_function () +{ +} + +void ABI1 +test_abi_tag_ovld_function (int) +{ +} + +/* Code for the overload functions, different ABI tag test. */ + +void +test_abi_tag_ovld2_function () +{ +} + +void ABI1 +test_abi_tag_ovld2_function (short) +{ +} + +void ABI2 +test_abi_tag_ovld2_function (int) +{ +} + +void ABI2 +test_abi_tag_ovld2_function (long) +{ +} + +struct ABI1 test_abi_tag_struct +{ + ABI2 test_abi_tag_struct (); + ABI2 ~test_abi_tag_struct (); +}; + +test_abi_tag_struct::test_abi_tag_struct () +{} + +test_abi_tag_struct::~test_abi_tag_struct () +{} + +ABI3 test_abi_tag_struct s; + +/* Code for the abi-tag in parameters test. */ + +struct ABI2 abi_tag_param_struct1 +{}; + +struct ABI2 abi_tag_param_struct2 +{}; + +void +test_abi_tag_in_params (abi_tag_param_struct1) +{} + +void +test_abi_tag_in_params (abi_tag_param_struct1, abi_tag_param_struct2) +{} + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp b/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp new file mode 100644 index 0000000..f4280c2 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpls-abi-tag.exp @@ -0,0 +1,312 @@ +# Copyright 2017 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the gdb testsuite. + +load_lib completion-support.exp + +standard_testfile cpls-abi-tag.cc + +if {[prepare_for_testing "failed to prepare" $testfile \ + [list $srcfile] {c++ debug}]} { + return -1 +} + +gdb_test_no_output "set max-completions unlimited" + +set timeout 5 + +# Check that the explicit location completer manages to find the next +# option name after a "-function function" option. A useful test when +# the -function options's argument is a C++ operator, which can +# include characters like '-'. + +proc check_explicit_skips_function_argument {function} { + test_gdb_complete_unique \ + "b -function $function -sour" \ + "b -function $function -source" +} + +# Helper function for the operator new/new[] tests. CLASS_NAME is the +# name of the class that contains the operator we're testing. +# BRACKETS is set to [] if testing operator new[], and to empty if +# testing operator new. + +proc_with_prefix test_abi_tag {} { + with_test_prefix "completion" { + foreach cmd_prefix {"b" "b -function"} { + # Complete all prefixes between "_funcio" and the full + # prototype. The ABI tag is not considered for actual + # completion. + + with_test_prefix "skip tag" { + # set location "test_abi_tag_function\[abi:tag1\](int)" + set location "test_abi_tag_function(int)" + set line "$cmd_prefix $location" + set start [index_after "_functio" $line] + test_complete_prefix_range $line $start + } + + # Now the same, but start completing at the [. In that case, + # GDB considers the ABI tag as part of actual completion. + with_test_prefix "at tag" { + set location "test_abi_tag_function\[abi:tag1\](int)" + set line "$cmd_prefix $location" + set start [index_after "_function" $line] + test_complete_prefix_range $line $start + } + + # Same, but with extra spaces. Note that the original spaces in + # the input line are preserved after completion. + + with_test_prefix "spaces" { + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] (" \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] (int)" + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] ( int " \ + "$cmd_prefix test_abi_tag_function \[abi:tag1\] ( int )" + } + } + } + + with_test_prefix "set breakpoints" { + foreach cmd_prefix {"b" "b -function"} { + # Test setting breakpoints. If the symbol name has an ABI + # tag, but the input linespec doesn't, then the ABI tag in the + # symbol name is ignored. + set linespec_list { + "test_abi_tag_function" + "test_abi_tag_function[abi:tag1]" + "test_abi_tag_function[abi:tag1](int)" + "test_abi_tag_function [abi:tag1]" + "test_abi_tag_function [abi:tag1] ( int )" + "test_abi_tag_function(int)" + "test_abi_tag_function (int)" + "test_abi_tag_function ( int )" + } + foreach linespec $linespec_list { + check_bp_locations_match_list \ + "$cmd_prefix $linespec" [list $location] + } + } + } + + with_test_prefix "set breakpoints wrong ABI tag" { + foreach cmd_prefix {"b" "b -function"} { + # Test setting breakpoints with the wrong ABI tag. Should + # fail to create the breakpoints. Completion should not find + # any match either. + set linespec_list { + "test_abi_tag_function[abi:tag2]" + "test_abi_tag_function[abi:tag2](int)" + "test_abi_tag_function [abi:tag2]" + "test_abi_tag_function [abi:tag2] ( int )" + } + foreach linespec $linespec_list { + check_setting_bp_fails "$cmd_prefix $linespec" + test_gdb_complete_none "$cmd_prefix $linespec" + } + + } + } + + # Test completion of overloaded functions with ABI tags. + with_test_prefix "completion of overloaded functions" { + foreach cmd_prefix {"b" "b -function"} { + set completion_list { + "test_abi_tag_ovld_function[abi:tag1]()" + "test_abi_tag_ovld_function[abi:tag1](int)" + } + + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld_f" "unction(" \ + $completion_list + + # Otherwise, it's considered. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld_function\[" "abi:tag1\](" \ + $completion_list + + } + } + + # Test setting breakpoints on overloaded functions with ABI tags. + with_test_prefix "breakpoints on overloaded functions" { + foreach cmd_prefix {"b" "b -function"} { + set completion_list { + "test_abi_tag_ovld_function[abi:tag1]()" + "test_abi_tag_ovld_function[abi:tag1](int)" + } + set location_list { + "test_abi_tag_ovld_function" + "test_abi_tag_ovld_function[abi:tag1]" + } + foreach linespec $location_list { + check_bp_locations_match_list \ + "$cmd_prefix $linespec" $completion_list + } + + } + } + + with_test_prefix "completion of overloaded functions different abi" { + foreach cmd_prefix {"b" "b -function"} { + # Test completion of overloaded functions with ABI tags. + set completion_list { + "test_abi_tag_ovld2_function()" + "test_abi_tag_ovld2_function[abi:tag1](short)" + "test_abi_tag_ovld2_function[abi:tag2](int)" + "test_abi_tag_ovld2_function[abi:tag2](long)" + } + + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld2_f" "unction(" \ + $completion_list + + # Otherwise, it's considered. Match stops at the part of + # the tag that diverges, and the completion list only + # shows matches with ABI tags. + set completion_list { + "test_abi_tag_ovld2_function[abi:tag1](short)" + "test_abi_tag_ovld2_function[abi:tag2](int)" + "test_abi_tag_ovld2_function[abi:tag2](long)" + } + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld2_function\[" "abi:tag" \ + $completion_list + + # If you disambiguate, matches include only locations for + # the specified tag. + set completion_list { + "test_abi_tag_ovld2_function[abi:tag2](int)" + "test_abi_tag_ovld2_function[abi:tag2](long)" + } + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_ovld2_function\[abi:tag2" "\](" \ + $completion_list + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_ovld2_function\[abi:tag1" \ + "$cmd_prefix test_abi_tag_ovld2_function\[abi:tag1\](short)" + } + } + + with_test_prefix "completion of struct prefixes with tags" { + foreach cmd_prefix {"b" "b -function"} { + # Test completion of methods of structs with ABI tags. + set completion_list { + "test_abi_tag_struct[abi:tag1]::test_abi_tag_struct[abi:tag2]()" + "test_abi_tag_struct[abi:tag1]::~test_abi_tag_struct[abi:tag2]()" + } + + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_struc" "t::" \ + $completion_list + + # Otherwise, it's considered. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_struct\[" "abi:tag1\]::" \ + $completion_list + + # Mix and match different abi tag positions. + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_struct::t" \ + "$cmd_prefix test_abi_tag_struct::test_abi_tag_struct()" + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::t" \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct()" + + test_gdb_complete_unique \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct\[" \ + "$cmd_prefix test_abi_tag_struct\[abi:tag1\]::test_abi_tag_struct\[abi:tag2\]()" + } + } + + with_test_prefix "abi tag in parameters" { + foreach cmd_prefix {"b" "b -function"} { + # Complete all prefixes between "_funcio" and the full + # prototype. The ABI tag is not considered for actual + # completion. + + set completion_list { + "test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2])" + "test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2], abi_tag_param_struct2[abi:tag2])" + } + # If the input string does not include the ABI tag, then + # actual completion ignores it. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_in_para" "ms(abi_tag_param_struct1" \ + $completion_list + + # If OTOH the input string includes the ABI tag, then it + # is considered. + test_gdb_complete_multiple \ + "$cmd_prefix " "test_abi_tag_in_params(abi_tag_param_struct1\[ab" "i:tag2\]"\ + $completion_list + + set location_list { + "test_abi_tag_in_params(abi_tag_param_struct1[abi:tag2], abi_tag_param_struct2[abi:tag2])" + } + + set tags {"" "\[abi:tag2\]"} + foreach tag1 $tags { + foreach tag2 $tags { + set linespec "test_abi_tag_in_params(abi_tag_param_struct1${tag1}, abi_tag_param_struct2${tag2})" + check_bp_locations_match_list \ + "$cmd_prefix $linespec" $location_list + } + } + } + } + + # Check that the explicit location completer manages to find the + # option name after -function, when the -function's argument is a + # function with an ABI tag. + check_explicit_skips_function_argument \ + "test_abi_tag_function\[abi:unknown\](int)" +} + +test_abi_tag +return + +# The testcase driver. Calls all test procedures. + +proc test_driver {} { + operator-delete + operator-delete\[\] + operator-new + operator-new\[\] + operator()-unique + operator()-ambiguous + operator\[\]-unique + operator\[\]-ambiguous + ops-valid-ambiguous + ops-valid-unique + ops-invalid + conversion-operator + assignment-operator + arrow-operator +} + +test_driver diff --git a/gdb/utils.c b/gdb/utils.c index 484c1ef..78578b3 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -2504,12 +2504,40 @@ cp_is_operator (const char *string, const char *start) && !valid_identifier_name_char (string[CP_OPERATOR_LEN])); } +/* If *NAME points at an ABI tag, skip it and return true. Otherwise + leave *NAME unmodified and return false. (see GCC's abi_tag + attribute), such names are demangled as e.g., + "function[abi:cxx11]()". */ + +static bool +skip_abi_tag (const char **name) +{ + const char *p = *name; + + if (startswith (p, "[abi:")) + { + p += 5; + + while (valid_identifier_name_char (*p)) + p++; + + if (*p == ']') + { + p++; + *name = p; + return true; + } + } + return false; +} + /* See utils.h. */ int strncmp_iw_with_mode (const char *string1, const char *string2, size_t string2_len, strncmp_iw_mode mode, - enum language language) + enum language language, + completion_match_for_lcd *match_for_lcd) { const char *string1_start = string1; const char *end_str2 = string2 + string2_len; @@ -2528,6 +2556,37 @@ strncmp_iw_with_mode (const char *string1, const char *string2, skip_spaces = false; } + /* Skip [abi:cxx11] tags in the symbol name if the lookup name + doesn't include them. E.g.: + + string1: function[abi:cxx1](int) + string2: function + + string1: function[abi:cxx1](int) + string2: function(int) + + string1: Struct[abi:cxx1]::function() + string2: Struct::function() + + string1: function(Struct[abi:cxx1], int) + string2: function(Struct, int) + */ + if (string2 == end_str2 + || (*string2 != '[' && !valid_identifier_name_char (*string2))) + { + const char *abi_start = string1; + + /* There can be more than one tag. */ + while (*string1 == '[' && skip_abi_tag (&string1)) + ; + + if (match_for_lcd != NULL && abi_start != string1) + match_for_lcd->mark_ignored_range (abi_start, string1); + + while (isspace (*string1)) + string1++; + } + if (*string1 == '\0' || string2 == end_str2) break; @@ -2661,7 +2720,40 @@ strncmp_iw_with_mode (const char *string1, const char *string2, if (string2 == end_str2) { if (mode == strncmp_iw_mode::NORMAL) - return 0; + { + /* Strip abi tag markers from the matched symbol name. + Usually the ABI marker will be found on function name + (automatically added because the function returns an + object marked with an ABI tag). However, it's also + possible to see a marker in one of the function + parameters, for example. + + string2 (lookup name): + func + symbol name: + function(some_struct[abi:cxx11], int) + + and for completion LCD computation we want to say that + the match was for: + function(some_struct, int) + */ + if (match_for_lcd != NULL) + { + while ((string1 = strstr (string1, "[abi:")) != NULL) + { + const char *abi_start = string1; + + /* There can be more than one tag. */ + while (skip_abi_tag (&string1) && *string1 == '[') + ; + + if (abi_start != string1) + match_for_lcd->mark_ignored_range (abi_start, string1); + } + } + + return 0; + } else return (*string1 != '\0' && *string1 != '('); } diff --git a/gdb/utils.h b/gdb/utils.h index 4ce263e..426e4f1 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -52,12 +52,14 @@ enum class strncmp_iw_mode /* Helper for strcmp_iw and strncmp_iw. Exported so that languages can implement both NORMAL and MATCH_PARAMS variants in a single - function and defer part of the work to strncmp_iw_with_mode. */ -extern int strncmp_iw_with_mode (const char *string1, - const char *string2, - size_t string2_len, - strncmp_iw_mode mode, - enum language language); + function and defer part of the work to strncmp_iw_with_mode. + MATCH_FOR_LCD is passed down so that the function can mark parts of + the symbol name as ignored for completion matching purposes (e.g., + to handle abi tags). */ +extern int strncmp_iw_with_mode + (const char *string1, const char *string2, size_t string2_len, + strncmp_iw_mode mode, enum language language, + completion_match_for_lcd *match_for_lcd = NULL); /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any differences in whitespace. STRING2_LEN is STRING2's length. -- 2.5.5