| # 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 "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 "set max-completions $max_completions" "" | |
| } |