c45ec17c07
Continuing the theme of the explicit locations patch, this patch gets rid of the need for quoting function names in linespec TAB completion. To recap, when you have overloads in your program, and you want to set a breakpoint in one of them: void function(int); // set breakpoint here. void function(long); (gdb) b function(i[TAB] <all the symbols in the program that start with "i" are uselessly shown...> This patch gets rid of the need for quoting by switching the linespec completer to use the custom completion word point mechanism added in the previous explicit location patch (extending it as needed), to correctly determine the right completion word point. In the case above, we want the completer to figure out that it's completing a function name that starts with "function(i", and it now does. We also want the completer to know when it's potentially completing a source file name, for: (gdb) break source.[TAB] -> source.c: (gdb) break source.c: # Type line number or function name now And we want it to know to complete label names, which it doesn't today: (gdb) break function:lab[TAB] etc., etc. So what we want is for completion to grok the input string as closely to how the linespec parser groks it. With that in mind, the solution suggests itself - make the linespec completer use the same parsing code as normal linespec parsing. That's what the patch does. The old completer is replaced by one that reuses the actual linespec parser as much as possible. This (ideally) eliminate differences between what completion understands and actually setting breakpoints understands by design. The completer now offers sensible completion candidates depending on which component of the linespec is being completed, source filename, function, line number, expression, and (a new addition), labels. For example, when completing the function part, we now show the full name of the method as completion candidates, instead of showing whatever comes after what readline considered the word break character: (gdb) break klass::method[TAB] klass:method1(int) klass:method2() If input is past the function, then we now offer keyword condidates: (gdb) b function(int) [TAB] if task thread If input is past a keyword, we offer expression completion, which is different from linespec completion: (gdb) b main if 1 + glo[TAB] global (e.g., completes on types, struct data fields, etc.) As mentioned, this teaches the linespec completer about completing label symbols too: (gdb) b source.c:function:lab[TAB] A nice convenience is that when completion uniquely matches a source name, gdb adds the ":" automatically for you: (gdb) b filenam[TAB] (gdb) b filename.c: # ':' auto-added, cursor right after it. It's the little details. :-) I worked on this patch in parallel with writing the (big) testcase added closer to the end of the series, which exercises many many tricky cases around quoting and whitespace insertion placement. In general, I think it now all Just Works. gdb/ChangeLog: 2017-07-17 Pedro Alves <palves@redhat.com> * completer.c (complete_source_filenames): New function. (complete_address_and_linespec_locations): New function. (location_completer): Use complete_address_and_linespec_locations. (completion_tracker::build_completion_result): Honor the tracker's request to suppress append. * completer.h (completion_tracker::suppress_append_ws) (completion_tracker::set_suppress_append_ws): New methods. (completion_tracker::m_suppress_append_ws): New field. (complete_source_filenames): New declaration. * linespec.c (linespec_complete_what): New. (struct ls_parser) <complete_what, completion_word, completion_quote_char, completion_quote_end, completion_tracker>: New fields. (string_find_incomplete_keyword_at_end): New. (linespec_lexer_lex_string): Record quote char. If in completion mode, don't throw. (linespec_lexer_consume_token): Advance the completion word point. (linespec_lexer_peek_token): Save/restore completion info. (save_stream_and_consume_token): New. (set_completion_after_number): New. (linespec_parse_basic): Set what to complete next depending on token. Handle function and label completions specially. (parse_linespec): Disable objc shortcut in completion mode. Set what to complete next depending on token type. Skip keyword if in completion mode. (complete_linespec_component, linespec_complete): New. * linespec.h (linespec_complete): Declare. gdb/testsuite/ChangeLog: 2017-07-17 Pedro Alves <palves@redhat.com> * gdb.base/completion.exp: Adjust expected output. * gdb.linespec/ls-errs.exp: Don't send tab characters, now that the completer works.
275 lines
9.1 KiB
Plaintext
275 lines
9.1 KiB
Plaintext
# Copyright 2012-2017 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/>.
|
|
|
|
# Tests for linespec errors with C and C++.
|
|
|
|
# The test proper. LANG is either C or C++.
|
|
|
|
proc do_test {lang} {
|
|
global testfile srcfile error_messages compiler_info
|
|
|
|
standard_testfile
|
|
set exefile $testfile
|
|
if [info exists compiler_info] {
|
|
# Unsetting compiler_info allows us to switch compilers
|
|
# used by prepare_for_testing.
|
|
unset compiler_info
|
|
}
|
|
set options {debug}
|
|
|
|
if {$lang == "C++"} {
|
|
if {[skip_cplus_tests]} {
|
|
return 0
|
|
}
|
|
# Build ".c" source file with g++.
|
|
lappend options "c++"
|
|
}
|
|
|
|
if {[prepare_for_testing "failed to prepare" $exefile $srcfile $options]} {
|
|
return -1
|
|
}
|
|
|
|
# Turn off the pending breakpoint queries.
|
|
gdb_test_no_output "set breakpoint pending off"
|
|
|
|
# Turn off completion limiting
|
|
gdb_test_no_output "set max-completions unlimited"
|
|
|
|
if {![runto_main]} {
|
|
fail "can't run to main"
|
|
return 0
|
|
}
|
|
|
|
# Run to a location in the file.
|
|
set bp_location [gdb_get_line_number "set breakpoint here"]
|
|
|
|
gdb_test "break $srcfile:$bp_location" \
|
|
"Breakpoint.*at.* file .*$srcfile, line $bp_location\\." \
|
|
"breakpoint line number in file"
|
|
|
|
gdb_continue_to_breakpoint "$bp_location"
|
|
|
|
# Common error message format strings.
|
|
array set error_messages {
|
|
invalid_file "No source file named %s."
|
|
invalid_function "Function \"%s\" not defined."
|
|
invalid_var_or_func
|
|
"Undefined convenience variable or function \"%s\" not defined."
|
|
invalid_function_f "Function \"%s\" not defined in \"%s\"."
|
|
invalid_var_or_func_f \
|
|
"Undefined convenience variable or function \"%s\" not defined in \"%s\"."
|
|
invalid_label "No label \"%s\" defined in function \"%s\"."
|
|
invalid_parm "invalid linespec argument, \"%s\""
|
|
invalid_offset "No line %d in the current file."
|
|
invalid_offset_f "No line %d in file \"%s\"."
|
|
malformed_line_offset "malformed line offset: \"%s\""
|
|
source_incomplete \
|
|
"Source filename requires function, label, or line offset."
|
|
unexpected "malformed linespec error: unexpected %s"
|
|
unexpected_opt "malformed linespec error: unexpected %s, \"%s\""
|
|
unmatched_quote "unmatched quote"
|
|
garbage "Garbage '%s' at end of command"
|
|
}
|
|
|
|
# We intentionally do not use gdb_breakpoint for these tests.
|
|
|
|
# Break at 'linespec' and expect the message in ::error_messages
|
|
# indexed by msg_id with the associated args.
|
|
proc test_break {linespec msg_id args} {
|
|
global error_messages
|
|
|
|
gdb_test "break $linespec" [string_to_regexp \
|
|
[eval format \$error_messages($msg_id) \
|
|
$args]]
|
|
}
|
|
|
|
# Some commonly used whitespace tests around ':'.
|
|
set spaces [list \
|
|
":" \
|
|
": " \
|
|
" :" \
|
|
" : " \
|
|
" : " \
|
|
]
|
|
|
|
# A list of invalid offsets.
|
|
set invalid_offsets [list -100 +500 1000]
|
|
|
|
# Try some simple, invalid linespecs involving spaces.
|
|
foreach x $spaces {
|
|
test_break $x unexpected "colon"
|
|
}
|
|
|
|
# Test invalid filespecs starting with offset. This is done
|
|
# first so that default offsets are tested.
|
|
foreach x $invalid_offsets {
|
|
set offset $x
|
|
|
|
# Relative offsets are relative to line 16. Adjust
|
|
# expected offset from error message accordingly.
|
|
if {[string index $x 0] == "+" || [string index $x 0] == "-"} {
|
|
incr offset 24
|
|
}
|
|
test_break $x invalid_offset $offset
|
|
test_break "-line $x" invalid_offset $offset
|
|
}
|
|
|
|
# Test offsets with trailing tokens w/ and w/o spaces.
|
|
foreach x $spaces {
|
|
test_break "3$x" unexpected "colon"
|
|
test_break "+10$x" unexpected "colon"
|
|
test_break "-10$x" unexpected "colon"
|
|
}
|
|
|
|
foreach x {1 +1 +100 -10} {
|
|
test_break "3 $x" unexpected_opt "number" $x
|
|
test_break "-line 3 $x" garbage $x
|
|
test_break "+10 $x" unexpected_opt "number" $x
|
|
test_break "-line +10 $x" garbage $x
|
|
test_break "-10 $x" unexpected_opt "number" $x
|
|
test_break "-line -10 $x" garbage $x
|
|
}
|
|
|
|
foreach x {3 +10 -10} {
|
|
test_break "$x foo" unexpected_opt "string" "foo"
|
|
test_break "-line $x foo" garbage "foo"
|
|
}
|
|
|
|
# Test invalid linespecs starting with filename.
|
|
# It's OK to use the ".c" extension for the C++ test
|
|
# since the extension doesn't affect GDB's lookup.
|
|
set invalid_files [list "this_file_doesn't_exist.c" \
|
|
"this file has spaces.c" \
|
|
"\"file::colons.c\"" \
|
|
"'file::colons.c'" \
|
|
"\"this \"file\" has quotes.c\"" \
|
|
"'this \"file\" has quotes.c'" \
|
|
"'this 'file' has quotes.c'" \
|
|
"\"this 'file' has quotes.c\"" \
|
|
"\"spaces: and :colons.c\"" \
|
|
"'more: :spaces: :and colons::.c'" \
|
|
"C:/nonexist-with-windrive.c"]
|
|
|
|
foreach x $invalid_files {
|
|
# Remove any quoting from FILENAME for the error message.
|
|
test_break "$x:3" invalid_file [string trim $x \"']
|
|
}
|
|
foreach x [list "this_file_doesn't_exist.c" \
|
|
"file::colons.c" \
|
|
"'file::colons.c'"] {
|
|
test_break "-source $x -line 3" invalid_file [string trim $x \"']
|
|
}
|
|
|
|
# Test that option lexing stops at whitespace boundaries, except
|
|
# when lexing function names, where we want to handle setting
|
|
# breakpoints on e.g., "int template_function<int>()".
|
|
test_break "-source this file has spaces.c -line 3" invalid_file "this"
|
|
test_break "-function ret_type tmpl_function" \
|
|
invalid_function "ret_type tmpl_function"
|
|
test_break "-source $srcfile -function ret_type tmpl_function" \
|
|
invalid_function_f "ret_type tmpl_function" $srcfile
|
|
|
|
test_break "-function main -label label whitespace" \
|
|
invalid_label "label" "main"
|
|
|
|
# Test unmatched quotes.
|
|
foreach x {"\"src-file.c'" "'src-file.c"} {
|
|
test_break "$x:3" unmatched_quote
|
|
}
|
|
|
|
test_break $srcfile invalid_function $srcfile
|
|
foreach x {"foo" " foo" " foo "} {
|
|
# Trim any leading/trailing whitespace for error messages.
|
|
test_break "$srcfile:$x" invalid_function_f [string trim $x] $srcfile
|
|
test_break "-source $srcfile -function $x" \
|
|
invalid_function_f [string trim $x] $srcfile
|
|
test_break "$srcfile:main:$x" invalid_label [string trim $x] "main"
|
|
test_break "-source $srcfile -function main -label $x" \
|
|
invalid_label [string trim $x] "main"
|
|
}
|
|
|
|
foreach x $spaces {
|
|
test_break "$srcfile$x" unexpected "end of input"
|
|
test_break "$srcfile:main$x" unexpected "end of input"
|
|
}
|
|
|
|
test_break "${srcfile}::" invalid_function "${srcfile}::"
|
|
test_break "$srcfile:3 1" unexpected_opt "number" "1"
|
|
test_break "-source $srcfile -line 3 1" garbage "1"
|
|
test_break "$srcfile:3 +100" unexpected_opt "number" "+100"
|
|
test_break "-source $srcfile -line 3 +100" garbage "+100"
|
|
test_break "$srcfile:3 -100" unexpected_opt "number" "-100"
|
|
test_break "$srcfile:3 foo" unexpected_opt "string" "foo"
|
|
test_break "-source $srcfile -line 3 foo" garbage "foo"
|
|
|
|
foreach x $invalid_offsets {
|
|
test_break "$srcfile:$x" invalid_offset_f $x $srcfile
|
|
test_break "\"$srcfile:$x\"" invalid_offset_f $x $srcfile
|
|
test_break "'$srcfile:$x'" invalid_offset_f $x $srcfile
|
|
test_break "-source $srcfile -line $x" invalid_offset_f $x $srcfile
|
|
}
|
|
test_break "-source $srcfile -line -x" malformed_line_offset "-x"
|
|
|
|
# Test invalid filespecs starting with function.
|
|
foreach x {"foobar" "foo::bar" "foo.bar" "foo ." "foo bar" "foo 1" \
|
|
"foo 0" "foo +10" "foo -10" "foo +100" "foo -100"} {
|
|
test_break $x invalid_function $x
|
|
test_break "-function \"$x\"" invalid_function $x
|
|
}
|
|
|
|
foreach x $spaces {
|
|
test_break "main${x}there" invalid_label "there" "main"
|
|
if {[test_compiler_info {clang-*-*}]} {
|
|
setup_xfail clang/14500 *-*-*
|
|
}
|
|
test_break "main:here${x}" unexpected "end of input"
|
|
}
|
|
|
|
foreach x {"3" "+100" "-100" "foo"} {
|
|
test_break "main 3" invalid_function "main 3"
|
|
test_break "-function \"main $x\"" invalid_function "main $x"
|
|
if {$x == "foo"} {
|
|
test_break "main:here $x" unexpected_opt "string" $x
|
|
} else {
|
|
test_break "main:here $x" unexpected_opt "number" $x
|
|
}
|
|
|
|
test_break "-function main -label \"here $x\"" \
|
|
invalid_label "here $x" "main"
|
|
}
|
|
|
|
foreach x {"if" "task" "thread"} {
|
|
test_break $x invalid_function $x
|
|
}
|
|
|
|
test_break "'main.c'flubber" unexpected_opt "string" "flubber"
|
|
test_break "'main.c',21" invalid_function "main.c"
|
|
test_break "'main.c' " invalid_function "main.c"
|
|
test_break "'main.c'3" unexpected_opt "number" "3"
|
|
test_break "'main.c'+3" unexpected_opt "number" "+3"
|
|
|
|
# Test undefined convenience variables.
|
|
set x {$zippo}
|
|
test_break $x invalid_var_or_func $x
|
|
test_break "$srcfile:$x" invalid_var_or_func_f $x $srcfile
|
|
|
|
# Explicit linespec-specific tests
|
|
test_break "-source $srcfile" source_incomplete
|
|
}
|
|
|
|
foreach_with_prefix lang {"C" "C++"} {
|
|
do_test ${lang}
|
|
}
|