572 lines
17 KiB
Text
572 lines
17 KiB
Text
# Copyright 2017-2022 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 <http://www.gnu.org/licenses/>.
|
|
|
|
# This file is part of the gdb testsuite.
|
|
|
|
# Any variable or procedure in the namespace whose name starts with
|
|
# "_" is private to the module. Do not use these.
|
|
|
|
namespace eval completion {
|
|
variable bell_re "\\\x07"
|
|
|
|
# List of all quote chars.
|
|
variable all_quotes_list {"'" "\""}
|
|
|
|
# List of all quote chars, including no-quote at all.
|
|
variable maybe_quoted_list {"" "'" "\""}
|
|
|
|
variable keyword_list {"-force-condition" "if" "task" "thread"}
|
|
|
|
variable explicit_opts_list \
|
|
{"-function" "-label" "-line" "-qualified" "-source"}
|
|
}
|
|
|
|
# Make a regular expression that matches a TAB completion list.
|
|
|
|
proc make_tab_completion_list_re { completion_list } {
|
|
# readline separates the completion columns that fit on the same
|
|
# line with whitespace. Since we're testing under "set width
|
|
# unlimited", all completions will be printed on the same line.
|
|
# The amount of whitespace depends on the length of the widest
|
|
# completion. We could compute that here and expect the exact
|
|
# number of ws characters between each completion match, but to
|
|
# keep it simple, we accept any number of characters.
|
|
set ws " +"
|
|
|
|
set completion_list_re ""
|
|
foreach c $completion_list {
|
|
append completion_list_re [string_to_regexp $c]
|
|
append completion_list_re $ws
|
|
}
|
|
append completion_list_re $ws
|
|
|
|
return $completion_list_re
|
|
}
|
|
|
|
# Make a regular expression that matches a "complete" command
|
|
# completion list. CMD_PREFIX is the command prefix added to each
|
|
# completion match.
|
|
|
|
proc make_cmd_completion_list_re { cmd_prefix completion_list start_quote_char end_quote_char } {
|
|
|
|
set completion_list_re ""
|
|
foreach c $completion_list {
|
|
# The command prefix is included in all completion matches.
|
|
append completion_list_re [string_to_regexp $cmd_prefix$start_quote_char$c$end_quote_char]
|
|
append completion_list_re "\r\n"
|
|
}
|
|
|
|
return $completion_list_re
|
|
}
|
|
|
|
# Clear the input line.
|
|
|
|
proc clear_input_line { test } {
|
|
global gdb_prompt
|
|
|
|
send_gdb "\003"
|
|
gdb_test_multiple "" "$test (clearing input line)" {
|
|
-re "Quit\r\n$gdb_prompt $" {
|
|
}
|
|
}
|
|
}
|
|
|
|
# Test that completing LINE with TAB completes to nothing.
|
|
|
|
proc test_gdb_complete_tab_none { line } {
|
|
set line_re [string_to_regexp $line]
|
|
|
|
set test "tab complete \"$line\""
|
|
send_gdb "$line\t"
|
|
gdb_test_multiple "" "$test" {
|
|
-re "^$line_re$completion::bell_re$" {
|
|
pass "$test"
|
|
}
|
|
}
|
|
|
|
clear_input_line $test
|
|
}
|
|
|
|
# Test that completing INPUT_LINE with TAB completes to
|
|
# COMPLETE_LINE_RE. APPEND_CHAR_RE is the character expected to be
|
|
# appended after EXPECTED_OUTPUT. Normally that's a whitespace, but
|
|
# in some cases it's some other character, like a colon.
|
|
|
|
proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re } {
|
|
|
|
set test "tab complete \"$input_line\""
|
|
send_gdb "$input_line\t"
|
|
set res 1
|
|
gdb_test_multiple "" "$test" {
|
|
-re "^$complete_line_re$append_char_re$" {
|
|
pass "$test"
|
|
}
|
|
timeout {
|
|
fail "$test (timeout)"
|
|
set res -1
|
|
}
|
|
}
|
|
|
|
clear_input_line $test
|
|
return $res
|
|
}
|
|
|
|
# Test that completing INPUT_LINE with TAB completes to "INPUT_LINE +
|
|
# ADD_COMPLETED_LINE" and that it displays the completion matches in
|
|
# COMPLETION_LIST. If MAX_COMPLETIONS then we expect the completion
|
|
# to hit the max-completions limit.
|
|
|
|
proc test_gdb_complete_tab_multiple { input_line add_completed_line \
|
|
completion_list {max_completions 0}} {
|
|
global gdb_prompt
|
|
|
|
set input_line_re [string_to_regexp $input_line]
|
|
set add_completed_line_re [string_to_regexp $add_completed_line]
|
|
|
|
set expected_re [make_tab_completion_list_re $completion_list]
|
|
|
|
if {$max_completions} {
|
|
append expected_re "\r\n"
|
|
append expected_re \
|
|
"\\*\\*\\* List may be truncated, max-completions reached\\. \\*\\*\\*"
|
|
}
|
|
|
|
set test "tab complete \"$input_line\""
|
|
send_gdb "$input_line\t"
|
|
gdb_test_multiple "" "$test (first tab)" {
|
|
-re "^${input_line_re}${completion::bell_re}$add_completed_line_re$" {
|
|
send_gdb "\t"
|
|
# If we auto-completed to an ambiguous prefix, we need an
|
|
# extra tab to show the matches list.
|
|
if {$add_completed_line != ""} {
|
|
send_gdb "\t"
|
|
set maybe_bell ${completion::bell_re}
|
|
} else {
|
|
set maybe_bell ""
|
|
}
|
|
gdb_test_multiple "" "$test (second tab)" {
|
|
-re "^${maybe_bell}\r\n$expected_re\r\n$gdb_prompt " {
|
|
gdb_test_multiple "" "$test (second tab)" {
|
|
-re "^$input_line_re$add_completed_line_re$" {
|
|
pass "$test"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
clear_input_line $test
|
|
}
|
|
|
|
# Test that completing LINE with the complete command completes to
|
|
# nothing.
|
|
|
|
proc test_gdb_complete_cmd_none { line } {
|
|
gdb_test_no_output "complete $line" "cmd complete \"$line\""
|
|
}
|
|
|
|
# Test that completing LINE with the complete command completes to
|
|
# COMPLETE_LINE_RE.
|
|
|
|
proc test_gdb_complete_cmd_unique { input_line complete_line_re } {
|
|
global gdb_prompt
|
|
|
|
set cmd "complete $input_line"
|
|
set cmd_re [string_to_regexp $cmd]
|
|
set test "cmd complete \"$input_line\""
|
|
gdb_test_multiple $cmd $test {
|
|
-re "^$cmd_re\r\n$complete_line_re\r\n$gdb_prompt $" {
|
|
pass $test
|
|
}
|
|
}
|
|
}
|
|
|
|
# Test that completing "CMD_PREFIX + COMPLETION_WORD" with the
|
|
# complete command displays the COMPLETION_LIST completion list. Each
|
|
# entry in the list should be prefixed by CMD_PREFIX. If
|
|
# MAX_COMPLETIONS then we expect the completion to hit the
|
|
# max-completions limit.
|
|
|
|
proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list {start_quote_char ""} {end_quote_char ""} {max_completions 0}} {
|
|
global gdb_prompt
|
|
|
|
set expected_re [make_cmd_completion_list_re $cmd_prefix $completion_list $start_quote_char $end_quote_char]
|
|
|
|
if {$max_completions} {
|
|
set cmd_prefix_re [string_to_regexp $cmd_prefix]
|
|
append expected_re \
|
|
"$cmd_prefix_re \\*\\*\\* List may be truncated, max-completions reached\\. \\*\\*\\*.*\r\n"
|
|
}
|
|
|
|
set cmd_re [string_to_regexp "complete $cmd_prefix$completion_word"]
|
|
set test "cmd complete \"$cmd_prefix$completion_word\""
|
|
gdb_test_multiple "complete $cmd_prefix$completion_word" $test {
|
|
-re "^$cmd_re\r\n$expected_re$gdb_prompt $" {
|
|
pass $test
|
|
}
|
|
}
|
|
}
|
|
|
|
# Test that completing LINE completes to nothing.
|
|
|
|
proc test_gdb_complete_none { input_line } {
|
|
if { [readline_is_used] } {
|
|
test_gdb_complete_tab_none $input_line
|
|
}
|
|
test_gdb_complete_cmd_none $input_line
|
|
}
|
|
|
|
# Test that completing INPUT_LINE completes to COMPLETE_LINE_RE.
|
|
#
|
|
# APPEND_CHAR is the character expected to be appended after
|
|
# EXPECTED_OUTPUT when TAB completing. Normally that's a whitespace,
|
|
# but in some cases it's some other character, like a colon.
|
|
#
|
|
# If MAX_COMPLETIONS is true, then we expect the completion to hit the
|
|
# max-completions limit. Since we're expecting a unique completion
|
|
# match, this will only be visible in the "complete" command output.
|
|
# Tab completion will just auto-complete the only match and won't
|
|
# display a match list.
|
|
#
|
|
# Note: usually it's more convenient to pass a literal string instead
|
|
# of a regular expression (as COMPLETE_LINE_RE). See
|
|
# test_gdb_complete_unique below.
|
|
|
|
proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {
|
|
set append_char_re [string_to_regexp $append_char]
|
|
if { [readline_is_used] } {
|
|
if { [test_gdb_complete_tab_unique $input_line $complete_line_re \
|
|
$append_char_re] == -1 } {
|
|
return -1
|
|
}
|
|
}
|
|
|
|
# Trim COMPLETE LINE, for the case we're completing a command with leading
|
|
# whitespace. Leading command whitespace is discarded by GDB.
|
|
set expected_output_re [string trimleft $complete_line_re]
|
|
if {$append_char_re != " "} {
|
|
append expected_output_re $append_char_re
|
|
}
|
|
if {$max_completions} {
|
|
set max_completion_reached_msg \
|
|
"*** List may be truncated, max-completions reached. ***"
|
|
set input_line_re \
|
|
[string_to_regexp [string trimleft $input_line]]
|
|
set max_completion_reached_msg_re \
|
|
[string_to_regexp $max_completion_reached_msg]
|
|
|
|
append expected_output_re \
|
|
"\r\n$input_line_re $max_completion_reached_msg_re"
|
|
}
|
|
|
|
test_gdb_complete_cmd_unique $input_line $expected_output_re
|
|
return 1
|
|
}
|
|
|
|
# Like TEST_GDB_COMPLETE_UNIQUE_RE, but COMPLETE_LINE is a string, not
|
|
# a regular expression.
|
|
|
|
proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_completions 0}} {
|
|
set complete_line_re [string_to_regexp $complete_line]
|
|
test_gdb_complete_unique_re $input_line $complete_line_re $append_char $max_completions
|
|
}
|
|
|
|
# Test that completing "CMD_PREFIX + COMPLETION_WORD" adds
|
|
# ADD_COMPLETED_LINE to the input line, and that it displays
|
|
# COMPLETION_LIST as completion match list. COMPLETION_WORD is the
|
|
# completion word. If MAX_COMPLETIONS then we expect the completion
|
|
# to hit the max-completions limit.
|
|
|
|
proc test_gdb_complete_multiple {
|
|
cmd_prefix completion_word add_completed_line completion_list
|
|
{start_quote_char ""} {end_quote_char ""} {max_completions 0}
|
|
} {
|
|
if { [readline_is_used] } {
|
|
test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list $max_completions
|
|
}
|
|
test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char $max_completions
|
|
}
|
|
|
|
# Test that all the substring prefixes of INPUT from [0..START) to
|
|
# [0..END) complete to COMPLETION_RE (a regular expression). If END
|
|
# is ommitted, default to the length of INPUT.
|
|
|
|
proc test_complete_prefix_range_re {input completion_re start {end -1}} {
|
|
if {$end == -1} {
|
|
set end [string length $input]
|
|
}
|
|
|
|
set timeouts 0
|
|
set max_timeouts 3
|
|
for {set i $start} {$i < $end} {incr i} {
|
|
set line [string range $input 0 $i]
|
|
set res [test_gdb_complete_unique_re "$line" $completion_re]
|
|
if { $res == -1 } {
|
|
incr timeouts
|
|
} else {
|
|
if { $timeouts > 0 } {
|
|
set timeouts 0
|
|
}
|
|
}
|
|
if { $timeouts == $max_timeouts } {
|
|
verbose -log "Consecutive timeouts in test_complete_prefix_range_re, giving up"
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
# Test that all the substring prefixes of COMPLETION from [0..START)
|
|
# to [0..END) complete to COMPLETION. If END is ommitted, default to
|
|
# the length of COMPLETION.
|
|
|
|
proc test_complete_prefix_range {completion start {end -1}} {
|
|
set completion_re [string_to_regexp $completion]
|
|
test_complete_prefix_range_re $completion $completion_re $start $end
|
|
}
|
|
|
|
# Find NEEDLE in HAYSTACK and return the index _after_ NEEDLE. E.g.,
|
|
# searching for "(" in "foo(int)" returns 4, which would be useful if
|
|
# you want to find the "(" to try completing "foo(".
|
|
|
|
proc index_after {needle haystack} {
|
|
set start [string first $needle $haystack]
|
|
if {$start == -1} {
|
|
error "could not find \"$needle\" in \"$haystack\""
|
|
}
|
|
return [expr $start + [string length $needle]]
|
|
}
|
|
|
|
# Create a breakpoint using BREAK_COMMAND, and return the number
|
|
# of locations found.
|
|
|
|
proc completion::_create_bp {break_command} {
|
|
global gdb_prompt
|
|
global decimal hex
|
|
|
|
set found_locations -1
|
|
|
|
set test "set breakpoint"
|
|
gdb_test_multiple "$break_command" $test {
|
|
-re "\\\(\($decimal\) locations\\\)\r\n$gdb_prompt $" {
|
|
set found_locations "$expect_out(1,string)"
|
|
}
|
|
-re "Breakpoint $decimal at $hex: file .*, line .*$gdb_prompt $" {
|
|
set found_locations 1
|
|
}
|
|
-re "Make breakpoint pending on future shared library load.*y or .n.. $" {
|
|
send_gdb "n\n"
|
|
gdb_test_multiple "" "$test (prompt)" {
|
|
-re "$gdb_prompt $" {
|
|
}
|
|
}
|
|
set found_locations 0
|
|
}
|
|
-re "invalid explicit location argument, \[^\r\n\]*\r\n$gdb_prompt $" {
|
|
set found_locations 0
|
|
}
|
|
-re "Function \[^\r\n\]* not defined in \[^\r\n\]*\r\n$gdb_prompt $" {
|
|
set found_locations 0
|
|
}
|
|
}
|
|
return $found_locations
|
|
}
|
|
|
|
# Return true if lists A and B have the same elements. Order of
|
|
# elements does not matter.
|
|
|
|
proc completion::_leq {a b} {
|
|
return [expr {[lsort $a] eq [lsort $b]}]
|
|
}
|
|
|
|
# Check that trying to create a breakpoint using BREAK_COMMAND fails.
|
|
|
|
proc check_setting_bp_fails {break_command} {
|
|
with_test_prefix "\"$break_command\" creates no bp locations" {
|
|
set found_locations [completion::_create_bp $break_command]
|
|
gdb_assert {$found_locations == 0} "matches"
|
|
if {$found_locations != 0} {
|
|
delete_breakpoints
|
|
}
|
|
}
|
|
}
|
|
|
|
# Check that creating the breakpoint using BREAK_COMMAND finds the
|
|
# same breakpoint locations as completing BREAK_COMMAND.
|
|
# COMPLETION_LIST is the expected completion match list.
|
|
|
|
proc check_bp_locations_match_list {break_command completion_list} {
|
|
global gdb_prompt
|
|
global hex
|
|
|
|
with_test_prefix "compare \"$break_command\" completion list with bp location list" {
|
|
set num_locations [completion::_create_bp $break_command]
|
|
|
|
set found_list ""
|
|
|
|
set any "\[^\r\n\]*"
|
|
|
|
gdb_test_multiple "info breakpoint \$bpnum" "info breakpoint" {
|
|
-re "in \(\[^\r\n\]*\) at " {
|
|
# A function location.
|
|
set found_location "$expect_out(1,string)"
|
|
lappend found_list $found_location
|
|
exp_continue
|
|
}
|
|
-re "breakpoint${any}keep${any}y${any}$hex\[ \t]*\(${any}\)\r\n" {
|
|
# A label location.
|
|
set found_location "$expect_out(1,string)"
|
|
lappend found_list $found_location
|
|
exp_continue
|
|
}
|
|
-re "$gdb_prompt $" {
|
|
}
|
|
}
|
|
|
|
gdb_assert {[completion::_leq $found_list $completion_list]} "matches"
|
|
|
|
delete_breakpoints
|
|
}
|
|
}
|
|
|
|
# Build linespec and explicit locations out of all the combinations of
|
|
# SOURCES, FUNCTIONS and LABELS, with all combinations of possible
|
|
# quoting and whitespace around separators, and run BODY_LINESPEC and
|
|
# BODY_EXPLICIT in the context of the caller for each combination. A
|
|
# variable named "location" is set in the callers context with the
|
|
# currently iterated location.
|
|
|
|
proc foreach_location_functions { sources functions body_linespec body_explicit } {
|
|
upvar source source
|
|
upvar function function
|
|
upvar source_sep source_sep
|
|
upvar location location
|
|
|
|
foreach source $sources {
|
|
# 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 {
|
|
# Test known and unknown functions.
|
|
foreach function $functions {
|
|
# Linespec version. Test with and without spacing
|
|
# after the source/colon colon separator.
|
|
foreach source_sep {"" ":" ": "} {
|
|
# Skip invalid combinations.
|
|
if {$source == "" && $source_sep != ""} {
|
|
continue
|
|
}
|
|
if {$source != "" && $source_sep == ""} {
|
|
continue
|
|
}
|
|
|
|
set location "${sqc}${source}${sqc}${source_sep}${fqc}$function${fqc}"
|
|
uplevel 1 $body_linespec
|
|
}
|
|
|
|
# Explicit locations version.
|
|
if {$source != ""} {
|
|
set loc_src "-source ${sqc}${source}${sqc} "
|
|
} else {
|
|
set loc_src ""
|
|
}
|
|
|
|
set location "${loc_src}-function ${fqc}$function${fqc}"
|
|
uplevel 1 $body_explicit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Same as foreach_locations_functions, but also iterate over
|
|
# combinations of labels.
|
|
proc foreach_location_labels { sources functions labels body_linespec body_explicit } {
|
|
upvar source source
|
|
upvar function function
|
|
upvar label label
|
|
upvar source_sep source_sep
|
|
upvar label_sep label_sep
|
|
upvar location location
|
|
|
|
# Test both with a known source file and without a source file
|
|
# component.
|
|
foreach_location_functions \
|
|
$sources \
|
|
$functions \
|
|
{
|
|
# Linespec version. Test various spacing around the label
|
|
# colon separator.
|
|
set saved_location ${location}
|
|
foreach label_sep {":" " :" ": " " : "} {
|
|
# Test both known and unknown label.
|
|
foreach label $labels {
|
|
set location "${saved_location}${label_sep}$label"
|
|
uplevel 1 $body_linespec
|
|
}
|
|
}
|
|
} \
|
|
{
|
|
# Explicit locations version.
|
|
set saved_location ${location}
|
|
foreach label $labels {
|
|
set location "${saved_location} -label $label"
|
|
uplevel 1 $body_explicit
|
|
}
|
|
}
|
|
}
|
|
|
|
# Check that completion of INPUT_LINE results in GDB completing on all
|
|
# command names.
|
|
proc test_gdb_completion_offers_commands {input_line} {
|
|
global gdb_prompt
|
|
|
|
# There are two many commands to usefully check here. So we force
|
|
# max-completions to 2, and check if those 2 come out.
|
|
|
|
# Save current max-completions.
|
|
set max_completions 0
|
|
set test "show max-completions"
|
|
gdb_test_multiple $test $test {
|
|
-re "Maximum number of completion candidates is (.*)\\.\r\n$gdb_prompt $" {
|
|
set max_completions $expect_out(1,string)
|
|
}
|
|
}
|
|
|
|
# Force showing two commands.
|
|
gdb_test_no_output -nopass "set max-completions 2"
|
|
|
|
# TUI adds additional commands to the possible completions, so we
|
|
# need different patterns depending on whether or not it is enabled.
|
|
if { [skip_tui_tests] } {
|
|
test_gdb_complete_multiple $input_line "" "" {
|
|
"!"
|
|
"actions"
|
|
} "" "" 1
|
|
} else {
|
|
test_gdb_complete_multiple $input_line "" "" {
|
|
"!"
|
|
"+"
|
|
} "" "" 1
|
|
}
|
|
|
|
# Restore.
|
|
gdb_test_no_output -nopass "set max-completions $max_completions"
|
|
}
|