mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-12 12:16:04 +08:00
a393b15517
This commit makes the gdb.Command.complete methods more verbose when it comes to error handling. Previous to this commit if any commands implemented in Python implemented the complete method, and if there were any errors encountered when calling that complete method, then GDB would silently hide the error and continue as if there were no completions. This makes is difficult to debug any errors encountered when writing completion methods, and encourages the idea that Python extensions can be broken, and GDB will just silently work around them. I don't think this is a good idea. GDB should encourage extensions to be written correctly, and robustly, and one way in which GDB can (I think) support this, is by pointing out when an extension goes wrong. In this commit I've gone through the Python command completion code, and added calls to gdbpy_print_stack() or gdbpy_print_stack_or_quit() in places where we were either clearing the Python error, or, in some cases, just not handling the error at all. One thing I have not changed is in cmdpy_completer (py-cmd.c) where we process the list of completions returned from the Command.complete method; this routine includes a call to gdbpy_is_string to check a possible completion is a string, if not the completion is ignored. I was tempted to remove this check, attempt to complete each result to a string, and display an error if the conversion fails. After all, returning anything but a string is surely a mistake by the extension author. However, the docs clearly say that only strings within the returned list will be considered as completions. Anything else is ignored. As such, and to avoid (what I think is pretty unlikely) breakage of existing code, I've retained the gdbpy_is_string check. After the gdbpy_is_string check we call python_string_to_host_string, if this call fails then I do now print the error, where before we ignored the error. I think this is OK; if GDB thinks something is a string, but still can't convert it to a string, then I think it's OK to display the error in that case. Another case which I was a little unsure about was in cmdpy_completer_helper, and the call to PyObject_CallMethodObjArgs, which is when we actually call Command.complete. Previously, if this call resulted in an exception then we would ignore this and just pretend there were no completions. Of all the changes, this is possibly the one with the biggest potential for breaking existing scripts, but also, is, I think, the most useful change. If the user code is wrong in some way, such that an exception is raised, then previously the user would have no obvious feedback about this breakage. Now GDB will print the exception for them, making it, I think, much easier to debug their extension. But, if there is user code in the wild that relies on raising an exception as a means to indicate there are no completions .... well, that code is going to break after this commit. I think we can live with this though, the exceptions means no completions thing was never documented behaviour. I also added a new error() call if the PyObject_CallMethodObjArgs call raises an exception. This causes the completion mechanism within GDB to stop. Within GDB the completion code is called twice, the first time to compute the work break characters, and then a second time to compute the actual completions. If PyObject_CallMethodObjArgs raises an exception when computing the word break character, and we print it by calling gdbpy_print_stack_or_quit(), but then carry on as if PyObject_CallMethodObjArgs had returns no completions, GDB will call the Python completion code again, which results in another call to PyObject_CallMethodObjArgs, which might raise the same exception again. This results in the Python exception being printed twice. By throwing a C++ exception after the failed PyObject_CallMethodObjArgs call, the completion mechanism is aborted, and no completions are offered. But importantly, the Python exception is only printed once. I think this gives a much better user experience. I've added some tests to cover this case, as I think this is the most likely case that a user will run into. Approved-By: Tom Tromey <tom@tromey.com>
154 lines
4.9 KiB
Plaintext
154 lines
4.9 KiB
Plaintext
# Copyright (C) 2014-2023 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/>.
|
|
|
|
set testfile "py-completion"
|
|
|
|
load_lib gdb-python.exp
|
|
|
|
require allow_python_tests
|
|
|
|
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
|
|
set discard 0
|
|
gdb_exit
|
|
gdb_start
|
|
|
|
# Tab completion tests require readline support, and this set of tests
|
|
# doesn't work on a remote host either.
|
|
if { [readline_is_used] && ![is_remote host] } {
|
|
gdb_test_no_output "source ${pyfile}" "load python file"
|
|
|
|
# Create a temporary directory
|
|
set testdir "[standard_output_file "py-completion-testdir"]/"
|
|
set testdir_regex [string_to_regexp $testdir]
|
|
set testdir_complete [standard_output_file "py-completion-test"]
|
|
file mkdir $testdir
|
|
|
|
# This one should always pass.
|
|
send_gdb "completefileinit ${testdir_complete}\t"
|
|
gdb_test_multiple "" "completefileinit completion" {
|
|
-re "^completefileinit ${testdir_regex}$" {
|
|
pass "completefileinit completion"
|
|
}
|
|
}
|
|
|
|
# Just discarding whatever we typed.
|
|
gdb_test " " ".*" "discard #[incr discard]"
|
|
|
|
# This should offer no suggestions - the complete() methods
|
|
# returns something that is neither an integer, or a sequence.
|
|
gdb_test_no_output "complete completefilenone ${testdir_complete}" \
|
|
"no suggestions given"
|
|
|
|
# This is the problematic one.
|
|
send_gdb "completefilemethod ${testdir_complete}\t"
|
|
gdb_test_multiple "" "completefilemethod completion" {
|
|
-re "^completefilemethod ${testdir_regex} $" {
|
|
fail "completefilemethod completion (completed filename as wrong command arg)"
|
|
}
|
|
-re "^completefilemethod ${testdir_regex}$" {
|
|
pass "completefilemethod completion"
|
|
}
|
|
}
|
|
|
|
# Discarding again
|
|
gdb_test " " ".*" "discard #[incr discard]"
|
|
|
|
# Another problematic
|
|
set completion_regex "[string_to_regexp [standard_output_file "py-completion-t"]]"
|
|
send_gdb "completefilecommandcond [standard_output_file "py-completion-t\t"]"
|
|
gdb_test_multiple "" "completefilecommandcond completion" {
|
|
-re "^completefilecommandcond ${testdir}$" {
|
|
fail "completefilecommandcond completion (completed filename instead of command)"
|
|
}
|
|
-re "^completefilecommandcond ${completion_regex}\007$" {
|
|
pass "completefilecommandcond completion"
|
|
}
|
|
}
|
|
|
|
# Discarding again
|
|
gdb_test " " ".*" "discard #[incr discard]"
|
|
}
|
|
|
|
# Start gdb over again to clear out current state. This can interfere
|
|
# with the expected output of the below tests in a buggy gdb.
|
|
gdb_exit
|
|
gdb_start
|
|
gdb_test_no_output "source ${pyfile}" "load python file again"
|
|
|
|
# Check that GDB prints exceptions raised by Command.complete calls.
|
|
# This first command raises an exception during the brkchars phase of
|
|
# completion.
|
|
gdb_test "complete complete_brkchar_exception " \
|
|
"Python Exception <class 'gdb\\.GdbError'>: brkchars exception"
|
|
|
|
# In this test the brkchars phase of completion is fine, but an
|
|
# exception is raised during the actual completion phase.
|
|
gdb_test "complete complete_raise_exception " \
|
|
"Python Exception <class 'gdb\\.GdbError'>: completion exception"
|
|
|
|
gdb_test_sequence "complete completel" \
|
|
"list all completions of 'complete completel'" {
|
|
"completelimit1"
|
|
"completelimit2"
|
|
"completelimit3"
|
|
"completelimit4"
|
|
"completelimit5"
|
|
"completelimit6"
|
|
"completelimit7"
|
|
}
|
|
|
|
# Discarding again
|
|
gdb_test " " ".*" "discard #[incr discard]"
|
|
|
|
gdb_test_sequence "complete completelimit1 c" \
|
|
"list all completions of 'complete completelimit1 c'" {
|
|
"completelimit1 cl11"
|
|
"completelimit1 cl12"
|
|
"completelimit1 cl13"
|
|
}
|
|
|
|
# Discarding again
|
|
gdb_test " " ".*" "discard #[incr discard]"
|
|
|
|
# If using readline, we can TAB-complete. This used to trigger a bug
|
|
# because the cached result from the completer was being reused for
|
|
# a different python command.
|
|
if {[readline_is_used]} {
|
|
set testname "tab-complete 'completelimit1 c'"
|
|
send_gdb "completelimit1 c\t"
|
|
gdb_test_multiple "" $testname {
|
|
-re "^completelimit1 c\\\x07l1$" {
|
|
pass $testname
|
|
|
|
# Discard the command line
|
|
gdb_test " " ".*" "discard #[incr discard]"
|
|
}
|
|
}
|
|
|
|
gdb_test_sequence "complete completelimit2 c" \
|
|
"list all completions of 'complete completelimit2 c'" {
|
|
"completelimit2 cl21"
|
|
"completelimit2 cl210"
|
|
"completelimit2 cl22"
|
|
"completelimit2 cl23"
|
|
"completelimit2 cl24"
|
|
"completelimit2 cl25"
|
|
"completelimit2 cl26"
|
|
"completelimit2 cl27"
|
|
"completelimit2 cl28"
|
|
"completelimit2 cl29"
|
|
}
|
|
}
|