Support template lookups in strncmp_iw_with_mode

This patch adds support for wild template parameter list matches, similar
to how ABI tags or function overloads are now handled.

With this patch, users will be able to "gloss over" the details of matching
template parameter lists.  This is accomplished by adding (yet more) logic
to strncmp_iw_with_mode to skip parameter lists if none is explicitly given
by the user.

Here's a simple example using gdb.linespec/cpls-ops.exp:

Before
------
(gdb) ptype test_op_call
type = struct test_op_call {
  public:
    void operator()(void);
    void operator()(int);
    void operator()(long);
    void operator()<int>(int *);
}
(gdb) b test_op_call::operator()
Breakpoint 1 at 0x400583: test_op_call::operator(). (3 locations)
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   <MULTIPLE>
1.1                         y     0x400583 in test_op_call::operator()(int)
                                                   at cpls-ops.cc:43
1.2                         y     0x40058e in test_op_call::operator()()
                                                   at cpls-ops.cc:47
1.3                         y     0x40059e in test_op_call::operator()(long)
                                                   at cpls-ops.cc:51

The breakpoint at test_op_call::operator()<int> was never set.

After
-----
(gdb) b test_op_call::operator()
Breakpoint 1 at 0x400583: test_op_call::operator(). (4 locations)
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   <MULTIPLE>
1.1                         y     0x400583 in test_op_call::operator()(int)
                                                   at cpls-ops.cc:43
1.2                         y     0x40058e in test_op_call::operator()()
                                                   at cpls-ops.cc:47
1.3                         y     0x40059e in test_op_call::operator()(long)
                                                   at cpls-ops.cc:51
1.4                         y     0x4008d0 in test_op_call::operator()<int>(int*)
                                                   at cpls-ops.cc:57

Similar to how scope lookups work, passing "-qualified" to the break command
will cause a literal lookup of the symbol.  In the example immediately above,
this will cause GDB to only find the three non-template functions.
This commit is contained in:
Keith Seitz 2022-02-24 16:42:22 -08:00
parent b05752c223
commit 64a9760601
10 changed files with 800 additions and 40 deletions

View file

@ -3,6 +3,20 @@
*** Changes since GDB 11 *** Changes since GDB 11
* Improved C++ template support
GDB now treats functions/types involving C++ templates like it does function
overloads. Users may omit parameter lists to set breakpoints on families of
template functions, including types/functions composed of multiple template types:
(gdb) break template_func(template_1, int)
The above will set breakpoints at every function `template_func' where
the first function parameter is any template type named `template_1' and
the second function parameter is `int'.
TAB completion also gains similar improvements.
* The FreeBSD native target now supports async mode. * The FreeBSD native target now supports async mode.
* Configure changes * Configure changes

View file

@ -1692,6 +1692,12 @@ cp_search_name_hash (const char *search_name)
&& string[5] != ':') && string[5] != ':')
break; break;
/* Ignore template parameter lists. */
if (string[0] == '<'
&& string[1] != '(' && string[1] != '<' && string[1] != '='
&& string[1] != ' ' && string[1] != '\0')
break;
hash = SYMBOL_HASH_NEXT (hash, *string); hash = SYMBOL_HASH_NEXT (hash, *string);
} }
return hash; return hash;
@ -1745,7 +1751,7 @@ cp_symbol_name_matches_1 (const char *symbol_search_name,
while (true) while (true)
{ {
if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len, if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len,
mode, language_cplus, match_for_lcd) == 0) mode, language_cplus, match_for_lcd, true) == 0)
{ {
if (comp_match_res != NULL) if (comp_match_res != NULL)
{ {

View file

@ -16888,6 +16888,21 @@ also use the @value{GDBN} command-line word completion facilities to list the
available choices, or to finish the type list for you. available choices, or to finish the type list for you.
@xref{Completion,, Command Completion}, for details on how to do this. @xref{Completion,, Command Completion}, for details on how to do this.
@item @r{Breakpoints in template functions}
Similar to how overloaded symbols are handled, @value{GDBN} will ignore
template parameter lists when it encounters a symbol which includes a
C@t{++} template. This permits setting breakpoints on families of template functions
or functions whose parameters include template types.
The @kbd{-qualified} flag may be used to override this behavior, causing
@value{GDBN} to search for a specific function or type.
The @value{GDBN} command-line word completion facility also understands
template parameters and may be used to list available choices or finish
template parameter lists for you. @xref{Completion,, Command Completion}, for
details on how to do this.
@item @r{Breakpoints in functions with ABI tags} @item @r{Breakpoints in functions with ABI tags}
The GNU C@t{++} compiler introduced the notion of ABI ``tags'', which The GNU C@t{++} compiler introduced the notion of ABI ``tags'', which

View file

@ -742,6 +742,34 @@ template<class C> int FunctionArg<C>::method(Empty<void (FunctionArg<C>)> &arg)
Empty<void(FunctionArg<int>)> empty; Empty<void(FunctionArg<int>)> empty;
FunctionArg<int> arg; FunctionArg<int> arg;
template <typename T1>
struct Foozle
{
int x;
T1 t;
template <typename T2>
T2 fogey (T2 plop);
};
template <typename T1>
template <typename T2>
T2 Foozle<T1>::fogey (T2 plop)
{
return plop;
}
template <typename T>
int operator< (T &lhs, T &rhs)
{
return 0;
}
template <typename T>
int operator<< (T &obj, T &val)
{
return 1;
};
int main() int main()
{ {
int i; int i;
@ -815,5 +843,24 @@ int main()
arg.method(empty); arg.method(empty);
Empty<int> e;
Foozle<int> fzi;
x = fzi.fogey (0);
c = fzi.fogey<char> ('a');
e = fzi.fogey<Empty<int>> (e);
Foozle<char> fzc;
c = fzc.fogey ('b');
x = fzc.fogey<int> (0);
e = fzc.fogey<Empty<int>> (e);
Foozle<Empty<int>> fze;
e = fze.fogey (e);
c = fze.fogey<char> ('c');
x = fze.fogey<int> (0);
z = e < e;
z += e << e;
z += fzi < fzi;
z += fzi << fzi;
return 0; /* Final breakpoint. */ return 0; /* Final breakpoint. */
} }

View file

@ -614,3 +614,70 @@ gdb_test "print Garply<Garply<char> >::garply" \
# Now should work fine # Now should work fine
gdb_test "break Garply<Garply<char> >::garply" \ gdb_test "break Garply<Garply<char> >::garply" \
"Breakpoint \[0-9\]* at $hex: file .*templates.cc, line.*" "Breakpoint \[0-9\]* at $hex: file .*templates.cc, line.*"
#
# Template wild-matching tests
#
# Turn off "ask" when multiple symbols are seen.
gdb_test_no_output "set multiple-symbols all"
# Test setting breakpoints in a method of all class template instantiations,
# including overloads.
gdb_test "break Foo::foo" "Breakpoint.*at.* \\(3 locations\\)"
foreach t [list "int" "char" "volatile char *"] {
gdb_breakpoint "Foo<$t>::foo (int, $t)"
gdb_breakpoint "Foo::foo (int, $t)"
}
gdb_test "break Bar::bar" "Breakpoint.*at.* \\(2 locations\\)"
gdb_test "break Bar::bar (int, int)" "Breakpoint.*at.* \\(2 locations\\)"
foreach val [list 1 33] {
gdb_breakpoint "Bar<int, $val>::bar (int, int)"
}
# Test setting breakpoints in a member function template of a class template,
# including overloads.
gdb_test "break Foozle::fogey" "Breakpoint.*at.* \\(9 locations\\)" \
"break at template method fogey"
foreach t [list "int" "char" "Empty<int>"] {
gdb_test "break Foozle::fogey ($t)" "Breakpoint.*at.* \\(3 locations\\)"
gdb_test "break Foozle::fogey<$t>" "Breakpoint.*at.* \\(3 locations\\)"
foreach u [list "int" "char" "Empty<int>"] {
gdb_breakpoint "Foozle<$t>::fogey<$u>" message
gdb_breakpoint "Foozle<$t>::fogey<$u> ($u)" message
}
}
# Test templated operators < and <<. Restrict results to only the test
# source file.
# operator<:
# 1. operator< (const T2&, const T2&)
# 2. operator< (const T2&, char)
# 3. operator< <Empty<int>>
# 4. operator< <Foozle<in>>
#
# operator<<:
# 1. operator<< <Empty<int>>
# 2. operator<< <Foozle<int>>
gdb_test "break -source $srcfile -func operator<" \
"Breakpoint.*at.* \\(4 locations\\)"
gdb_test "break -source $srcfile -func operator<<" \
"Breakpoint.*at.* \\(2 locations\\)"
foreach t [list "Empty" "Foozle"] {
set tt "$t<int>"
gdb_breakpoint "operator< <$tt>" message
gdb_breakpoint "operator<< <$tt>" message
# Try a specific instance, both with and without whitespace
# after the template-template parameter.
gdb_breakpoint "operator< <$tt> ($tt&, $tt&)" message
gdb_breakpoint "operator< <$tt > ($tt&, $tt&)" message
gdb_breakpoint "operator<< <$tt> ($tt&, $tt&)" message
gdb_breakpoint "operator<< <$tt > ($tt&, $tt&)" message
}
# Test that "-qualified" finds no matching locations.
gdb_test_no_output "set breakpoint pending off"
gdb_test "break -qualified Foozle::fogey" \
"Function \"Foozle::fogey\" not defined."

View file

@ -16,6 +16,7 @@
# This file is part of the gdb testsuite. # This file is part of the gdb testsuite.
load_lib completion-support.exp load_lib completion-support.exp
load_lib data-structures.exp
standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc
@ -32,6 +33,217 @@ if { ![readline_is_used] } {
return -1 return -1
} }
#
# Some convenience procedures for testing template parameter list
# completion.
#
# For the variable named ARGLISTVAR, which should be the name of an
# argument list in the calling frame, "consume" the top-most token.
# [See comments for makefoo for description of arglist format.]
proc consume {arglistvar} {
upvar $arglistvar arglist
# ARGLIST is a string -- simply strip off the first character.
set arglist [string range $arglist 1 end]
}
# Create a function template named NAME, using the given stack ID to grab
# NUM template parameters. The result is pushed back onto the
# stack. NUM may be "all," in which case we use the entire stack
# to create the function template, including function arguments.
# The resulting template function's arguments are taken from the test
# source code for the function "foo" and is not generalized.
proc maket {sid name {num 1}} {
# Set up a temporary stack of parameters. This will reverse
# the order in SID so that when they are popped again below,
# we get them in the correct order. This also takes into account
# how many levels of the result stack we want to consider.
set paramstack [::Stack::new]
if {[string equal $num "all"]} {
while {![stack empty $sid]} {
stack push $paramstack [stack pop $sid]
}
} else {
for {set i 0} {$i < $num} {incr i} {
stack push $paramstack [stack pop $sid]
}
}
# Construct the function template and push it back to the
# top of the stack given by SID.
set result ""
set first true
while {![stack empty $paramstack]} {
set top [stack pop $paramstack]
if {$first} {
set first false
} else {
append result ", "
}
append result $top
}
# Save argument list.
set args $result
# GDB outputs "> >" instead of ">>".
if {[string index $top end] == ">"} {
append result " "
}
set result "$name<$result>"
if {[string equal $num "all"]} {
append result "($args)"
}
stack push $sid $result
stack delete $paramstack
}
# Given the stack SID and the name of a variable of the desired template
# parameters, construct the actual template parameter and push it to the
# top of the stack.
proc makearg {sid arglistvar} {
upvar $arglistvar arglist
set c [string index $arglist 0]
consume arglist
switch $c {
A -
B {
makearg $sid arglist
makearg $sid arglist
maket $sid $c 2
}
a -
b -
c -
d {
makearg $sid arglist
maket $sid $c
}
i {
stack push $sid "int"
}
n {
# These are not templates.
set c [string index $arglist 0]
stack push $sid "n::n$c"
consume arglist
}
N {
set c [string index $arglist 0]
makearg $sid arglist
set top [stack pop $sid]
stack push $sid "n::N$top"
}
default { error "unhandled arglist identifier: '$c'" }
}
}
# Given ARGLIST, construct a class template for the type and return
# it as a string.
proc maketype {arglist} {
set s [Stack::new]
makearg $s arglist
set result [stack pop $s]
stack delete $s
return $result
}
# Returns a function template declaration for the function "foo" in the
# corresponding test source code. ARGLIST specifies the exact instantiation
# that is desired.
#
# Generically, this procedure returns a string of the form,
# "foo<parameter-list> (arg-list)", where ARGLIST describes the parameter(s).
#
# Valid specifiers for ARGLIST (must be kept in sync with source code):
#
# i: Creates an "int" type.
# a, b, c, d: Creates the struct template of the same name, taking a single
# template parameter.
# A, B: Creates the struct template of the same name, taking two template
# parameters.
# na, nb: Creates the non-template structs n::na and n::nb, respectively.
# NA, NB: Creates the struct templates n::NA and n::NB, respectively, which
# take two template parameters.
#
# Examples:
# makefoo i
# --> foo<int> (int)
# makefoo ii
# --> foo<int, int> (int, int)
# makefoo Aiabi
# --> foo<A<int, a<b<int> > > > (A<int, a<b<int> > >)
# makefoo NANAiaiNBbiabi
# --> foo<n::NA<n::NA<int, a<int> >, n::NB<b<int>, a<b<int> > > > >
# (n::NA<n::NA<int, a<int> >, n::NB<b<int>, a<b<int> > > >)
proc makefoo {arglist} {
set s [::Stack::new]
while {[string length $arglist] > 0} {
makearg $s arglist
}
maket $s "foo" all
set result [stack pop $s]
stack delete $s
return $result
}
# Test wrapper for a single "makefoo" unit test.
proc test_makefoo_1 {arglist expected} {
set exp "foo<$expected"
if {[string index $exp end] == ">"} {
append exp " "
}
append exp ">"
append exp "($expected)"
set calc [makefoo $arglist]
send_log "makefoo $arglist = $calc\n"
send_log "expecting: $exp\n"
if {[string equal $exp $calc]} {
pass "makefoo unit test: $arglist"
} else {
fail "makefoo unit test: $arglist"
}
}
# Test whether the procedure "makefoo" is functioning as expected.
proc test_makefoo {} {
test_makefoo_1 "i" "int"
test_makefoo_1 "ai" "a<int>"
test_makefoo_1 "aai" "a<a<int> >"
test_makefoo_1 "ii" "int, int"
test_makefoo_1 "aaibi" "a<a<int> >, b<int>"
test_makefoo_1 \
"ababiibababai" "a<b<a<b<int> > > >, int, b<a<b<a<b<a<int> > > > > >"
test_makefoo_1 "Aii" "A<int, int>"
test_makefoo_1 "ABaibibi" "A<B<a<int>, b<int> >, b<int> >"
test_makefoo_1 "na" "n::na"
test_makefoo_1 "nana" "n::na, n::na"
test_makefoo_1 "NAii" "n::NA<int, int>"
test_makefoo_1 "NANAiiNAii" "n::NA<n::NA<int, int>, n::NA<int, int> >"
}
#
# Tests start here.
#
# Disable the completion limit for the whole testcase. # Disable the completion limit for the whole testcase.
gdb_test_no_output "set max-completions unlimited" gdb_test_no_output "set max-completions unlimited"
@ -385,12 +597,11 @@ proc_with_prefix template-ret-type {} {
test_complete_prefix_range $complete_line $start test_complete_prefix_range $complete_line $start
} }
# Setting a breakpoint without the template params doesn't work. # Setting a breakpoint with or without template params and without
check_setting_bp_fails "$cmd_prefix template2_fn" # the method params works, just like with non-template functions.
# However, setting a breakpoint with template params and without # It also works with or without return type.
# the method params does work, just like with non-template
# functions. It also works with or without return type.
foreach linespec [list \ foreach linespec [list \
"template2_fn" \
"${method_name}" \ "${method_name}" \
"${method_name}${param_list}" \ "${method_name}${param_list}" \
"${struct_type}::${method_name}" \ "${struct_type}::${method_name}" \
@ -404,6 +615,218 @@ proc_with_prefix template-ret-type {} {
} }
} }
# Test completion of function template foo.
proc_with_prefix template-function-foo {} {
foreach cmd_prefix {"b" "b -function"} {
# "foo" is ambiguous, this will set many different breakpoints.
set completion_list \
[list \
[makefoo Aabiaai] \
[makefoo Aabiabi] \
[makefoo Aabicdi] \
[makefoo AabicdiAabiaai] \
[makefoo AabicdiAabiabi] \
[makefoo AabicdiBabicdi] \
[makefoo Babicdi] \
[makefoo aai] \
[makefoo aaiabi] \
[makefoo aaicci] \
[makefoo aaicdi] \
[makefoo abi] \
[makefoo anabnb] \
[makefoo cci] \
[makefoo cdi] \
[makefoo NAnanbNBnanb] \
[makefoo nanb]]
test_gdb_complete_multiple "$cmd_prefix " "foo" "<" $completion_list
check_bp_locations_match_list "$cmd_prefix foo" $completion_list
# "foo<" should give the same result, but it should not set any
# breakpoints.
test_gdb_complete_multiple "$cmd_prefix " "foo<" "" $completion_list
check_setting_bp_fails "$cmd_prefix foo<"
# "foo<A" should only give completions in COMPLETION_LIST that
# start with "A" but should set no breakpoints.
set completion_list \
[list \
[makefoo Aabiaai] \
[makefoo Aabiabi] \
[makefoo Aabicdi] \
[makefoo AabicdiAabiaai] \
[makefoo AabicdiAabiabi] \
[makefoo AabicdiBabicdi]]
test_gdb_complete_multiple "$cmd_prefix " "foo<A" "<a<b<int> >, " \
$completion_list
check_setting_bp_fails "$cmd_prefix foo<A"
# "foo<A>" should give any function with one parameter of any type
# of A. While the parameter list in the template should be ignored,
# the function's argument list should not be ignored.
set completion_list \
[list \
[makefoo Aabiaai] \
[makefoo Aabiabi] \
[makefoo Aabicdi]]
test_gdb_complete_multiple "$cmd_prefix " "foo<A>" \
"(A<a<b<int> >, " $completion_list
check_bp_locations_match_list "$cmd_prefix foo<A>" $completion_list
# "foo<A," should complete to any function with more than one
# parameter where the first parameter is any type of A. Insufficient
# location to set breakpoints.
set completion_list \
[list \
[makefoo AabicdiAabiaai] \
[makefoo AabicdiAabiabi] \
[makefoo AabicdiBabicdi]]
test_gdb_complete_multiple "$cmd_prefix " "foo<A," " " \
$completion_list
check_setting_bp_fails "$cmd_prefix foo<A,"
# "foo<A<a<b<int>, a" should give all completions starting with
# "Aabia" but it is insufficient to set breakpoints.
set completion_list \
[list \
[makefoo Aabiaai] \
[makefoo Aabiabi]]
test_gdb_complete_multiple "$cmd_prefix " "foo<A<a<b<int> >, a" \
"<" $completion_list
check_setting_bp_fails "$cmd_prefix foo<A<a<b<int> >, a"
# "foo<A<a<b<int>, a<" should yield the same results as above.
test_gdb_complete_multiple "$cmd_prefix " "foo<A<a<b<int> >, a<" \
"" $completion_list
check_setting_bp_fails "$cmd_prefix foo<A<a<b<int> >, a<"
# "foo<A<a<b<int>, a<a" is unique but insufficient to set a
# breakpoint. This has an ignored template parameter list, so
# the completion will contain an ignored list ("a<a>")
test_gdb_complete_unique "$cmd_prefix foo<A<a<b<int> >, a<a" \
"$cmd_prefix [makefoo Aabiaai]"
check_setting_bp_fails "$cmd_prefix foo<A<b<int> >, a<a"
# "foo<A<a<b<int>, a<b" is also unique. Same parameter ignoring
# happens here, too (except "a<b>").
test_gdb_complete_unique "$cmd_prefix foo<A<a<b<int> >, a<b" \
"$cmd_prefix [makefoo Aabiabi]"
check_setting_bp_fails "$cmd_prefix foo<A<a<b<int> >, a<b"
# "foo<B" is unique but insufficient to set a breakpoint.
test_gdb_complete_unique "$cmd_prefix foo<B" \
"$cmd_prefix [makefoo Babicdi]"
check_setting_bp_fails "$cmd_prefix foo<B"
# "foo<B>" yields the same unique result and sets a breakpoint.
# Since the input skips the parameter list, so does the completion.
test_gdb_complete_unique "$cmd_prefix foo<B>" \
"$cmd_prefix foo<B>(B<a<b<int> >, c<d<int> > >)"
check_bp_locations_match_list "$cmd_prefix foo<B>" \
[list [makefoo Babicdi]]
# "foo<B," should return no completions and no breakpoints.
test_gdb_complete_none "$cmd_prefix foo<B,"
check_setting_bp_fails "$cmd_prefix foo<B,"
# "foo<n::" should yield only the functions starting with
# "n" and "N" and no breakpoints.
set completion_list \
[list \
[makefoo NAnanbNBnanb] \
[makefoo nanb]]
test_gdb_complete_multiple "$cmd_prefix " "foo<n::" "" \
$completion_list
check_setting_bp_fails "$cmd_prefix foo<n::"
# "foo<A<a, c> >" is unique and sets a breakpoint.
# Multiple template parameter lists are skipped, so GDB will ignore
# them in the completion.
test_gdb_complete_unique "$cmd_prefix foo<A<a, c> >" \
"$cmd_prefix foo<A<a, c> >(A<a<b<int> >, c<d<int> > >)"
check_bp_locations_match_list "$cmd_prefix foo<A<a, c> >" \
[list [makefoo Aabicdi]]
}
}
# Helper for template-class-with-method to build completion lists.
proc makem {arglist_list} {
set completion_list {}
foreach arglist $arglist_list {
lappend completion_list "[maketype $arglist]::method()"
}
return $completion_list
}
# Returns a list of elements that look like
# void TYPE::method()
# where TYPE is derived from each arglist in ARGLIST_LIST.
proc test_makem_1 {arglist_list expected_list} {
set result [makem $arglist_list]
send_log "makem $arglist = $result\n"
send_log "expecting $expected_list\n"
# Do list equality via canonical strings.
if {[expr {[list {*}$expected_list] eq [list {*}$result]}]} {
pass "makem unit test: $arglist"
} else {
fail "makem unit test: $arglist"
}
}
# Unit tests for makem.
proc test_makem {} {
test_makem_1 ai {"a<int>::method()"}
test_makem_1 bi {"b<int>::method()"}
test_makem_1 {ai bi} {"a<int>::method()" "b<int>::method()"}
test_makem_1 {Aaiaai Bbibbi abi cdi} {
"A<a<int>, a<a<int> > >::method()"
"B<b<int>, b<b<int> > >::method()"
"a<b<int> >::method()"
"c<d<int> >::method()"
}
}
# Test class template containing a (non-templated) method called "method."
proc_with_prefix template-class-with-method {} {
foreach {type type_list} \
[list \
"" {aai abi cci cdi Aabicdi Aabiaai Aabiabi Babicdi} \
"a" {aai abi} \
"b" {} \
"c" {cci cdi} \
"A" {Aabicdi Aabiaai Aabiabi} \
"B" {Babicdi} \
"A<a, a>" {Aabiaai Aabiabi} \
"A<a<b>, c>" {Aabicdi}\
"A<a, d>" {} \
"B<a, a>" {} \
"B<a, b>" {} \
"B<a, c>" {Babicdi}] \
{
foreach cmd_prefix {"b" "b -function"} {
set c "$cmd_prefix "
if {$type != ""} {
append c "${type}::"
}
append c "method"
if {[llength $type_list] > 0} {
test_gdb_complete_unique $c "${c}()"
check_bp_locations_match_list $c [makem $type_list]
} else {
test_gdb_complete_none $c
}
}
}
}
# Test completion of a const-overloaded funtion (const-overload). # Test completion of a const-overloaded funtion (const-overload).
# Note that "const" appears after the function/method parameters. # Note that "const" appears after the function/method parameters.
@ -947,6 +1370,10 @@ proc test_driver {} {
overload-3 overload-3
template-overload template-overload
template-ret-type template-ret-type
#test_makefoo
template-function-foo
#test_makem
template-class-with-method
const-overload const-overload
const-overload-quoted const-overload-quoted
append-end-quote-char-when-unambiguous append-end-quote-char-when-unambiguous

View file

@ -248,20 +248,6 @@ proc test_operator_ambiguous {class_name opn cls} {
test_gdb_complete_multiple \ test_gdb_complete_multiple \
"$cmd_prefix " "$linespec_noparams" "" $location_list "$cmd_prefix " "$linespec_noparams" "" $location_list
# Setting the breakpoint doesn't create a breakpoint location
# for the template, because immediately after
# "operator()/operator[]" we have the template parameters, not
# the parameter list.
set location_list \
[list \
"${class_name}::operator${opn}${cls}(int)" \
"${class_name}::operator${opn}${cls}(long)"]
if {$opn == "("} {
set location_list \
[concat \
[list "${class_name}::operator${opn}${cls}()"] \
$location_list]
}
check_bp_locations_match_list "$cmd_prefix $linespec_noparams" \ check_bp_locations_match_list "$cmd_prefix $linespec_noparams" \
$location_list $location_list
check_bp_locations_match_list "$cmd_prefix $linespec_noparams<int>" \ check_bp_locations_match_list "$cmd_prefix $linespec_noparams<int>" \
@ -269,26 +255,33 @@ proc test_operator_ambiguous {class_name opn cls} {
# Test the template version. Test both with and without # Test the template version. Test both with and without
# return type. # return type.
set f "${class_name}::operator"
foreach ws1 {"" " "} {
foreach ws2 {"" " "} {
foreach ws3 {"" " "} {
test_gdb_complete_unique \ test_gdb_complete_unique \
"$cmd_prefix ${class_name}::operator${opn}${cls}<int>(in" \ "$cmd_prefix ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(in" \
"$cmd_prefix ${class_name}::operator${opn}${cls}<int>(int*)" "$cmd_prefix ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)"
check_bp_locations_match_list \ check_bp_locations_match_list \
"$cmd_prefix ${class_name}::operator${opn}${cls}<int>(int*)" \ "$cmd_prefix ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)" \
[list "${class_name}::operator${opn}${cls}<int>(int*)"] [list "${f}${opn}${cls}<int>(int*)"]
test_gdb_complete_unique \ test_gdb_complete_unique \
"$cmd_prefix void ${class_name}::operator${opn}${cls}<int>(in" \ "$cmd_prefix void ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(in" \
"$cmd_prefix void ${class_name}::operator${opn}${cls}<int>(int*)" "$cmd_prefix void ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)"
check_bp_locations_match_list \ check_bp_locations_match_list \
"$cmd_prefix void ${class_name}::operator${opn}${cls}<int>(int*)" \ "$cmd_prefix void ${f}${opn}${ws1}${cls}<${ws2}int${ws3}>(int*)" \
[list "${class_name}::operator${opn}${cls}<int>(int*)"] [list "${f}${opn}${cls}<int>(int*)"]
}
}
}
# Add extra spaces. # Add extra spaces.
test_gdb_complete_unique \ test_gdb_complete_unique \
"$cmd_prefix ${class_name}::operator ${opn} ${cls} ( in" \ "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( lo" \
"$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int)" "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( long)"
check_bp_locations_match_list \ check_bp_locations_match_list \
"$cmd_prefix ${class_name}::operator ${opn} ${cls} ( int )" \ "$cmd_prefix ${class_name}::operator ${opn} ${cls} ( long )" \
[list "${class_name}::operator${opn}${cls}(int)"] [list "${class_name}::operator${opn}${cls}(long)"]
} }
} }

View file

@ -146,7 +146,7 @@ namespace ns_overload3_test
} }
} }
/* Code for the template-overload tests. */ /* Code for the template-function_foo (template parameter completion) tests. */
template <typename T> template <typename T>
struct template_struct struct template_struct
@ -163,6 +163,113 @@ T template_struct<T>::template_overload_fn (T t)
template_struct<int> template_struct_int; template_struct<int> template_struct_int;
template_struct<long> template_struct_long; template_struct<long> template_struct_long;
/* Code for the template-parameter-overload test. */
template <typename T>
void foo (T c) {}
template <typename T1, typename T2>
void foo (T1 a, T2 b) {}
template <typename T>
struct a
{
void method () {}
};
template <typename T>
struct b
{
void method () {}
};
template <typename T>
struct c
{
void method () {}
};
template <typename T>
struct d
{
void method () {};
};
template <typename T1, typename T2>
struct A
{
void method () {}
};
template <typename T1, typename T2>
struct B
{
void method () {}
};
namespace n
{
struct na {};
struct nb {};
template <typename T1, typename T2>
struct NA {};
template <typename T1, typename T2>
struct NB {};
};
static void
template_function_foo ()
{
a<a<int>> aa;
aa.method ();
a<b<int>> ab;
ab.method ();
c<c<int>> cc;
cc.method ();
c<d<int>> cd;
cd.method ();
foo (aa);
foo (ab);
foo (cc);
foo (cd);
foo (aa, ab);
foo (aa, cc);
foo (aa, cd);
A<a<b<int>>, c<d<int>>> Aabcd;
Aabcd.method ();
foo (Aabcd);
A<a<b<int>>, a<a<int>>> Aabaa;
Aabaa.method ();
foo (Aabaa);
A<a<b<int>>, a<b<int>>> Aabab;
Aabab.method ();
foo (Aabab);
B<a<b<int>>, c<d<int>>> Babcd;
Babcd.method ();
foo (Babcd);
foo (Aabcd, Babcd);
foo (Aabcd, Aabaa);
foo (Aabcd, Aabab);
n::na na;
n::nb nb;
foo (na, nb);
a<n::na> ana;
b<n::nb> bnb;
foo (ana, bnb);
n::NA<n::na, n::nb> NAnanb;
n::NB<n::na, n::nb> Nbnanb;
foo (NAnanb, Nbnanb);
}
/* Code for the template2-ret-type tests. */ /* Code for the template2-ret-type tests. */
template <typename T> template <typename T>
@ -381,6 +488,7 @@ main ()
template2_struct_inst.template2_fn<int, int> (); template2_struct_inst.template2_fn<int, int> ();
template_struct_int.template_overload_fn(0); template_struct_int.template_overload_fn(0);
template_struct_long.template_overload_fn(0); template_struct_long.template_overload_fn(0);
template_function_foo ();
return 0; return 0;
} }

View file

@ -2252,13 +2252,45 @@ skip_abi_tag (const char **name)
return false; return false;
} }
/* If *NAME points at a template parameter list, skip it and return true.
Otherwise do nothing and return false. */
static bool
skip_template_parameter_list (const char **name)
{
const char *p = *name;
if (*p == '<')
{
const char *template_param_list_end = find_toplevel_char (p + 1, '>');
if (template_param_list_end == NULL)
return false;
p = template_param_list_end + 1;
/* Skip any whitespace that might occur after the closing of the
parameter list, but only if it is the end of parameter list. */
const char *q = p;
while (ISSPACE (*q))
++q;
if (*q == '>')
p = q;
*name = p;
return true;
}
return false;
}
/* See utils.h. */ /* See utils.h. */
int int
strncmp_iw_with_mode (const char *string1, const char *string2, strncmp_iw_with_mode (const char *string1, const char *string2,
size_t string2_len, strncmp_iw_mode mode, size_t string2_len, strncmp_iw_mode mode,
enum language language, enum language language,
completion_match_for_lcd *match_for_lcd) completion_match_for_lcd *match_for_lcd,
bool ignore_template_params)
{ {
const char *string1_start = string1; const char *string1_start = string1;
const char *end_str2 = string2 + string2_len; const char *end_str2 = string2 + string2_len;
@ -2308,6 +2340,48 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
string1++; string1++;
} }
/* Skip template parameters in STRING1 if STRING2 does not contain
any. E.g.:
Case 1: User is looking for all functions named "foo".
string1: foo <...> (...)
string2: foo
Case 2: User is looking for all methods named "foo" in all template
class instantiations.
string1: Foo<...>::foo <...> (...)
string2: Foo::foo (...)
Case 3: User is looking for a specific overload of a template
function or method.
string1: foo<...>
string2: foo(...)
Case 4: User is looking for a specific overload of a specific
template instantiation.
string1: foo<A> (...)
string2: foo<B> (...)
Case 5: User is looking wild parameter match.
string1: foo<A<a<b<...> > > > (...)
string2: foo<A
*/
if (language == language_cplus && ignore_template_params
&& *string1 == '<' && *string2 != '<')
{
/* Skip any parameter list in STRING1. */
const char *template_start = string1;
if (skip_template_parameter_list (&string1))
{
/* Don't mark the parameter list ignored if the user didn't
try to ignore it. [Case #5 above] */
if (*string2 != '\0'
&& match_for_lcd != NULL && template_start != string1)
match_for_lcd->mark_ignored_range (template_start, string1);
}
}
if (*string1 == '\0' || string2 == end_str2) if (*string1 == '\0' || string2 == end_str2)
break; break;
@ -2416,6 +2490,12 @@ strncmp_iw_with_mode (const char *string1, const char *string2,
break; break;
if (*string1 == '(' || *string2 == '(') if (*string1 == '(' || *string2 == '(')
break; break;
/* If STRING1 or STRING2 starts with a template
parameter list, break out of operator processing. */
skip_ws (string1, string2, end_str2);
if (*string1 == '<' || *string2 == '<')
break;
} }
continue; continue;

View file

@ -63,11 +63,14 @@ enum class strncmp_iw_mode
MATCH_FOR_LCD is passed down so that the function can mark parts of 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., the symbol name as ignored for completion matching purposes (e.g.,
to handle abi tags). */ to handle abi tags). If IGNORE_TEMPLATE_PARAMS is true, all template
parameter lists will be ignored when language is C++. */
extern int strncmp_iw_with_mode extern int strncmp_iw_with_mode
(const char *string1, const char *string2, size_t string2_len, (const char *string1, const char *string2, size_t string2_len,
strncmp_iw_mode mode, enum language language, strncmp_iw_mode mode, enum language language,
completion_match_for_lcd *match_for_lcd = NULL); completion_match_for_lcd *match_for_lcd = NULL,
bool ignore_template_params = false);
/* Do a strncmp() type operation on STRING1 and STRING2, ignoring any /* Do a strncmp() type operation on STRING1 and STRING2, ignoring any
differences in whitespace. STRING2_LEN is STRING2's length. differences in whitespace. STRING2_LEN is STRING2's length.