# Copyright 2017-2024 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
load_lib data-structures.exp
standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc
set opts {}
lappend opts debug
lappend opts additional_flags=-std=c++11
if {[prepare_for_testing "failed to prepare" $testfile \
[list $srcfile $srcfile2 $srcfile3] $opts]} {
return -1
}
# Tests below are about tab-completion, which doesn't work if readline
# library isn't used. Check it first.
if { ![readline_is_used] } {
untested "no tab completion support without readline"
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 (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)
# makefoo ii
# --> foo (int, int)
# makefoo Aiabi
# --> foo > > > (A > >)
# makefoo NANAiaiNBbiabi
# --> foo >, n::NB, a > > > >
# (n::NA >, n::NB, a > > >)
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"
test_makefoo_1 "aai" "a >"
test_makefoo_1 "ii" "int, int"
test_makefoo_1 "aaibi" "a >, b"
test_makefoo_1 \
"ababiibababai" "a > > >, int, b > > > > >"
test_makefoo_1 "Aii" "A"
test_makefoo_1 "ABaibibi" "A, b >, b >"
test_makefoo_1 "na" "n::na"
test_makefoo_1 "nana" "n::na, n::na"
test_makefoo_1 "NAii" "n::NA"
test_makefoo_1 "NANAiiNAii" "n::NA, n::NA >"
}
#
# Tests start here.
#
# Disable the completion limit for the whole testcase.
gdb_test_no_output "set max-completions unlimited"
# Start of tests.
# Test completion of all parameter prefixes, crossing "(" and ")",
# with and without whitespace.
proc_with_prefix all-param-prefixes {} {
# Test both linespecs and explicit locations.
foreach cmd_prefix {"b" "b -function"} {
set line "$cmd_prefix param_prefixes_test_long(long)"
set start [index_after "test_long" $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.
test_gdb_complete_unique \
"$cmd_prefix param_prefixes_test_long(long " \
"$cmd_prefix param_prefixes_test_long(long )"
test_gdb_complete_unique \
"$cmd_prefix param_prefixes_test_long( long " \
"$cmd_prefix param_prefixes_test_long( long )"
test_gdb_complete_unique \
"$cmd_prefix param_prefixes_test_long ( long " \
"$cmd_prefix param_prefixes_test_long ( long )"
# Complete all parameter prefixes between "(i" and "(int*, int&)".
# Note that this exercises completing when the point is at the
# space in "param_prefixes_test_intp_intr(int*, ".
set line "$cmd_prefix param_prefixes_test_intp_intr(int*, int&)"
set start [index_after "intp_intr" $line]
test_complete_prefix_range $line $start
# Similar, but with extra spaces.
test_gdb_complete_unique \
"$cmd_prefix param_prefixes_test_intp_intr ( int* " \
"$cmd_prefix param_prefixes_test_intp_intr ( int* , int&)"
test_gdb_complete_unique \
"$cmd_prefix param_prefixes_test_intp_intr ( int *" \
"$cmd_prefix param_prefixes_test_intp_intr ( int *, int&)"
test_gdb_complete_unique \
"$cmd_prefix param_prefixes_test_intp_intr ( int *, int " \
"$cmd_prefix param_prefixes_test_intp_intr ( int *, int &)"
test_gdb_complete_unique \
"$cmd_prefix param_prefixes_test_intp_intr ( int *, int & " \
"$cmd_prefix param_prefixes_test_intp_intr ( int *, int & )"
}
}
# Test completion of an overloaded function.
proc_with_prefix overload {} {
set completion_list {
"overload_ambiguous_test(int, int)"
"overload_ambiguous_test(int, long)"
"overload_ambiguous_test(long)"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple \
"$cmd_prefix " "overload_ambiguous_" "test(" \
$completion_list
check_bp_locations_match_list \
"$cmd_prefix overload_ambiguous_test" \
$completion_list
# Test disambiguating by typing enough to pick the "int" as
# first parameter type. This then tests handling ambiguity in
# the second parameter, which checks that tab completion when
# the point is at the whitespace behaves naturally, by showing
# the remaining matching overloads to the user.
test_gdb_complete_multiple \
"$cmd_prefix " "overload_ambiguous_test(i" "nt, " {
"overload_ambiguous_test(int, int)"
"overload_ambiguous_test(int, long)"
}
# Add a few more characters to make the completion
# unambiguous.
test_gdb_complete_unique \
"$cmd_prefix overload_ambiguous_test(int, i" \
"$cmd_prefix overload_ambiguous_test(int, int)"
check_bp_locations_match_list \
"$cmd_prefix overload_ambiguous_test(int, int)" {
"overload_ambiguous_test(int, int)"
}
}
}
# Test completion of a function that is defined in different scopes
# with different parameters.
proc_with_prefix overload-2 {} {
with_test_prefix "all" {
set completion_list {
"(anonymous namespace)::overload2_function(overload2_arg3)"
"(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg4)"
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
"ns_overload2_test::overload2_function(overload2_arg5)"
"ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"
"overload2_function(overload2_arg1)"
"struct_overload2_test::overload2_function(overload2_arg2)"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple \
"$cmd_prefix " "overload2_func" "tion(overload2_arg" $completion_list
check_bp_locations_match_list \
"$cmd_prefix overload2_function" $completion_list
}
}
# Same, but restrict to functions/methods in some scope.
with_test_prefix "restrict scope" {
set completion_list {
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
"ns_overload2_test::overload2_function(overload2_arg5)"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple \
"$cmd_prefix " "ns_overload2_test::overload2_func" "tion(overload2_arg" $completion_list
check_bp_locations_match_list \
"$cmd_prefix ns_overload2_test::overload2_function" $completion_list
}
}
# Restrict to anonymous namespace scopes.
with_test_prefix "restrict scope 2" {
set completion_list {
"(anonymous namespace)::overload2_function(overload2_arg3)"
"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple \
"$cmd_prefix " "(anonymous namespace)::overload2_func" "tion(overload2_arg" $completion_list
check_bp_locations_match_list \
"$cmd_prefix (anonymous namespace)::overload2_function" $completion_list
}
}
# Add enough scopes, and we get a unique completion.
with_test_prefix "unique completion" {
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_unique \
"$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_func" \
"$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
check_setting_bp_fails "$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_func"
check_bp_locations_match_list \
"$cmd_prefix ns_overload2_test::(anonymous namespace)::overload2_function" \
{"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"}
}
}
}
# Test linespecs / locations using fully-qualified names.
proc_with_prefix fqn {} {
# "-qualified" works with both explicit locations and linespecs.
# Also test that combining a source file with a function name
# still results in a full match, with both linespecs and explicit
# locations.
foreach cmd_prefix {
"b -qualified "
"b -qualified -function "
"b -qualified cpls.cc:"
"b -qualified -source cpls.cc -function "
"b -source cpls.cc -qualified -function "
} {
test_gdb_complete_unique \
"${cmd_prefix}overload2_func" \
"${cmd_prefix}overload2_function(overload2_arg1)"
# Drill down until we find a unique completion.
test_gdb_complete_multiple "${cmd_prefix}" "ns_overload2_test::" "" {
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
"ns_overload2_test::overload2_function(overload2_arg5)"
"ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"
}
test_gdb_complete_multiple "${cmd_prefix}" "ns_overload2_test::(anonymous namespace)::" "" {
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
}
test_gdb_complete_multiple "${cmd_prefix}" "ns_overload2_test::(anonymous namespace)::ns_overload2_test::" "" {
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
}
test_gdb_complete_unique \
"${cmd_prefix}ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_func" \
"${cmd_prefix}ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
}
}
# Check that a fully-qualified lookup name doesn't match symbols in
# nested scopes.
proc_with_prefix fqn-2 {} {
set linespec "struct_overload2_test::overload2_function(overload2_arg6)"
set cmd_prefix "b -qualified"
check_setting_bp_fails "$cmd_prefix $linespec"
test_gdb_complete_none "$cmd_prefix $linespec"
# Check that using the same name, but not fully-qualifying it,
# would find something, just to make sure the test above is
# testing what we intend to test.
set cmd_prefix "b -function"
test_gdb_complete_unique "$cmd_prefix $linespec" "$cmd_prefix $linespec"
check_bp_locations_match_list \
"$cmd_prefix $linespec" \
{"ns_overload2_test::struct_overload2_test::overload2_function(overload2_arg6)"}
}
# Test completion of functions in different scopes that have the same
# name and parameters. Restricting the scopes should find fewer and
# fewer matches.
proc_with_prefix overload-3 {} {
with_test_prefix "all overloads" {
set completion_list {
"(anonymous namespace)::overload3_function(int)"
"(anonymous namespace)::overload3_function(long)"
"(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"(anonymous namespace)::struct_overload3_test::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(long)"
"ns_overload3_test::overload3_function(int)"
"ns_overload3_test::overload3_function(long)"
"ns_overload3_test::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::struct_overload3_test::overload3_function(long)"
"overload3_function(int)"
"overload3_function(long)"
"struct_overload3_test::overload3_function(int)"
"struct_overload3_test::overload3_function(long)"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple "$cmd_prefix " "overload3_func" "tion(" $completion_list
check_bp_locations_match_list "$cmd_prefix overload3_function" $completion_list
}
}
with_test_prefix "restrict overload" {
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_unique \
"$cmd_prefix overload3_function(int)" \
"$cmd_prefix overload3_function(int)"
check_bp_locations_match_list "$cmd_prefix overload3_function(int)" {
"(anonymous namespace)::overload3_function(int)"
"(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::overload3_function(int)"
"ns_overload3_test::struct_overload3_test::overload3_function(int)"
"overload3_function(int)"
"struct_overload3_test::overload3_function(int)"
}
}
}
with_test_prefix "restrict scope" {
set completion_list {
"(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"(anonymous namespace)::struct_overload3_test::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(long)"
"ns_overload3_test::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::struct_overload3_test::overload3_function(long)"
"struct_overload3_test::overload3_function(int)"
"struct_overload3_test::overload3_function(long)"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple \
"$cmd_prefix " "struct_overload3_test::overload3_func" "tion(" \
$completion_list
check_bp_locations_match_list \
"$cmd_prefix struct_overload3_test::overload3_function" \
$completion_list
}
}
}
# Test completing an overloaded template method.
proc_with_prefix template-overload {} {
set completion_list {
"template_struct::template_overload_fn(int)"
"template_struct::template_overload_fn(long)"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple "$cmd_prefix " "template_overload_fn" "(" $completion_list
check_bp_locations_match_list "$cmd_prefix template_overload_fn" $completion_list
check_bp_locations_match_list \
"$cmd_prefix template_struct::template_overload_fn" \
"template_struct::template_overload_fn(int)"
}
}
# Test completing template methods with non-void return type.
proc_with_prefix template-ret-type {} {
set method_name "template2_fn"
set param_list "(template2_ret_type, int, int)"
set struct_type "template2_struct >"
set ret_type "template2_ret_type"
# Templates are listed both with and without return type, making
# "template2_" ambiguous.
foreach cmd_prefix {"b" "b -function"} {
set completion_list \
[list \
"${ret_type} ${struct_type}::${method_name}${param_list}" \
"${struct_type}::${method_name}${param_list}"]
test_gdb_complete_multiple "$cmd_prefix " "template2_" "" $completion_list
# Add one character more after "2_", and the linespec becomes
# unambiguous. Test completing the whole prefix range after that,
# thus testing completing either with or without return type.
foreach {s t} [list \
"template2_r" \
"${ret_type} ${struct_type}::${method_name}${param_list}" \
"template2_s" \
"${struct_type}::${method_name}${param_list}"] {
set linespec $t
set complete_line "$cmd_prefix $linespec"
set start [index_after $s $complete_line]
test_complete_prefix_range $complete_line $start
}
# Setting a breakpoint with or without template params and without
# the method params works, just like with non-template functions.
# It also works with or without return type.
foreach linespec [list \
"template2_fn" \
"${method_name}" \
"${method_name}${param_list}" \
"${struct_type}::${method_name}" \
"${struct_type}::${method_name}${param_list}" \
"${ret_type} ${struct_type}::${method_name}" \
"${ret_type} ${struct_type}::${method_name}${param_list}"] {
check_bp_locations_match_list \
"$cmd_prefix $linespec" \
[list "${struct_type}::${method_name}${param_list}"]
}
}
}
# 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 >, " \
$completion_list
check_setting_bp_fails "$cmd_prefix foo" 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 >, " $completion_list
check_bp_locations_match_list "$cmd_prefix foo" $completion_list
# "foo, 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" \
"<" $completion_list
check_setting_bp_fails "$cmd_prefix foo >, a"
# "foo, a<" should yield the same results as above.
test_gdb_complete_multiple "$cmd_prefix " "foo >, a<" \
"" $completion_list
check_setting_bp_fails "$cmd_prefix foo >, a<"
# "foo, a")
test_gdb_complete_unique "$cmd_prefix foo >, a >, a, a").
test_gdb_complete_unique "$cmd_prefix foo >, a >, a" 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" \
"$cmd_prefix foo(B >, c > >)"
check_bp_locations_match_list "$cmd_prefix foo" \
[list [makefoo Babicdi]]
# "foo >" 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 >" \
"$cmd_prefix foo >(A >, c > >)"
check_bp_locations_match_list "$cmd_prefix foo >" \
[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::method()"}
test_makem_1 bi {"b::method()"}
test_makem_1 {ai bi} {"a::method()" "b::method()"}
test_makem_1 {Aaiaai Bbibbi abi cdi} {
"A, a > >::method()"
"B, b > >::method()"
"a >::method()"
"c >::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" {Aabiaai Aabiabi} \
"A, c>" {Aabicdi}\
"A" {} \
"B" {} \
"B" {} \
"B" {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).
# Note that "const" appears after the function/method parameters.
proc_with_prefix const-overload {} {
set completion_list {
"struct_with_const_overload::const_overload_fn()"
"struct_with_const_overload::const_overload_fn() const"
}
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple \
"$cmd_prefix " "const_overload_fn" "()" \
$completion_list
test_gdb_complete_multiple \
"$cmd_prefix " "const_overload_fn ( " ")" \
$completion_list
test_gdb_complete_multiple \
"$cmd_prefix " "const_overload_fn()" "" \
$completion_list
check_bp_locations_match_list \
"$cmd_prefix const_overload_fn" \
{"struct_with_const_overload::const_overload_fn()"
"struct_with_const_overload::const_overload_fn() const"}
check_setting_bp_fails "$cmd_prefix const_overload_fn("
check_bp_locations_match_list \
"$cmd_prefix const_overload_fn()" \
{"struct_with_const_overload::const_overload_fn()"}
check_bp_locations_match_list \
"$cmd_prefix const_overload_fn() const" \
{"struct_with_const_overload::const_overload_fn() const"}
}
}
# Same but quote-enclose the function name. This makes the overload
# no longer be ambiguous.
proc_with_prefix const-overload-quoted {} {
foreach cmd_prefix {"b" "b -function"} {
set linespec "'const_overload_fn()'"
test_gdb_complete_unique "$cmd_prefix $linespec" "$cmd_prefix $linespec"
check_bp_locations_match_list \
"$cmd_prefix $linespec" {
"struct_with_const_overload::const_overload_fn()"
}
set linespec "'const_overload_fn() const'"
test_gdb_complete_unique "$cmd_prefix $linespec" "$cmd_prefix $linespec"
check_bp_locations_match_list \
"$cmd_prefix $linespec" {
"struct_with_const_overload::const_overload_fn() const"
}
}
}
# Test that when the function is unambiguous, linespec completion
# appends the end quote char automatically, both ' and ".
proc_with_prefix append-end-quote-char-when-unambiguous {} {
foreach cmd_prefix {"b" "b -function"} {
foreach qc $completion::all_quotes_list {
set linespec "${qc}not_overloaded_fn()${qc}"
foreach cmd [list "$cmd_prefix ${qc}not_overloaded_fn()" \
"$cmd_prefix ${qc}not_overloaded_fn" \
"$cmd_prefix ${qc}not_overloaded_"] {
test_gdb_complete_unique $cmd "$cmd_prefix $linespec"
}
check_bp_locations_match_list \
"$cmd_prefix $linespec" {"not_overloaded_fn()"}
}
}
}
# Test completing symbols of source files.
proc_with_prefix in-source-file-unconstrained {} {
# First test that unconstrained matching picks up functions from
# multiple files.
test_gdb_complete_multiple "b " "file_constrained_test" "_cpls" {
"file_constrained_test_cpls2_function(int)"
"file_constrained_test_cpls_function(int)"
}
check_setting_bp_fails "b file_constrained_test_cpls"
}
# Test an unambiguous completion that would be ambiguous if it weren't
# for the source file component, due to
# "file_constrained_test_cpls_function" in cpls.cc. Test with
# different components quoted, and with whitespace before the function
# name.
proc_with_prefix in-source-file-unambiguous {} {
foreach sqc $completion::maybe_quoted_list {
foreach fqc $completion::maybe_quoted_list {
# Linespec.
foreach sep {":" ": "} {
set linespec \
"${sqc}cpls2.cc${sqc}${sep}${fqc}file_constrained_test_cpls2_function(int)${fqc}"
set complete_line "b $linespec"
set start [index_after "constrained_test" $complete_line]
set input_line [string range $complete_line 0 $start]
test_gdb_complete_unique $input_line ${complete_line}
check_bp_locations_match_list "b $linespec" {
"file_constrained_test_cpls2_function(int)"
}
}
# Explicit location.
set source_opt "-source ${sqc}cpls2.cc${sqc}"
set function_opt "-function ${fqc}file_constrained_test_cpls2_function(int)${fqc}"
set complete_line "b $source_opt $function_opt"
set start [index_after "cpls2_functio" $complete_line]
set input_line [string range $complete_line 0 $start]
test_gdb_complete_unique $input_line ${complete_line}
check_bp_locations_match_list "$complete_line" {
"file_constrained_test_cpls2_function(int)"
}
}
}
}
# Test an ambiguous completion constrained by a source file. Test
# with different components quoted, and with whitespace before the
# function name.
proc_with_prefix in-source-file-ambiguous {} {
foreach sqc $completion::maybe_quoted_list {
foreach fqc $completion::maybe_quoted_list {
# Linespec.
foreach sep {":" ": "} {
set cmd_prefix "b ${sqc}cpls2.cc${sqc}${sep}"
test_gdb_complete_multiple "${cmd_prefix}" ${fqc} "" {
"another_file_constrained_test_cpls2_function(int)"
"file_constrained_test_cpls2_function(int)"
} ${fqc} ${fqc}
}
# Explicit location.
test_gdb_complete_multiple \
"b -source ${sqc}cpls2.cc${sqc} -function " ${fqc} "" {
"another_file_constrained_test_cpls2_function(int)"
"file_constrained_test_cpls2_function(int)"
} ${fqc} ${fqc}
}
}
}
# Check that completing a file name in a linespec auto-appends a colon
# instead of a whitespace character.
proc_with_prefix source-complete-appends-colon {} {
# Test with quotes to make sure the end quote char is put at the
# right place.
foreach qc $completion::maybe_quoted_list {
test_gdb_complete_unique \
"b ${qc}cpls2." \
"b ${qc}cpls2.cc${qc}" ":"
test_gdb_complete_unique \
"b ${qc}cpls2.c" \
"b ${qc}cpls2.cc${qc}" ":"
test_gdb_complete_unique \
"b ${qc}cpls2.cc" \
"b ${qc}cpls2.cc${qc}" ":"
# Same, but with a filename with an hyphen (which is normally
# a language word break char).
test_gdb_complete_unique \
"b ${qc}cpls-" \
"b ${qc}cpls-hyphen.cc${qc}" ":"
test_gdb_complete_unique \
"b ${qc}cpls-hyphen" \
"b ${qc}cpls-hyphen.cc${qc}" ":"
}
# Test the same, but with the name of a nonexisting file.
# Cursor at the end of the string.
test_gdb_complete_none "b nonexistingfilename.cc"
# Cursor past the end of the string.
test_gdb_complete_multiple "b nonexistingfilename.cc " "" "" \
$completion::keyword_list
foreach qc $completion::all_quotes_list {
# Unterminated quote.
test_gdb_complete_none "b ${qc}nonexistingfilename.cc"
test_gdb_complete_none "b ${qc}nonexistingfilename.cc "
# Terminated quote, cursor at the quote.
test_gdb_complete_unique \
"b ${qc}nonexistingfilename.cc${qc}" \
"b ${qc}nonexistingfilename.cc${qc}"
# Terminated quote, cursor past the quote.
test_gdb_complete_multiple \
"b ${qc}nonexistingfilename.cc${qc} " "" "" \
$completion::keyword_list
}
}
####################################################################
# Test that a colon at the end of the linespec is understood as an
# incomplete scope operator (incomplete-scope-colon), instead of a
# source/function separator.
proc_with_prefix incomplete-scope-colon {} {
# Helper for the loop below to simplify it. Tests completion of
# the range defined by the RANGE_SS found in the constructed line.
#
# E.g., with:
#
# source="source.cc"
# fqc="'"
# prototype="ns::function()"
# range_ss="s::f"
#
# we'd try completing with the cursor set in each of the
# underlined range's positions of:
#
# b source.cc:'ns::function()'"
# ^^^^
#
# Also test that setting a breakpoint at the constructed line
# finds the same breakpoint location as completion does.
#
proc incomplete_scope_colon_helper {prototype range_ss {skip_check_bp 0}} {
foreach source {"" "cpls.cc"} {
# Test with and without source quoting.
foreach sqc $completion::maybe_quoted_list {
if {$source == "" && $sqc != ""} {
# Invalid combination.
continue
}
# Test with and without function quoting.
foreach fqc $completion::maybe_quoted_list {
if {$source == ""} {
set linespec_source ""
set explicit_source ""
} else {
set linespec_source "${sqc}${source}${sqc}:"
set explicit_source "-source ${sqc}${source}${sqc}"
}
# Even though this use case is trickier with
# linespecs due to the ":" as separator, test both
# linespecs and explicit locations for
# completeness.
foreach location [list \
"${linespec_source}${fqc}$prototype${fqc}" \
"${explicit_source} -function ${fqc}$prototype${fqc}"] {
set complete_line "b $location"
set start [string first $range_ss $complete_line]
set end [expr ($start + [string length $range_ss])]
test_complete_prefix_range $complete_line $start $end
if {!$skip_check_bp} {
check_bp_locations_match_list "b $location" [list "$prototype"]
}
}
}
}
}
}
incomplete_scope_colon_helper \
"struct_incomplete_scope_colon_test::incomplete_scope_colon_test()" \
"t::i"
incomplete_scope_colon_helper \
"ns_incomplete_scope_colon_test::incomplete_scope_colon_test()" \
"t::i"
# Test completing around both "::"s.
foreach range_ss {"t::s" "t::i"} skip_check_bp {0 1} {
incomplete_scope_colon_helper \
"ns2_incomplete_scope_colon_test::struct_in_ns2_incomplete_scope_colon_test::incomplete_scope_colon_test()" \
$range_ss $skip_check_bp
}
}
# Test completing functions/methods in anonymous namespaces.
proc_with_prefix anon-ns {} {
foreach cmd_prefix {"b" "b -function"} {
foreach qc $completion::maybe_quoted_list {
test_gdb_complete_unique \
"$cmd_prefix ${qc}anon_ns_function" \
"$cmd_prefix ${qc}anon_ns_function()${qc}"
check_bp_locations_match_list "$cmd_prefix ${qc}anon_ns_function()${qc}" {
"(anonymous namespace)::anon_ns_function()"
"(anonymous namespace)::anon_ns_struct::anon_ns_function()"
"the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_function()"
"the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_struct::anon_ns_function()"
}
}
# A "(" finds all anonymous namespace functions/methods in all
# scopes.
test_gdb_complete_multiple "$cmd_prefix " "(" "anonymous namespace)::" {
"(anonymous namespace)::anon_ns_function()"
"(anonymous namespace)::anon_ns_struct::anon_ns_function()"
"(anonymous namespace)::overload2_function(overload2_arg3)"
"(anonymous namespace)::overload3_function(int)"
"(anonymous namespace)::overload3_function(long)"
"(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg4)"
"(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"(anonymous namespace)::struct_overload3_test::overload3_function(long)"
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::overload2_function(overload2_arg9)"
"ns_overload2_test::(anonymous namespace)::ns_overload2_test::struct_overload2_test::overload2_function(overload2_arga)"
"ns_overload2_test::(anonymous namespace)::overload2_function(overload2_arg7)"
"ns_overload2_test::(anonymous namespace)::struct_overload2_test::overload2_function(overload2_arg8)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::ns_overload3_test::struct_overload3_test::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::overload3_function(long)"
"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(int)"
"ns_overload3_test::(anonymous namespace)::struct_overload3_test::overload3_function(long)"
"the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_function()"
"the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_struct::anon_ns_function()"
}
set function "the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_function()"
test_gdb_complete_unique "$cmd_prefix $function" "$cmd_prefix $function"
check_bp_locations_match_list "$cmd_prefix $function" [list $function]
# Test completing after the "(anonymous namespace)" part.
test_gdb_complete_unique \
"$cmd_prefix the_anon_ns_wrapper_ns::(anonymous namespace)::anon_ns_fu" \
"$cmd_prefix $function"
# Test whitespace in the "(anonymous namespace)" component.
test_gdb_complete_unique \
"$cmd_prefix the_anon_ns_wrapper_ns::( anonymous namespace )::anon_ns_fu" \
"$cmd_prefix the_anon_ns_wrapper_ns::( anonymous namespace )::anon_ns_function()"
check_setting_bp_fails \
"$cmd_prefix the_anon_ns_wrapper_ns::( anonymous namespace )::anon_ns_fu"
set function_ws \
"the_anon_ns_wrapper_ns::( anonymous namespace )::anon_ns_function ( )"
test_gdb_complete_unique "$cmd_prefix $function_ws" "$cmd_prefix $function_ws"
check_bp_locations_match_list "$cmd_prefix $function_ws" [list $function]
}
}
# Basic test for completing "operator<". More extensive C++ operator
# tests in cpls-op.exp.
proc_with_prefix operator< {} {
# Complete all prefixes between "oper" and the whole prototype.
set function "operator<(foo_enum, foo_enum)"
foreach cmd_prefix {"b" "b -function"} {
set line "$cmd_prefix $function"
set start [index_after "oper" $line]
test_complete_prefix_range $line $start
}
# There's a label in the function; try completing it. (Exhaustive
# label completion tests further below.)
foreach location [list \
"$function:label1" \
"-function $function -label label1"] {
set cmd "b $location"
set input_line [string range $cmd 0 [expr [string length $cmd] - 3]]
test_gdb_complete_unique $input_line $cmd
test_gdb_complete_unique $cmd $cmd
check_bp_locations_match_list $cmd [list "$location"]
}
}
# Test completion of scopes with an ambiguous prefix.
proc_with_prefix ambiguous-prefix {} {
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple "$cmd_prefix " "ambiguous_pre" "fix_" {
"ambiguous_prefix_global_func()"
"the_ambiguous_prefix_ns::ambiguous_prefix_ns_func()"
"the_ambiguous_prefix_struct::ambiguous_prefix_method()"
}
check_setting_bp_fails "$cmd_prefix ambiguous_prefix_"
}
}
# Test completion of function labels.
proc_with_prefix function-labels {} {
# Test with and without a source file component.
foreach_location_functions \
{ "" "cpls.cc" } \
{ "function_with_labels(int)" } \
{
# Linespec version. Test various spacing around the label
# colon separator.
foreach label_sep {":" " :" ": " " : "} {
set linespec "${location}${label_sep}"
test_gdb_complete_multiple "b $linespec" "l" "abel" {
"label1"
"label2"
}
check_setting_bp_fails "b ${linespec}label"
set tsep [string trim ${source_sep}]
check_bp_locations_match_list \
"b ${linespec}label1" [list "${source}${tsep}${function}:label1"]
check_bp_locations_match_list \
"b ${linespec}label2" [list "${source}${tsep}${function}:label2"]
}
} \
{
# Explicit locations version.
append location " -label"
test_gdb_complete_multiple "b $location " "l" "abel" {
"label1"
"label2"
}
check_setting_bp_fails "b $location label"
if {$source != ""} {
set bp_loc_src "-source ${source} "
} else {
set bp_loc_src ""
}
check_bp_locations_match_list \
"b ${location} label1" [list "${bp_loc_src}-function $function -label label1"]
check_bp_locations_match_list \
"b ${location} label2" [list "${bp_loc_src}-function $function -label label2"]
}
}
# Test that completion after a function name offers keyword
# (if/inferior/task/thread/-force-condition) matches in linespec mode,
# and also the explicit location options in explicit locations mode.
proc_with_prefix keywords-after-function {} {
set explicit_list \
[lsort [concat \
$completion::explicit_opts_list \
$completion::keyword_list]]
# Test without a source file, with a known source file, and with
# and unknown source file.
# Test a known and an unknown function.
foreach_location_functions \
{ "" "cpls.cc" "unknown_file.cc" } \
{ "function_with_labels(int)" "unknown_function(int)" } \
{
# Linespec version.
test_gdb_complete_multiple "b ${location} " "" "" \
$completion::keyword_list
} \
{
# Explicit locations version.
test_gdb_complete_multiple "b ${location} " "" "" \
$explicit_list
}
}
# Same, but after a label.
proc_with_prefix keywords-after-label {} {
set explicit_list \
[lsort [concat \
$completion::explicit_opts_list \
$completion::keyword_list]]
foreach_location_labels \
{ "" "cpls.cc" } \
{ "function_with_labels(int)" "unknown_function(int)" } \
{ "label1" "non_existing_label" } \
{
# Linespec version.
test_gdb_complete_multiple "b ${location} " "" "" \
$completion::keyword_list
} \
{
# Explicit locations version.
test_gdb_complete_multiple "b ${location} " "" "" \
$explicit_list
}
}
# Similar, but after an unknown file, and in linespec mode only.
proc_with_prefix keywords-after-unknown-file {} {
# Test with and without quoting.
foreach qc $completion::maybe_quoted_list {
set line "b ${qc}unknown_file.cc${qc}: "
test_gdb_complete_multiple $line "" "" $completion::keyword_list
}
}
# Test that linespec / function completion does not match data
# symbols, only functions/methods.
proc_with_prefix no-data-symbols {} {
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_unique "$cmd_prefix code_" "$cmd_prefix code_function()"
}
}
# After "if", we expect an expression, which has a different completer
# that matches data symbols as well. Check that that works.
proc_with_prefix if-expression {} {
foreach cmd_prefix {"b" "b -function"} {
test_gdb_complete_multiple "$cmd_prefix function() if " "code_" "" {
"code_data"
"code_function()"
}
test_gdb_complete_unique \
"$cmd_prefix function() if code_data + another_da" \
"$cmd_prefix function() if code_data + another_data"
test_gdb_complete_unique \
"$cmd_prefix non_existing_function() if code_data + another_da" \
"$cmd_prefix non_existing_function() if code_data + another_data"
# FIXME: For now, thread and task also use the expression
# completer.
test_gdb_complete_unique \
"$cmd_prefix function() thread code_data + another_da" \
"$cmd_prefix function() thread code_data + another_data"
test_gdb_complete_unique \
"$cmd_prefix function() task code_data + another_da" \
"$cmd_prefix function() task code_data + another_data"
}
}
# The testcase driver. Calls all test procedures.
proc test_driver {} {
all-param-prefixes
overload
overload-2
fqn
fqn-2
overload-3
template-overload
template-ret-type
#test_makefoo
template-function-foo
#test_makem
template-class-with-method
const-overload
const-overload-quoted
append-end-quote-char-when-unambiguous
in-source-file-unconstrained
in-source-file-unambiguous
in-source-file-ambiguous
source-complete-appends-colon
incomplete-scope-colon
anon-ns
operator<
ambiguous-prefix
function-labels
keywords-after-function
keywords-after-label
keywords-after-unknown-file
no-data-symbols
if-expression
}
test_driver